贪吃蛇游戏:快速响应 vs 碰撞漏洞

4

我有一个使用SFML C++编写的贪吃蛇游戏,现在我被两个选项困住了。如果我这样设置控制:

    if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Up
            || event.key.code == sf::Keyboard::W) && move != Down)
        move = Up;

    else if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Down
            || event.key.code == sf::Keyboard::S) && move != Up)
        move = Down;

    else if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Left
            || event.key.code == sf::Keyboard::A)&& move != Right)
        move = Left;

    else if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Right
            || event.key.code == sf::Keyboard::D) && move != Left)
        move = Right;

这里,控制非常灵敏。问题在于,例如,如果蛇正在向右移动,您快速连续按下“上”和“左”键,则蛇尚未向上移动,但是上方向控制将被缓冲,因此蛇将被允许向左移动,并最终死亡。
如果我将这些运动条件包装在if语句中,每帧仅允许更新一次,则可以解决该错误,但控制响应速度会大大降低。
因此,我的问题是,如何在避免移动到自身的错误的同时保持快速更新运动的灵敏度?

1
那么这已经是你的解决方案了,不是吗?只需保存输入列表,并在需要进行下一步操作时从中读取即可。 - Cubic
所以,我不仅应该保留 enum Direction{Up, Down, Left, Right};,还要跟踪以前的方向吗?好的,我觉得有道理。我会尝试实现它,谢谢。 - Austin
2
你可以随时将按键附加到队列中,并在计时器中执行该队列。 - xuhdev
以前没有见过这个,但可能就是我要找的。如果我不想排队超过2个按键,那么队列的最大大小可以设置吗? - Austin
如果需要的话,您可以在容器周围创建一个包装器来提供该行为,或者您可以在追加之前检查其size()成员函数:如果当前size() == MAX_CAPACITY,则忽略输入;否则将其追加到队列中。 - bku_drytt
显示剩余2条评论
2个回答

1
由于您正在制作贪吃蛇游戏,问题不在于您的输入(但那些“if”应该是一个switch语句),而在于与输入相应的对象,例如“Snake_Object”。正常的游戏在按下方向键时会强制设置一定数量的位置移动。如果您观察游戏,如果您按上键,则蛇会立即向上移动一个碰撞框,比之前高出一个。这确保了您无法发生意外的碰撞,并仍然保持正常的fps流畅性。

因此,当move变量被发送到Snake时,它应该在第一次实例中向该方向移动整个碰撞框,然后以正常速度移动。


啊,我没意识到原版贪吃蛇游戏是这样的。我会尽快尝试实现这个功能。目前,我想到了一个临时解决方案,即将输入选项从时间循环中移除,但让它们控制第二个移动变量。然后,在时间/移动循环中将第一个移动变量设置为等于第二个变量。这使得它能够响应而不会出现错误,但我仍想尝试正确的方法。 - Austin

1
我没有游戏开发经验,甚至不确定我是否理解了你的问题。
然而,我想知道以下代码结构是否有助于控制更新过程:
inline void UpdateMoveUp(Move& move)      { if(move != Down)  { move = Up;    }}
inline void UpdateMoveDown(Move& move)    { if(move != Up)    { move = Down;  }}
inline void UpdateMoveLeft(Move& move)    { if(move != Right) { move = Left;  }}
inline void UpdateMoveRight(Move& move)   { if(move != Left)  { move = Right; }}
inline void UpdateMoveInvalid(Move& move) { move = Invalid; }

inline void HandleUpdate(const Code& code, Move& move) {

  switch(code) {

    case sf::Keyboard::Up:
      return UpdateMoveUp(move);
    case sf::Keyboard::W:
      return UpdateMoveUp(move);

    case sf::Keyboard::Down:
      return UpdateMoveDown(move);
    case sf::Keyboard::S:
      return UpdateMoveDown(move);

    case sf::Keyboard::Left:
      return UpdateMoveLeft(move);
    case sf::Keyboard::A:
      return UpdateMoveLeft(move);

    case sf::Keyboard::Right:
      return UpdateMoveRight(move);
    case sf::Keyboard::D:
      return UpdateMoveRight(move);

    default:
      return UpdateMoveInvalid(move);
  }
}

void HandleInput(const Event& event, Move& move) {
  // I assume that `move` comes from somewhere else
  if(event.type == sf::Event::KeyPressed) {
    return HandleUpdate(event.key.code, move);
  }
}

我个人的看法是,您正在将更新功能隔离到一个单独的位置,并且可以在那里添加更多条件,而无需再次测试整个条件。
换句话说,您可以向每个UpdateMoveXXXX函数添加进一步的条件,例如取消缓冲并更新保存时间戳并移动(然后仅在上一个时间戳减去当前时间戳大于/小于某个数字时才移动)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接