我的SFML棋盘游戏出了问题。我得到了在棋盘上移动的令牌,棋盘有40个场,每个场都定义了positionX和positionY。然而有时候,我的令牌完全随机发生,而我的目标位置错过了他的目标位置,在棋盘周围进行了额外的回合然后停止。
class Field {
protected:
int m_positionX;
int m_positionY;
public:
//getters and setters
};
每个玩家都有PositionID目标位置坐标和CircleShape,它是导致麻烦的标记
class Player {
sf::CircleShape m_token;
int m_positionID = 0;
int m_targetPositionX;
int m_targetPositionY;
public:
//getters and setters
}
然后在我的GameEngine
课程中,我得到了setInMotion()
函数,它为播放器设置了一些变量,这里调用的函数代码无关紧要,我想,函数名称都说明了这一切
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());
}
然后最重要的功能实际上是在棋盘上移动令牌。 #define TOKEN_SPEED 1000
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();
}
}
令牌向四个方向移动,方向取决于当前位置(提醒,其棋盘游戏,这将更容易理解我在这里做了什么)
最后,一旦当前位置等于目标位置,令牌就会停止移动。现在这里是主要问题,我的令牌有时会错过目标位置,会在棋盘周围跑动,然后停在他错过的目标位置。它完全随机发生。有时候应用程序没有足够的时间来检查应该停止令牌的last if语句。放慢TOKEN_SPEED
会有所帮助,但之后它的移动速度太慢了。这是我的最后一个if语句看起来像这样的另一个原因,如果我只是检查(int)m_token.getPosition().x == getTargetPositionX()
它几乎总是会错过。不将浮点位置值转换为int将使其更糟糕。最后,这是update()
gameLoop()
函数
void GameState::update(sf::Time dt) {
if (m_gameEngine.getActivePlayer().isMoving()) {
m_gameEngine.getActivePlayer().moveForward(dt);
}
}
答案 0 :(得分:1)
我认为你让它变得比要求复杂得多。您的具体问题很可能是一个时间问题:时间过去(dt
)可能足以让您简单地跨过您的目标位置(并且添加的+1不足以补偿)。除非您正在实施固定时间步长运动,否则您无法避免这种情况(如果您想了解更多内容,请查看该术语)。
但是,我以完全不同的方式实现了这个动作,这也将使整个实现变得更容易,更有活力。
首先,我使用矢量定义我的游戏板及其位置。如果您想要更复杂的路径,如分支或快捷方式,您可能需要定义自己的链接列表或类似的东西。
回到比赛场地,在我的例子中,我通过一系列位置定义棋盘(每个位置代表玩家可能站立的一个可能的场地):
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});
玩家位置本身 - 为简单起见,我只有一个令牌 - 只是一个代表&#34;索引的浮点数&#34;在比赛场地中当前的代币位置:
float playerPos = 0.f;
听起来很奇怪?是的,或多或少。数字的整数部分表示索引,而小数点后面的部分表示字段之间的实际位置。例如,值为3.75表示令牌在第四个(索引3)和第五个(索引4)字段之间为75%。
绘制游戏板非常简单,我已将其简化为仅为每个区域绘制一些圆形:
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);
虽然我们现在已经拥有了计算令牌视觉表示位置的所有内容,但我想添加一个小的跳跃偏移量&#34;动画字段之间的路径:
const sf::Vector2f jumpOffset(0, 25.f * (2.f * (step - .5)) * (2.f * (step - .5)) - 25.f);
这背后的实际数学并不重要,但由于步长范围从0到1,所以很容易使这个像#&#34;路径&#34; (偏移从0变为-25并在点之间变回0)。
现在我们只使用一些矢量数学设置令牌的位置(根据计算的step
确定上一个和下一个字段之间的点并添加我们的跳跃偏移量):
player.setPosition(playerPosLast + (playerPosNext - playerPosLast) * step + jumpOffset);
window.draw(player);
但如何移动玩家?很简单。如果您希望玩家向前移动4个字段,请将{4}添加到playerPos
。如果你想要移回2个字段,则减去2.只要确保在结束后溢出就回到板的有效范围内。
如果您编译并运行以下演示,您最终会得到一个简单的窗口,其中一个标记无限期地从一个字段跳到另一个字段:
这里是完整的源代码 - 未注释,但大多数内容应该是明显的或上面解释的:
#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();
}
}