碰撞检测的边界框重叠并引发问题

本文关键字:问题 重叠 边界 碰撞检测 | 更新日期: 2023-09-27 18:22:33

下面的代码是我试图阻止PC(玩家角色)穿过NPC。

  • "if"语句检查两个矩形(BoundingBox)是否相交
  • collisionBox定义重叠边界框的区域
  • moveDir定义了在if语句之后PC将进行的Vector更改(例如:如果moveDir=(2,0),则PC将向右移动两个像素)
  • currentSpeed根据键盘输入(上、下、左、右)定义分配给moveDir.X或moveDir.Y的值

代码:

if (PC.charSprite.BoundingBox.Intersects(npc.charSprite.BoundingBox))       
{        
    Rectangle collisionBox = Rectangle.Intersect(PC.charSprite.BoundingBox, npc.charSprite.BoundingBox);
    if (PC.moveDir.X > 0) //Moving Right
    {
        //Unknown Code Goes Here
    }
    else if (PC.moveDir.X < 0) //Moving Left
    {
    }
    else if (PC.moveDir.Y > 0) //Moving Down
    {
    }
    else if (PC.moveDir.Y < 0) //Moving Up
    {
    }
}

我如何做到当电脑触摸NPC时,电脑停止向该方向移动,但可以自由移动其他三个方向中的任何一个?

我尝试过的代码:

if (PC.moveDir.X > 0) //Moving Right
{
    PC.moveDir = Vector2.Zero;
}
else if (PC.moveDir.X < 0) //Moving Left
{
    PC.moveDir = Vector2.Zero;
}
else if (PC.moveDir.Y > 0) //Moving Down
{
    PC.moveDir = Vector2.Zero;
}
else if (PC.moveDir.Y < 0) //Moving Up
{
    PC.moveDir = Vector2.Zero;
}

^这样可以将电脑锁定到位,防止任何移动。

if (PC.moveDir.X > 0) //Moving Right
{
    PC.moveDir.X = 0;
}
else if (PC.moveDir.X < 0) //Moving Left
{
    PC.moveDir.X = 0;
}
else if (PC.moveDir.Y > 0) //Moving Down
{
    PC.moveDir.Y = 0;
}
else if (PC.moveDir.Y < 0) //Moving Up
{
    PC.moveDir.Y = 0;
}

^这也会将电脑锁定到位。

if (PC.moveDir.X > 0) //Moving Right
{
    PC.moveDir.X = -currentspeed;
}
else if (PC.moveDir.X < 0) //Moving Left
{
    PC.moveDir.X = currentspeed;
}
else if (PC.moveDir.Y > 0) //Moving Down
{
    PC.moveDir.Y = -currentspeed;
}
else if (PC.moveDir.Y < 0) //Moving Up
{
    PC.moveDir.Y = currentspeed;
}

^这是在非常断断续续的基础上进行的。我曾希望,通过将moveDir改为与它重叠的程度(或更大)相反的方向,可以使PC保持在NPC的边缘,但防止重叠。不幸的是,有一半的时间电脑卡在原地。

合并两个

if (PC.moveDir.X > 0) //Moving Right
{
    PC.moveDir.X = -currentspeed;
    PC.moveDir.X = 0;
}
else if (PC.moveDir.X < 0) //Moving Left
{
    PC.moveDir.X = currentspeed;
    PC.moveDir.X = 0;
}
else if (PC.moveDir.Y > 0) //Moving Down
{
    PC.moveDir.Y = -currentspeed;
    PC.moveDir.Y = 0;
}
else if (PC.moveDir.Y < 0) //Moving Up
{
    PC.moveDir.Y = currentspeed;
    PC.moveDir.Y = 0;
}

只会导致整体锁定。

if (PC.moveDir.X > 0) //Moving Right
{
    PC.moveDir.X = -collisionBox.Width;
}
else if (PC.moveDir.X < 0) //Moving Left
{
    PC.moveDir.X = collisionBox.Width;
}
else if (PC.moveDir.Y > 0) //Moving Down
{
    PC.moveDir.Y = -collisionBox.Height;
}
else if (PC.moveDir.Y < 0) //Moving Up
{
    PC.moveDir.Y = collisionBox.Height;
}

