2017-12-28 16 views
0

SFMLボードゲームに問題があります。私はボードの周りを動くトークンを得て、ボードは40のフィールドを持ち、すべてのフィールドはpositionXとpositionYを定義しています。しかし、時々、完全にランダムに発生し、私のトークンは目標位置を逃し、ボード周りに追加のラウンドを行い、その後停止します。SFMLとオブジェクトを位置に移動

class Field { 
protected: 
    int m_positionX; 
    int m_positionY; 
public: 
    //getters and setters 
}; 

すべてのプレイヤーが、私はプレーヤーのためのいくつかの変数を設定しますsetInMotion()機能を持って私のGameEngineクラス、呼び出された関数のコードに続いてトラブルに

class Player { 
    sf::CircleShape m_token; 
    int m_positionID = 0;  
    int m_targetPositionX; 
    int m_targetPositionY; 
public: 
    //getters and setters 
} 

の原因となるトークンですPositionID目標位置座標とCircleShapeを持っていますここでは無関係です。関数名はすべてそれを言うと思います。

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(); 
    } 
} 

トークンは、現在位置が等しいたら方向が、最後には現在位置(リマインダー、そのボードゲーム、私がここで何をしたかを理解することが容易になります) に依存し、4つの方向に移動します目標位置、トークンの移動が停止します。ここでは主な問題があります。私のトークンは目標位置を逃してしまい、ボードの周りを走り回って、逃した目標位置で停止します。それは完全にランダムに起こります。それは時々アプリケーションがトークンを停止すべき最後のif文をチェックするのに十分な時間がないのが好きです。遅くなるとTOKEN_SPEEDが助けになりますが、遅すぎます。私の最後のif文がこのように見えるもう一つの理由は、単に私が(int)m_token.getPosition().x == getTargetPositionX()をチェックすると、ほとんどの場合、それは見逃してしまいます。 floatの位置の値をintに変換しないと、さらに悪化します。ここで終わりにgameLoop()

void GameState::update(sf::Time dt) { 

    if (m_gameEngine.getActivePlayer().isMoving()) { 
     m_gameEngine.getActivePlayer().moveForward(dt);   
    } 
} 

答えて

1

から私のupdate()関数である私はあなたが必要とされるよりも、それははるかに複雑作っていると思います。あなたの特定の問題はタイミングの問題である可能性が最も高いです:時間が過ぎて(dt)、あなたの目標位置を単純に乗り越えることができます。 の固定タイムステップを実装していない限り、これを回避することはできません(詳しく知りたい場合は、その用語を参照してください)。

しかし、私は動きを完全に異なる方法で実装しています。これにより、実装全体がより簡単でダイナミックになります。

まず、ゲームボードとその位置をベクトルで定義します。ブランチやショートカットなどのより複雑なパスが必要な場合は、独自のリンクリストなどを定義したい場合があります。

戻る競技場に、私の例では、私は位置のシーケンス(プレイヤーが立つ可能性がある一つの可能​​なフィールドを表す各位置)でボードを定義します。

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}); 

プレーヤーの位置自体 - のためにシンプルさ私は1つのトークンしか持っていません - 現在のトークン位置の "インデックス"を表す浮動小数点数です:

float playerPos = 0.f; 

音が奇妙ですか?はい、多かれ少なかれ。数値の整数部分はインデックスを表し、小数点の後ろの部分はフィールド間の実際の位置を表します。たとえば、値は3です。75は、トークンが第4の(インデックス3)と第5の(インデックス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); 

私たちは今、トークンの視覚的表現の位置を計算するためにすべてを持っているが、私は追加したいです小さなは、フィールド間のパスをアニメーション化する「オフセットジャンプ」:

const sf::Vector2f jumpOffset(0, 25.f * (2.f * (step - .5)) * (2.f * (step - .5)) - 25.f); 

は、この背後にある実際の数学は、ここでは重要ではありませんが、ステップは、0から1の範囲にあるので、この素敵な放物線にするために、むしろ簡単です"パス"(オフセットは0から-25まで変化し、 0点間)。

今我々はいくつかのベクトル数学(計算stepに基づいて、前と次のフィールドの間に点を決定し、私たちのジャンプオフセットを加える)を使用して、トークンの位置を設定します。

player.setPosition(playerPosLast + (playerPosNext - playerPosLast) * step + jumpOffset); 
window.draw(player); 

しかし、どのようにプレイヤーを移動するには?ものすごく単純。プレーヤーが4つのフィールドを前方に移動させたい場合は、playerPosに4を追加します。 2つのフィールドを戻したい場合は、2を引いてください。最後にオーバーフローしたら、ボードの有効範囲に折り返してください。

次のデモをコンパイルして実行する場合は、トークンが無期限フィールドからフィールドの周りにジャンプし、簡単なウィンドウになってしまいます:

Demo Screenshot

ここでは、完全なソースコードだ - 、コメントを外しほとんどのことは明白であるか上記で説明されるべきです:

#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(); 
    } 
} 
関連する問題