2017-12-28 11 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 
} 

원인이 토큰은, 함수의 코드라고 그 좌표 CircleShape PositionID 대상 위치가 여기에 관련이 없습니다. 함수 이름이 모두라고 말합니다.

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

는, 방향이 현재의 위치에 따라 4 개 방향으로

토큰 이동 은 (알림, 그 보드 게임, 그것은 내가 여기에 무슨 짓을했는지 이해하기 쉬울 것이다), 끝에 현재 위치가 동일

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(); 
    } 
} 
하면 목표 위치, 토큰 이동이 중지됩니다. 이제 여기에 주요한 문제가 있습니다. 제 토큰은 때때로 목표 위치를 놓치고, 보드 주위를 달리다가 놓친 목표 위치에서 멈 춥니 다. 그것은 완전히 무작위로 발생합니다. 때로는 응용 프로그램이 토큰을 중지해야하는 마지막 if 문을 검사 할 시간이 충분하지 않은 경우가 있습니다. 천천히 내려 TOKEN_SPEED 도움이되지만 너무 느리게 이동합니다. 또 다른 이유는 내 마지막 if 문이 이렇게 보이는 이유입니다. 단순히 (int)m_token.getPosition().x == getTargetPositionX()을 확인하면 거의 항상 실패합니다. float position 값을 int로 변환하지 않으면 더욱 심각해질 것입니다. 마지막에 여기 당신이 그것을 훨씬 더 복잡 필요 이상으로하고 있다고 생각 gameLoop()

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

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

답변

1

에서 내 update() 기능입니다. 특정 문제는 타이밍 문제 일 가능성이 큽니다. 지나친 시간 (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}); 

플레이어의 위치 자체를 - 위해 단순성 나는 단지 하나의 토큰만을 가졌습니다 - 그냥 놀이터에서 현재 토큰 위치의 "인덱스"를 나타내는 부동 소수점 숫자입니다 :

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

것은, 내가 추가하고 싶습니다 :

는 점 사이의 위치를 ​​결정하기 위해, 우리는 단지 둥근 위치를 빼야 할 것 작은 들판 사이의 경로를 애니메이션 "오프셋 점프"

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

이 뒤에 실제 수학은 여기서 중요하지 않지만 단계는 0에서 1의 범위 때문에,이 멋진 포물선을 만들기 위해 오히려 쉽게 "경로"(오프셋이 0에서 -25로 변경되고 다시 0 점 사이).

이제 우리는 그냥 벡터 수학 사용하여 토큰의 위치를 ​​설정 (에 따라 이전 및 다음 필드 사이에 점을 결정을 step 계산 및 우리의 점프는 오프셋 (offset)를 부가) :

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(); 
    } 
} 
관련 문제