在我的项目中,我试图实现两个实体对象之间的离散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{#区域数据框边界框{get;}bool SolidCollider{get;set;}bool静态编译器{get;set;}float DistanceTraveledThisFrame{get;}#端区#区域方法void CheckCollision(IBoundingBoxCollider其他);#端区}
字符由character类表示。此类具有CheckCollisions和UpdateCollider方法。UpdateCollider也在类构造函数中调用。Character类继承自处理动画的类AnimatedObject,后者继承自处理在屏幕上绘制精灵的DrawableObject。DrawableObject具有"位置"、"旋转"one_answers"比例"属性。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类中有一个列表。列表的每次更新都会被迭代,并且每两个碰撞器都会调用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结构,并进行了一些命名更改:
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
}
它让我无法从密封类型"Microsoft"派生。Xna。框架矩形
使用Rectangle
类而不是AABB。这个类有预定义的函数,如Intersects
和Contains
,它仍然是一个矩形,您仍然可以更改它的位置、宽度和高度。如果您想通过两点定义Rectangle
,请创建一个新类(这是一个c#/pudo-code):
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的人想要的那样用简单的方法来做,可以尝试在谷歌上搜索"如何检查两个矩形之间的碰撞",并在Intersects
函数中实现这些公式。我不建议这样做,因为你会把时间浪费在你已经拥有的东西上(Rectangle
)。以最后一段作为我的经验,因为我尝试了你现在正在尝试的,并且到了我看到我的自定义BoundingBox
与XNA中包含的Rectangle
类几乎相同的地步。
编辑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时间,但当你的游戏开始滞后时,你会担心这一点。现在,练习练习。如果你不是初学者,你就知道自己在做什么。