当两个对象具有不同的大小时,AABB对撞机不与精灵重叠

时间:2016-05-27 13:37:35

标签: c# xna aabb

在我的项目中,我试图在两个实体对象之间实现离散的AABB碰撞。我对两个相同大小的对象的代码工作正常,但是对于两个不同大小的对象,当两个精灵明显没有碰到时会发生碰撞。两个精灵碰撞时的距离越大,物体尺寸之间的差异越大。

为什么会发生这种情况?如何更改代码,以便碰撞按预期用于两个不同大小的对象?

目前的相关代码:

Box结构代表一个边界框。它还有检查交叉点的方法。通过否定方式检查交叉点

public struct Box
{
    #region data
    public Vector2 TopLeft { get; set; }
    public Vector2 BottomRight{ get; set; }
    public float Width
    {
        get
        {
            return Math.Abs(BottomRight.X - TopLeft.X);
        }
    }
    public float Height
    {
        get
        {
            return Math.Abs(BottomRight.Y - TopLeft.Y);
        }
    }
    #endregion
    #region c'tor
    public Box(Vector2 tl, Vector2 br) : 
        this()
    {
        this.TopLeft = tl;
        this.BottomRight = br;
    }
    public Box(float top, float bottom, float left, float right) :
        this(new Vector2(left, top), new Vector2(right, bottom))
    {

    }
    public Box(Vector2 tl, float width, float height) :
        this(tl, new Vector2(tl.X + width, tl.Y + height))
    {

    }
    #endregion
    #region methods
    public bool Intersects(Box other)
    {
        return (IntersectsX(other) && IntersectsY(other)) || IsContained(other);
    }
    public bool IntersectsY(Box other)
    {
        return !((TopLeft.Y <= other.TopLeft.Y && BottomRight.Y <= other.TopLeft.Y) || (TopLeft.Y >= other.BottomRight.Y && BottomRight.Y >= other.BottomRight.Y));
    }
    public bool IntersectsX(Box other)
    {
        return !((TopLeft.X <= other.TopLeft.X && BottomRight.X <= other.TopLeft.X) || (TopLeft.X >= other.BottomRight.X && BottomRight.X >= other.BottomRight.X));
    }
    public bool IsContained(Box other)//checks if other is contained in this Box
    {
        return (TopLeft.X > other.TopLeft.X) && (TopLeft.X < other.BottomRight.X) && (TopLeft.Y > other.TopLeft.Y) && (TopLeft.Y < other.BottomRight.Y) &&
               (BottomRight.X > other.TopLeft.X) && (BottomRight.X < other.BottomRight.X) && (BottomRight.Y > other.TopLeft.Y) && (BottomRight.Y < other.BottomRight.Y);
    }
    #endregion
}

具有对撞机的对象必须实现IBoundingBoxCollider接口: 公共接口IBoundingBoxCollider     {         #region数据         Box BoundingBox {get; }         bool SolidCollider {get;组; }         bool StaticCollider {get;组; }         float DistanceTraveledThisFrame {get; }         #endregion         #region方法         void CheckCollision(IBoundingBoxCollider其他);         #endregion     }

字符由Character类表示。该类具有CheckCollisions和UpdateCollider方法。 UpdateCollider也在类构造函数中调用。 Character类继承自AnimatedObject,AnimatedObject是处理动画的类,它继承自DrawableObject,后者处理绘制精灵到屏幕。 DrawableObject具有“位置”,“旋转”和“缩放”属性。 Draw和Update方法处理Game1中的静态事件,这些事件在Draw和Update上相应调用。

public virtual void CheckCollision(IBoundingBoxCollider other)
    {
        if (BoundingBox.Intersects(other.BoundingBox))
        {
            //on collision
            float newX = Position.X, newY = Position.Y;
            if (directionLastMoved == Directions.Up || directionLastMoved == Directions.Up_Left || directionLastMoved == Directions.Up_Right)
                newY = other.BoundingBox.BottomRight.Y;
            if (directionLastMoved == Directions.Down || directionLastMoved == Directions.Down_Left || directionLastMoved == Directions.Down_Right)
                newY = other.BoundingBox.TopLeft.Y - BoundingBox.Height;
            if (directionLastMoved == Directions.Left || directionLastMoved == Directions.Up_Left || directionLastMoved == Directions.Down_Left)
                newX = other.BoundingBox.BottomRight.X;
            if (directionLastMoved == Directions.Right || directionLastMoved == Directions.Up_Right || directionLastMoved == Directions.Down_Right)
                newX = other.BoundingBox.TopLeft.X - BoundingBox.Width;
            Vector2 newPos = new Vector2(newX, newY);
            float ratio = DistanceTraveledThisFrame / (DistanceTraveledThisFrame + other.DistanceTraveledThisFrame);
            if(other.StaticCollider || ratio.Equals(float.NaN))
                Position = newPos;
            else
            {
                Vector2 delta = (newPos - Position) * ratio;
                Position += delta;
            }
            UpdateCollider();
        }
    }
    protected override void Draw(GameTime gameTime)
    {
        base.Draw(gameTime);
        UpdateCollider();
    }
    protected virtual void Update(GameTime gameTime)
    {
        lastPosition = Position;
    }
    protected void UpdateCollider()
    {
        Rectangle currentFrameBox = this.animator.CurrentAnimation.CurrentFrame(0).Frame;
        BoundingBox = new Box(Position, currentFrameBox.Width * Scale, currentFrameBox.Height * Scale);
    }

在Game1类中有一个List。每次更新列表都会被迭代,并且每两个碰撞器都会调用CheckCollision:

protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        // TODO: Add your update logic here
        foreach (IBoundingBoxCollider collider in colliderList)
        {
            if (collider.StaticCollider)
                continue;
            foreach (IBoundingBoxCollider other in colliderList)
            {
                if (collider != other)
                    collider.CheckCollision(other);
            }
        }
        if (InputEvent != null)
            InputEvent(gameTime, Keyboard.GetState(), Mouse.GetState());
        if (UpdateEvent != null)
            UpdateEvent(gameTime);
        base.Update(gameTime);
    }

