【问题标题】:How to correctly handle collision between a player and two overlapping walls XNA如何正确处理玩家和两个重叠墙 XNA 之间的碰撞
【发布时间】:2020-08-04 21:59:05
【问题描述】:

我正在尝试使用 MonoGame(它使用 XNA 的框架)为我的大学项目创建一个 2D 游戏,并且在两个重叠的矩形和一个玩家“Hitbox”矩形之间发生碰撞时遇到了很多麻烦。 如果玩家正在沿一个墙矩形对角线移动,而遇到另一个不碍事的垂直墙矩形,则玩家将被停止(这不是预期的结果)。

当玩家走进墙时,将调用 Player 类的 CollisionHandler 方法,并通过参数提供碰撞的 Rectangle 以及枚举 Side(本质上是 Wall 检查碰撞的方式)。该方法有一些条件,然后改变玩家的位置。这是它的代码:

public void CollisionHandler(Rectangle Wall, Side TestSide) // Assuming Hitbox and Wall are Intersecting
{
    if ((TestSide == Side.Up && Direction.Y > 0) // If the Wall is testing for collision Upwards and the Player is moving Downwards through it
        || (TestSide == Side.Down && Direction.Y < 0)) // or if the Wall is testing for collision Downwards and vice versa
    {
        Position.Y -= Direction.Y * MovementSpeed; // Y movement is reversed 
    }
    if ((TestSide == Side.Left && Direction.X > 0) // If the Wall is testing for collision to it's Left and the Player is moving to the Right through it
        || (TestSide == Side.Right && Direction.X < 0)) // or if the Wall is testing for collision to it's Right and vice versa
    {
        Position.X -= Direction.X * MovementSpeed; // X movement is reversed
    }
}

(方向是玩家的新位置减去他们之前的位置的归一化向量2)

问题是当玩家移动到两个重叠墙的角落时。 例如,这是我在地图上的两堵墙:

Walls.Add(new Rectangle(360, 240, 1, 120)); // Side = Side.Left
Walls.Add(new Rectangle(360, 240, 120, 1)); // Side = Side.Up

当玩家沿对角线向下和向右移动时(方向大致为 (0.707, 0.707)),“左”向的墙会与玩家的 Hitbox 相交,尽管玩家的 Hitbox 位于墙的后面和上方,从而减少播放器停止。

我已经尝试过很多次来解决这个问题,通常是通过改变墙的制作方式,而这实际上是墙呈现方式的最新迭代。在此之前,它们只是游戏中每个 Tile 上的大矩形,“CanEnter”属性为 false。

我已经断断续续地思考了几个星期,试图弄清楚如何才能防止这种情况发生,但实际上没有任何效果,现在我的项目由于这个问题而完全停滞不前。 我真的很感激一些关于如何解决这个问题的帮助或指示。

【问题讨论】:

  • 你的Rectangle HitBox是在Rectangle Wall里面还是外面?
  • @AzuxirenLeadGuy 问题发生时,矩形 HitBox 位于矩形墙内; CollisionHandler 检测到这一点并编辑 Player 的位置。

标签: c# xna collision monogame


【解决方案1】:

您还没有指定很多事情,但我想我可以做出一个有根据的猜测:您在获得输入后进行了位置更改,然后在检测到碰撞,这将位置 Vector 带到了它所在的位置。您可以通过至少两次“撤消”部分轻松解决此问题。

所以代替你做的地方

Position.Y -= Direction.Y * MovementSpeed;

你应该做类似的事情

Position.Y -= Direction.Y * MovementSpeed * 2;

您还需要将 HitBox 夹在墙的确切边缘。不这样做可能会使您的 Hitbox 矩形突出到墙外,您可能不希望这样。

现在更好的方法是只设置一次位置向量,这仅在从输入设置方向向量并检查碰撞后才合适。另外,我希望您使用基于加速度的运动,因为仅使用恒定速度进行移动会感觉粗糙,而使用加速度会使您的游戏流畅。