^这个几乎工作得很好,但当电脑靠在NPC上并垂直于它所接触的NPC转动时,电脑会跳到一边。同样,大约有一半的时间。

受CSJ评论启发的尝试:

if (PC.charSprite.BoundingBox.Intersects(npc.charSprite.BoundingBox))
{
    Rectangle collisionBox = Rectangle.Intersect(PC.charSprite.BoundingBox, npc.charSprite.BoundingBox);
    if (PC.moveDir.X > 0) //Moving Right
    {
        PC.charSprite.Position = new Vector2(npc.charSprite.BoundingBox.Left - 34, PC.charSprite.Position.Y);
    }
    else if (PC.moveDir.X < 0) //Moving Left
    {
        PC.charSprite.Position = new Vector2(npc.charSprite.BoundingBox.Right + 2, PC.charSprite.Position.Y);
    }
    else if (PC.moveDir.Y > 0) //Moving Down
    {
        PC.charSprite.Position = new Vector2(PC.charSprite.Position.X, npc.charSprite.BoundingBox.Top - 34);
    }
    else if (PC.moveDir.Y < 0) //Moving Up
    {
        PC.charSprite.Position = new Vector2(PC.charSprite.Position.X, npc.charSprite.BoundingBox.Bottom + 2)
    }
}

我再问一次:我该如何做到当电脑触摸NPC时,电脑停止向该方向移动,但可以自由移动其他三个方向中的任何一个?

或者,用更通用的术语来说,如何使一个矩形与另一个矩形相交,在不阻碍其向任何其他方向移动的情况下,失去向相交矩形移动的能力?

碰撞检测的边界框重叠并引发问题

在这种情况下我所做的是:

  • 正常执行位移(即将角色的运动矢量添加到其位置)
  • 检查所有障碍物是否发生碰撞。如果发生碰撞,将角色"传送"到远离障碍物的地方,比如一个像素。继续执行此操作,直到不再发生碰撞

这可能不是运行最快的解决方案,但它可以完成任务。当有许多动态的东西也可能发生碰撞时,效果很好。

经过多次戴头巾和酗酒,我终于想出了一个解决方案。我甚至把它放在一个班级里,这样其他人就可以用它来帮助他们解决类似的问题。

