Граничные рамки для обнаружения столкновений перекрываются и вызывают проблемы

Следующий код — моя попытка помешать ПК (персонажу игрока) пройти через NPC.

  • Оператор if проверяет, пересекаются ли два прямоугольника (BoundingBox).
  • CollisionBox определяет область перекрывающихся BoundingBox.
  • moveDir определяет изменение вектора, которое ПК будет претерпевать после оператора if (например: если moveDir=(2,0) ПК переместится на два пикселя вправо)
  • 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 в направлении, противоположном тому, на сколько он перекрывается (или больше), удержит ПК на краю 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, персонаж переставал двигаться в этом направлении, но мог двигаться в любом из трех других направлений?

Или, говоря более общим языком, как сделать так, чтобы один прямоугольник, пересекающийся с другим прямоугольником, потерял способность двигаться к пересекаемому прямоугольнику, не препятствуя его движению в любом другом направлении?


person Zach    schedule 16.07.2013    source источник


Ответы (2)


Что я сделал в такой ситуации:

  • Выполните смещение в обычном режиме (т.е. добавьте вектор движения персонажа к его положению)
  • Проверьте все препятствия на предмет столкновения. При столкновении «телепортировать» персонажа от препятствия, скажем, на один пиксель. Продолжайте делать это до тех пор, пока не прекратится столкновение.

Это, вероятно, не самое быстродействующее решение, но оно выполняет свою работу. Хорошо работает, когда есть много динамических вещей, которые также могут конфликтовать.

person CSJ    schedule 16.07.2013
comment
Я пробовал все мыслимые способы сделать то, что вы описали. Это работало довольно хорошо, но примерно в 25% случаев, когда персонаж отворачивался от NPC, с которым только что столкнулся персонаж, персонаж телепортировался на другую сторону NPC. - person Zach; 16.07.2013
comment
Не телепортируйте своего персонажа в направлении, противоположном его движению (это может вызвать нежелательное поведение при телепортации, которое вы описываете); вместо этого телепортируйте своего персонажа от препятствия. Это может означать, что перед применением смещения запишите логические переменные для того, начал ли он выше/ниже препятствия, и начал ли он слева/справа от препятствия. Телепортируйтесь в указанном направлении. - person CSJ; 16.07.2013
comment
Я не телепортировался в обратном направлении. Я отредактировал код, который использовал, всего через минуту после того, как вы опубликовали. - person Zach; 16.07.2013

После [i]много[/i] тряски головой и употребления алкоголя я наконец нашел решение. Я даже поместил его в класс, чтобы другие могли использовать его для решения подобных проблем.

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 отсчета), вектор, который вы хотите использовать, чтобы изменить или воспрепятствовать его движению, и скорость, с которой движется объект (пикселей на кадр). )

Я надеюсь, что это поможет всем остальным так же, как помогло мне

person Zach    schedule 17.07.2013