私のプロジェクトでは、2 つの固体オブジェクト間に個別の AABB 衝突を実装しようとしています。私が持っているコードは、同じサイズの 2 つのオブジェクトに対しては正常に動作しますが、サイズが異なる 2 つのオブジェクトでは、2 つのスプライトが明らかに接触していないときに衝突が発生します。衝突時の 2 つのスプライト間の距離は、オブジェクトのサイズの差が大きいほど大きくなります。
なぜこのようなことが起こるのでしょうか? また、衝突が異なるサイズの 2 つのオブジェクトに対して意図したとおりに機能するようにコードを変更するにはどうすればよいでしょうか?
現在の関連コード:
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 インターフェイスを実装する必要があります。 public interface IBoundingBoxCollider { #region data Box BoundingBox { get; } ブール SolidCollider { get; 設定; } bool StaticCollider { get; } 設定; } フロート DistanceTraveledThisFrame { get; } #endregion #region メソッド void CheckCollision(IBoundingBoxCollider other); #endregion }
文字は Character クラスによって表されます。このクラスには、CheckCollisions および UpdateCollider メソッドがあります。UpdateCollider は、クラス コンストラクターでも呼び出されます。Character クラスは、アニメーションを処理するクラスである AnimatedObject を継承します。このクラスは、画面へのスプライトの描画を処理する DrawableObject を継承します。DrawableObject には、Position、Rotation、および Scale プロパティがあります。Draw メソッドと Update メソッドは、それに応じて Draw と Update で呼び出される Game1 の静的イベントを処理します。
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 があります。リストが更新されるたびに繰り返され、2 つのコライダーごとに 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);
}
編集1
Box構造体をMonsetが作成したクラスに置き換え、いくつかの名前を変更して、Monsetによる解決策を試しました。
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.Framework.Rectangle'から派生できないことを示しています