class Collision
{
    #region Declarations
    private Rectangle rectangle1;
    private Rectangle rectangle2;
    private Rectangle collisionZone;
    #endregion
    #region Constructors
    public Collision(Rectangle R1, Rectangle R2)
    {
        rectangle1 = R1;
        rectangle2 = R2;
        if(AreColliding())
        {
            collisionZone = Rectangle.Intersect(rectangle1, rectangle2);
        }
        else
        {
            collisionZone = Rectangle.Empty;
        }
    }
    #endregion
    #region Properties
    /// <summary>
    /// Returns the x-axis value of the top-left corner of R1
    /// </summary>
    public int TopLeftR1X
    {
        get { return rectangle1.X; }
    }
    /// <summary>
    /// Returns the y-axis value of the top-left corner of R1
    /// </summary>
    public int TopLeftR1Y
    {
        get { return rectangle1.Y; }
    }
    /// <summary>
    /// Returns the x-axis value of the top-right corner of R1
    /// </summary>
    public int TopRightR1X
    {
        get { return rectangle1.X + rectangle1.Width; }
    }
    /// <summary>
    /// Returns the y-axis value of the top-right corner of R1
    /// </summary>
    public int TopRightR1Y
    {
        get { return rectangle1.Y; }
    }
    /// <summary>
    /// Returns the x-axis value of the bottom-left corner of R1
    /// </summary>
    public int BottomLeftR1X
    {
        get { return rectangle1.X; }
    }
    /// <summary>
    /// Returns the y-axis value of the bottom-left corner of R1
    /// </summary>
    public int BottomLeftR1Y
    {
        get { return rectangle1.Y + rectangle1.Height; }
    }
    /// <summary>
    /// Returns the x-axis value of the bottom-right corner of R1
    /// </summary>
    public int BottomRightR1X
    {
        get { return rectangle1.X + rectangle1.Width; }
    }
    /// <summary>
    /// Returns the y-axis value of the bottom-right corner of R1
    /// </summary>
    public int BottomRightR1Y
    {
        get { return rectangle1.Y + rectangle1.Height; }
    }
    /// <summary>
    /// Returns the x-axis value of the top-left corner of R2
    /// </summary>
    public int TopLeftR2X
    {
        get { return rectangle2.X; }
    }
    /// <summary>
    /// Returns the y-axis value of the top-left corner of R2
    /// </summary>
    public int TopLeftR2Y
    {
        get { return rectangle2.Y; }
    }
    /// <summary>
    /// Returns the x-axis value of the top-right corner of R2
    /// </summary>
    public int TopRightR2X
    {
        get { return rectangle2.X + rectangle2.Width; }
    }
    /// <summary>
    /// Returns the y-axis value of the top-right corner of R2
    /// </summary>
    public int TopRightR2Y
    {
        get { return rectangle2.Y; }
    }
    /// <summary>
    /// Returns the x-axis value of the bottom-left corner of R2
    /// </summary>
    public int BottomLeftR2X
    {
        get { return rectangle2.X; }
    }
    /// <summary>
    /// Returns the y-axis value of the bottom-left corner of R2
    /// </summary>
    public int BottomLeftR2Y
    {
        get { return rectangle2.Y + rectangle2.Height; }
    }
    /// <summary>
    /// Returns the x-axis value of the bottom-right corner of R2
    /// </summary>
    public int BottomRightR2X
    {
        get { return rectangle2.X + rectangle2.Width; }
    }
    /// <summary>
    /// Returns the y-axis value of the bottom-right corner of R2
    /// </summary>
    public int BottomRightR2Y
    {
        get { return rectangle2.Y + rectangle2.Height; }
    }
    /// <summary>
    /// Returns the rectangle formed by how much the rectangles overlap.
    /// </summary>
    public Rectangle Overlap
    {
        get { return collisionZone; }
    }
    #endregion
    #region Methods
    public bool AreColliding()
    {
        if (rectangle1.Intersects(rectangle2))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    public Vector2 StopOnCollision(Vector2 position, Vector2 moveDir, int currentspeed)
    {
        if (Overlap.Width < Overlap.Height)
        {
            if (position.X < rectangle2.Left)
            {
                if (moveDir.X > 0) //Moving Right
                {
                    moveDir = Vector2.Zero;
                }
                else
                {
                    moveDir.X = -currentspeed;
                    moveDir.Y = 0;
                }
            }
            //else if ((position.X + 33) > rectangle2.Right)
            else if (position.X < rectangle2.Right)
            {
                if (moveDir.X < 0) //Moving Left
                {
                    moveDir = Vector2.Zero;
                }
                else
                {
                    moveDir.X = currentspeed;
                    moveDir.Y = 0;
                }
            }
        }
        else
        {
            if (Overlap.Y == rectangle2.Top)
            {
                if (moveDir.Y > 0) //Moving Down
                {
                    moveDir = Vector2.Zero;
                }
                else
                {
                    moveDir.Y = -currentspeed;
                    moveDir.X = 0;
                }
            }
            else
            {
                if (moveDir.Y < 0) //Moving Up
                {
                    moveDir = Vector2.Zero;
                }
                else
                {
                    moveDir.Y = currentspeed;
                    moveDir.X = 0;
                }
            }
        }
        return moveDir;
    }
    #endregion
}

这有点简单:-您使用正在检查的两个碰撞框(矩形)实例化类。-您可以检查以确保它们确实发生了碰撞。-要使用StopOnCollision,您需要输入正在移动的物体的位置(参考的x,y坐标)、要用来改变或阻止其移动的矢量,以及物体移动的速度(每帧像素)

我希望这能像帮助我一样帮助其他人。