【问题标题】:Weird sticky collision detection in 2D Windows Form game2D Windows Form 游戏中奇怪的粘性碰撞检测
【发布时间】:2013-12-16 13:34:17
【问题描述】:

这是question that I posted last night 的后续问题。我必须为学校编写一个 Windows 窗体的游戏,我正在创建一个迷宫游戏,其中玩家必须在被杀死之前穿过迷宫。作为迷宫游戏,必须使用一些碰撞检测来确保玩家不会简单地穿过墙壁(这将是一个非常无聊的游戏)。根据我昨晚提出的问题,我已经实现了一项防止这种情况发生的功能,但我得到了一些奇怪的结果。

当玩家碰到墙时,游戏会阻止他们,玩家最终会被卡住。玩家不能移动,除非他们按下组合键穿过墙(我的游戏使用 WASD,所以如果我碰到墙,我可以按 W + A 并穿过墙到另一边,我的玩家会被卡住) .

这是我的碰撞代码:

                // This goes in the main class
                foreach (Rectangle wall in mazeWalls)
                {
                    if (playerRectangle.IntersectsWith(wall))
                    {
                        player.Stop();
                    }
                }   

这是玩家的移动代码:

    public void Move(Direction dir)
    {
        // First, check & save the current position.
        this.lastX = this.x;
        this.lastY = this.y;

        if (dir == Direction.NORTH)
        {
            if (!CheckCollision())
            {
                this.y -= moveSpeed;
            }
            else
            {
                this.y += 1;
            }

        }
        else if (dir == Direction.SOUTH)
        {
            if (!CheckCollision())
            {
                this.y += moveSpeed;
            }
            else
            {
                this.y -= 1;
            }


        }
        else if (dir == Direction.EAST)
        {
            if (!CheckCollision())
            {
                this.x += moveSpeed;
            }
            else
            {
                this.x -= 1;
            }

        }
        else if (dir == Direction.WEST)
        {
            if (!CheckCollision())
            {
                this.x -= moveSpeed;
            }
            else
            {
                this.x += 1;
            }


        }
    }

我的CheckCollision() 方法:

    private bool CheckCollision()
    {            
        // First, check to see if the player is hitting any of the boundaries of the game.
        if (this.x <= 0)
        {
            isColliding = true;
        }
        else if (this.x >= 748)
        {
            isColliding = true;
        }
        else if (this.y <= 0)
        {
            isColliding = true;
        }
        else if (this.y >= 405)
        {
            isColliding = true;
        }
        else if (isColliding)
        {
            isColliding = false;
        }
        // Second, check for wall collision.
        return isColliding;
    }

Stop() 方法:

    public void Stop()
    {
        this.x = lastX;
        this.y = lastY;            
    }

Here 是我上传的 gif,以便您可以看到玩家在迷宫墙中的行为。注意他是如何滑过墙壁并反复卡住的。

我的问题是我如何让这个播放器停止粘着并真正能够随着墙壁滑动和移动?我尝试了多种碰撞模式,并使用了昨晚的(非常有帮助的)答案,但他不会停止粘在墙上!如果您需要任何其他详细信息/信息,请告诉我。

编辑:Dan-o 要求的输入代码:http://pastebin.com/bFpPrq7g

【问题讨论】:

  • 确保您的代码逻辑可靠。代码可能会在大约同一时间执行检查 2 个按键事件,然后破坏您的逻辑。尝试确保只有按键事件检查仅在独占模式下工作,例如对最后一个事件使用线程等待/阻塞(排队方法)。
  • @Kelmen 你想让我发布我的键盘输入代码吗?因为游戏是在 WinForm 中构建的,所以输入是由键盘事件处理的,老实说,我对此并不了解。如果你愿意,我可以发布。
  • 抱歉,我对代码审查不感兴趣。我给你一个方向如何改进/调试你的代码。如果你不知道如何解决这个问题,比如使用 Debug.WriteLine("x is " + x.ToString())。并尝试在开发模式下重现此问题,当它发生时,查看您的 VS 输出窗口,检查消息,查看 x 值是否更改为某个意外值,并追溯您的代码,添加更多内容以查明原因。
  • @Kelmen 谢谢。这不是真正的代码审查。我只是想看看发布这种代码是否有助于解决问题。
  • @MrSir:是的,请贴出键盘输入代码。

标签: c# winforms collision-detection collision


【解决方案1】:

游戏设计是一门复杂的学科。实现 2D 迷宫游戏有一些非常有据可查的策略。任何人都可以在这里做的最多的事情就是根据你所做的事情提出一般性建议,因为你知道商业游戏不是在这里想要做的。所以无论如何,我会投入我的两分钱。我只在 OpenGL 中做过类似的事情,而不是在 Windows 窗体中。

我看到的第一件事是你的精灵没有在它的车道上保持居中。计算最终会使事情变得更容易,因为玩家可以移动的方向总是只有 4 个。当对角线也可能时,北、南、东和西的意义就没有那么大了。

设计您的游戏板,使您的墙壁和车道宽度相同。使用均匀间隔的行和列。由于您使用的是固定板尺寸,因此这应该很简单,只需将宽度和高度除以所需的行数和列数即可。

完成此操作后,您将始终根据您的 x 和 y 坐标知道您所在的车道。例如,如果您的列是 80 像素宽,并且您在 x = 160,那么您知道您在第 2 列(基于 0 的数组中的 1)。这是您能够将敌人放在棋盘上并能够以编程方式跟踪他们去向的唯一方法。此外,您还可以根据路线调整玩家和敌人的大小。

假设您的行和列是 80Wx60H 像素(它们可以是您最喜欢的任何东西)。现在假设您的玩家从 0, 160 开始向东移动。根据您的地图,当他到达第 3 列(从零开始的模型中为 2)时,他可以向北和向南移动。意思是,当他到达 160 时,160。请记住,如果 x = 0 开始第 1 列,则 80 开始第 2 列,依此类推。

暂时忘记碰撞,您可以使用这样的逻辑。

RectangleF playerRectangle = new RectangleF();
int COLUMN_WIDTH = 80;
int ROW_HEIGHT = 60;

if (playerRectangle.IntersectsWith(wall)){
    int column = playerRectangle.X / COLUMN_WIDTH;

    //----------------------------------------------
    // This will return false if the player 
    // is not positioned right at the column. The 
    // result of % will contain decimal digits.
    // playerRectangle.X has to be a float though.
    //----------------------------------------------
    if(column % 1 == 0){
        //--------------------------------------------
        // do this based on your keyboard logic. this 
        // is pseudo-code
        //--------------------------------------------
        if(keys == keys.UP){
            // Move up
        }
        else if(keys == keys.DOWN){
            // Move down
        }
    }
}

您要做的另一件事是将方向属性与墙壁一起存储。这样当你与一个人发生碰撞时,你就会知道你的运动选择是什么。如果您碰撞的墙是 WallOrientation.NorthSouth,则您只能在撞到它后向北或向南移动。或者,您可以逆向击打它的方向。如果没有按键被按下,你就坐着不动。这样的事情会做。

public enum WallOrientation{
    NorthSouth,
    EastWest
}

此代码均未经过测试。我只是临时写的,所以可能会有错误。我只是想给你一些想法。我希望这个建议对你有所帮助。

【讨论】:

  • 此时,我的截止日期只有几天,所以我想我会听取您的建议并实施一个基于网格的系统。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多