话虽如此,我可以建议对您的代码进行很多改进

我认为您正在使用 List Walls 为墙的每一侧存储一个矩形,并且我认为您在那里不必要地使问题复杂化。你需要一个有 4 面墙的“房间”。

假设你有一堵墙,如下所示

Rectangle Wall;

你将它初始化为类似的东西

Wall=new Rectangle(30,30,400,400);

这意味着你有 从 (30,30) 到 (430,30) 的顶墙, 从 (30,30) 到 (30,430) 的左墙, 从 (430,30) 到 (430,430) 的右墙, 和底墙为 (30,430) 到 (430,430)

现在我相信你有一个玩家 HitBox,它又是一个矩形

Rectangle HitBox;

现在在Update() 方法中,您需要测试HitBox 和Wall 是否相交,这很容易得到

if(HitBox.Intersects(Wall)
{
    //Handle the bouncing logic here
}

由于我有很多时间,我将为您提供一个完整的工作示例。

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace test
{
public class Game1: Game
{
    private readonly GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;
    private Texture2D HitboxTexture, WallTexture;
    private Rectangle HitBox, Wall;
    private Vector2 Position, Direction, Acceleration;
    float Speed;

    public Game1()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
    }

    protected override void Initialize(){base.Initialize();}

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        // TODO: use this.Content to load your game content here
        HitboxTexture = Content.Load<Texture2D>("dirt");
        WallTexture = Content.Load<Texture2D>("wall");
        Speed=2;
        HitBox=new Rectangle(0,0,40,40);
        Wall=new Rectangle(30,30,400,400);
        Position=Wall.Center.ToVector2();
    }

    protected override void Update(GameTime gameTime)
    {
        var ks=Keyboard.GetState();
        if (ks.IsKeyDown(Keys.Escape))Exit();
        
        //Get the Direction Vector from the keyboard

        Acceleration=Vector2.Zero;
        if(ks.IsKeyDown(Keys.Left))Acceleration.X=-1;
        else if(ks.IsKeyDown(Keys.Right))Acceleration.X=1;
        if(ks.IsKeyDown(Keys.Up))Acceleration.Y=-1;
        else if(ks.IsKeyDown(Keys.Down))Acceleration.Y=1;
        
        Acceleration*=Speed;

        Acceleration=Acceleration - Direction*0.54f;//Velocity = Acceleration - Friction

        Direction+=Acceleration;

        Position+=Direction; //Postition
        //Check for Intersectrion
        if(Wall.Intersects(HitBox))
        {
            //Intersection is true. Now add the bouncing logic

            //First, clip the box so that it is inside the wall
            if(HitBox.Left<Wall.Left)Position.X=Wall.X+1;
            if(HitBox.Top<Wall.Top)Position.Y=Wall.Y+1;
            if(HitBox.Right>Wall.Right)Position.X=Wall.Right-HitBox.Width -1;
            if(HitBox.Bottom>Wall.Bottom)Position.Y=Wall.Bottom-HitBox.Height-1;

            //Now, make the direction change

            if(HitBox.Left<Wall.Left || HitBox.Right>Wall.Right) Direction.X*=-1;
            if(HitBox.Top<Wall.Top || HitBox.Bottom>Wall.Bottom) Direction.Y*=-1;
            
            Direction*=2;//For True rebound
        }
        

        HitBox.X=(int)Position.X;
        HitBox.Y=(int)Position.Y;

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        Color color=gameTime.IsRunningSlowly?Color.Red:Color.CornflowerBlue;
        GraphicsDevice.Clear(color);

        // TODO: Add your drawing code here
        _spriteBatch.Begin();
        _spriteBatch.Draw(WallTexture, Wall, Color.White);
        _spriteBatch.Draw(HitboxTexture, HitBox, Color.White);
        _spriteBatch.End();

        base.Draw(gameTime);
    }
}
}

【讨论】:

    猜你喜欢
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 2013-04-07
    • 1970-01-01
    相关资源
    最近更新 更多