在我的项目中,我试图在两个实体对象之间实现离散的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;
答案 0 :(得分:0)
使用Rectangle
类而不是AABB。此类具有预定义的函数,如Intersects
和Contains
,它仍然是一个矩形,您仍然可以更改其位置,宽度和高度。如果你想用两点定义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
)。把这最后一段作为我的经验,因为我尝试了你现在正在尝试的东西,并且直到我看到我的自定义BoundingBox
与Rectangle
的工作方式非常相似包含在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时间,但是当你的游戏开始滞后时,你会担心这一点。目前,练习 - 练习 - 练习。如果您不是初学者,那么您就知道自己在做什么。