EDIT1

尝试使用Monset的解决方案,用Monset制作的Box替换Box结构,并进行一些命名更改:

public class Box : Rectangle
{
    #region data
    public Vector2 Position;
    public float Width, Height;
    public Rectangle GetRectangle
    { get { return new Rectangle((int)Position.X, (int)Position.Y, (int)Width, (int)Height); } }
    #endregion
    #region c'tor
    public Box(Vector2 position, float width, float height)
    {
        this.Position = position;
        this.Width = width;
        this.Height = height;
    }
    #endregion
}

它让我无法从密封类型中获得&#39; Microsoft.Xna.Framework.Rectangle&#39;

1 个答案:

答案 0 :(得分:0)

使用Rectangle类而不是AABB。此类具有预定义的函数,如IntersectsContains,它仍然是一个矩形,您仍然可以更改其位置,宽度和高度。如果你想用两点定义Rectangle,创建一个新类(这是一个c#/伪代码):

public class BoundingBox: Rectangle
{
    ...
    public Point TopLeft
    { get{ this.X = value.X; this.Y = value.Y; } }
    public Point BottomRight
    { get{ this.Width = value.X - this.X; this.Height = value.Y - this.Y; } }
    ...
}

如果您仍然不想这么做,那就是构建XNA的人的方式,尝试谷歌搜索&#34;如何检查两个矩形之间的碰撞&#34;,并在您的实施中实现这些公式Intersects功能。我不建议这样做,因为你会浪费时间与你已经拥有的东西(Rectangle)。把这最后一段作为我的经验,因为我尝试了你现在正在尝试的东西,并且直到我看到我的自定义BoundingBoxRectangle的工作方式非常相似包含在XNA中的课程。

编辑1:
正如评论中所述,对于使用十进制数字(float)持有位置,您可以在float中存储3个变量(位置,宽度和高度)并创建一个函数或一个代表这些变量的变量在Rectangle。执行此操作将保留更多信息(因为所有内容都以十进制数存储)并为您提供Rectangle类提供的功能。这看起来像这样(未经测试):

public class BoundingBox: Rectangle
{
    // PUBLIC
    public Vector2 Position;
    public float Width, Height,
    public Rectangle Box
    { get { return new Rectangle((int)Position.X, (int)Position.Y, (int)Width, (int)Height); } }

    // or, if you don't understand get/set, create function
    public Rectange Box()
    { return new Rectangle((int)Position.X, (int)Position.Y, (int)Width, (int)Height); } 
}

如果您是初学者,请不要担心此处的CPU或RAM。这将占用更多的RAM空间和CPU时间,但是当你的游戏开始滞后时,你会担心这一点。目前,练习 - 练习 - 练习。如果您不是初学者,那么您就知道自己在做什么。