SFML and moving object to position
我的SFML棋盘游戏有问题。我得到了在木板上移动的令牌,木板有40个字段,每个字段都定义了positionX和positionY。但是有时候,这完全是随机的,我的代币错过了他的目标位置,在局上进行了额外的回合,然后停下来。
1 2 3 4 5 6 7 | class Field { protected: int m_positionX; int m_positionY; public: //getters and setters }; |
每个玩家都有PositionID目标位置坐标和CircleShape,这是会引起麻烦的令牌
1 2 3 4 5 6 7 8 | class Player { sf::CircleShape m_token; int m_positionID = 0; int m_targetPositionX; int m_targetPositionY; public: //getters and setters } |
然后在我的
1 2 3 4 5 6 | void GameEngine::setInMotion(int number) { m_players[m_activePlayer].startMoving(); m_players[m_activePlayer].incrementPositionID(number); m_players[m_activePlayer].setTargetPositionX(m_gameBoard.getField(m_players[m_activePlayer].getPositionID()).getPositionX()); m_players[m_activePlayer].setTargetPositionY(m_gameBoard.getField(m_players[m_activePlayer].getPositionID()).getPositionY()); } |
然后是最重要的功能,它实际上在板上移动令牌。
#定义TOKEN_SPEED 1000
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | void Player::moveForward(sf::Time dt) { if ((int)m_token.getPosition().x >= 240 && (int)m_token.getPosition().y >= 600) { this->m_token.move(-TOKEN_SPEED * dt.asSeconds(), 0); } if ((int)m_token.getPosition().x <= 240 && (int)m_token.getPosition().y >= 40) { this->m_token.move(0, -TOKEN_SPEED * dt.asSeconds()); } if ((int)m_token.getPosition().x <= 800) && (int)m_token.getPosition().y <= 40) { this->m_token.move(TOKEN_SPEED * dt.asSeconds(), 0); } if ((int)m_token.getPosition().x >= 800) && (int)m_token.getPosition().y <= 600) { this->m_token.move(0, TOKEN_SPEED * dt.asSeconds()); } if ((int)m_token.getPosition().x >= getTargetPositionX() - 1 && (int)m_token.getPosition().x <= getTargetPositionX() + 1 && (int)m_token.getPosition().y >= getTargetPositionY() - 1 && (int)m_token.getPosition().y <= getTargetPositionY() + 1) { this->stopMoving(); } } |
令牌在4个方向上移动,方向取决于当前位置(提醒,它是棋盘游戏,这会更容易理解我在这里所做的事情)
最后,一旦当前位置等于目标位置,令牌便停止移动。现在这是主要问题,我的令牌有时会错过目标位置,在板上绕行,然后停在他错过的目标位置。它完全随机发生。就像有时应用程序没有足够的时间检查应该停止令牌的if语句。放慢
的
1 2 3 4 5 6 | void GameState::update(sf::Time dt) { if (m_gameEngine.getActivePlayer().isMoving()) { m_gameEngine.getActivePlayer().moveForward(dt); } } |
我认为您正在使它变得比所需的复杂得多。您的特定问题很可能是时间问题:经过的时间(
但是,我将以完全不同的方式来实现运动,这也将使整个实现更加容易和动态。
首先,我使用矢量定义游戏板及其位置。如果要使用更复杂的路径(例如分支或快捷方式),则可能需要定义自己的链接列表或类似内容。
在我的示例中,回到比赛场地,我通过一系列位置(每个位置代表玩家可能站立的一个可能的场地)来定义木板:
1 2 3 4 5 6 7 8 9 10 11 12 13 | std::vector<sf::Vector2f> board; board.push_back({75, 75}); board.push_back({150, 50}); board.push_back({250, 50}); board.push_back({325, 75}); board.push_back({350, 150}); board.push_back({350, 250}); board.push_back({325, 325}); board.push_back({250, 350}); board.push_back({150, 350}); board.push_back({75, 325}); board.push_back({50, 250}); board.push_back({50, 150}); |
玩家位置本身为简单起见,我只有一个代币a"只是一个浮点数,代表比赛场上当前代币位置的"索引":
1 | float playerPos = 0.f; |
听起来奇怪吗?是的,或多或少。数字的整数部分表示索引,而小数点后面的部分表示字段之间的实际位置。例如,值为3.75表示令牌在第四字段(索引3)和第五字段(索引4)之间为75%。
绘制游戏板非常简单,我已将其简化为仅为每个字段绘制一些圆形:
1 2 3 4 | for (auto &pos : board) { field.setPosition(pos); window.draw(field); } |
现在要绘制令牌,我们必须确定令牌之前和之后的点:
1 2 | const sf::Vector2f playerPosLast = board[static_cast<std::size_t>(playerPos)]; const sf::Vector2f playerPosNext = board[static_cast<std::size_t>(playerPos + 1) % board.size()]; |
强制类型转换将取整值,在第二种情况下,我只是确保我们会溢出回到木板的开始。
要确定点之间的位置,我们只需减去舍入后的位置:
1 | const float step = playerPos - static_cast<std::size_t>(playerPos); |
虽然我们现在拥有计算令牌可视表示位置的所有内容,但我想添加一个小的"跳跃偏移量"以动画化字段之间的路径:
1 | const sf::Vector2f jumpOffset(0, 25.f * (2.f * (step - .5)) * (2.f * (step - .5)) - 25.f); |
此后的实际数学运算在这里并不重要,但是由于步长范围是0到1,因此很容易使它成为类似于"路径"的抛物线(偏移量从0更改为-25,然后在点之间重新转换为0 )。
现在,我们只需要使用一些矢量数学来设置令牌的位置(根据计算出的
1 2 | player.setPosition(playerPosLast + (playerPosNext - playerPosLast) * step + jumpOffset); window.draw(player); |
但是如何移动播放器?很简单如果要让播放器向前移动4个字段,请将4添加到
如果编译并运行以下演示,您将得到一个简单的窗口,其中令牌无限期地从一个字段跳到另一个字段:
这里是完整的源代码,没有注释,但是大多数事情应该是显而易见的,或者在上面已经解释过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #include <SFML/Graphics.hpp> #include <vector> int main() { sf::RenderWindow window({480, 480},"Board Game"); std::vector<sf::Vector2f> board; board.push_back({75, 75}); board.push_back({150, 50}); board.push_back({250, 50}); board.push_back({325, 75}); board.push_back({350, 150}); board.push_back({350, 250}); board.push_back({325, 325}); board.push_back({250, 350}); board.push_back({150, 350}); board.push_back({75, 325}); board.push_back({50, 250}); board.push_back({50, 150}); sf::CircleShape field(25, 24); field.setFillColor(sf::Color::White); field.setOutlineColor(sf::Color::Black); field.setOutlineThickness(2); field.setOrigin({25, 25}); sf::CircleShape player(20, 3); player.setFillColor(sf::Color::Red); player.setOutlineColor(sf::Color::Black); player.setOutlineThickness(2); player.setOrigin({20, 20}); float playerPos = 0.f; while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { switch (event.type) { case sf::Event::Closed: window.close(); break; default: break; } } window.clear({0, 127, 127}); for (auto &pos : board) { field.setPosition(pos); window.draw(field); } const sf::Vector2f playerPosLast = board[static_cast<std::size_t>(playerPos)]; const sf::Vector2f playerPosNext = board[static_cast<std::size_t>(playerPos + 1) % board.size()]; const float step = playerPos - static_cast<std::size_t>(playerPos); const sf::Vector2f jumpOffset(0, 25.f * (2.f * (step - .5)) * (2.f * (step - .5)) - 25.f); player.setPosition(playerPosLast + (playerPosNext - playerPosLast) * step + jumpOffset); window.draw(player); window.display(); if ((playerPos += .001f) > board.size()) playerPos -= board.size(); } } |