2017-01-21 5 views
1

Я пытаюсь сделать простую игру на C++, используя xcode в качестве компилятора и SFML в качестве библиотеки. Пока я создал графический интерфейс, фон и спрайт (для космического корабля). Я также добавил обнаружение клавиш со стрелками, чтобы иметь возможность перемещать объект, но проблема в том, что когда я перемещаю объект, он не перемещается плавно, и вы можете видеть, что это своего рода «прыжок».Игра SFML 2D - Движение гладкого космического корабля

main.cpp

#include <SFML/Audio.hpp> 
#include <SFML/Graphics.hpp> 
#include "Spaceship.hpp" 
#include <vector> 

// Here is a small helper for you! Have a look. 
#include "ResourcePath.hpp" 

int main(int, char const**) 
{ 

    // Create the main window 
    sf::RenderWindow window(sf::VideoMode(800, 600), "SpaceShuttle"); 
    window.setFramerateLimit(30); 
    // Call to non-static member function without an object argument 
    // Set the Icon 
    sf::Image icon; 
    if (!icon.loadFromFile(resourcePath() + "space-shuttle.png")) { 
     return EXIT_FAILURE; 
    } 
    window.setIcon(icon.getSize().x, icon.getSize().y, icon.getPixelsPtr()); 

    // Load a sprite to display 
    sf::Texture texture; 
    if (!texture.loadFromFile(resourcePath() + "bg.png")) { 
     return EXIT_FAILURE; 
    } 
    sf::Sprite sprite(texture); 

    // Create a graphical text to display 
    sf::Font font; 
    if (!font.loadFromFile(resourcePath() + "sansation.ttf")) { 
     return EXIT_FAILURE; 
    } 
    sf::Text text("SpaceShuttle K1LLM33K", font, 50); 
    text.setFillColor(sf::Color::White); 
    text.setPosition(100.0, 130.0); 


    // Load a music to play 
    /* sf::Music music; if (!music.openFromFile(resourcePath() + "nice_music.ogg")) { return EXIT_FAILURE; } 
    // Play the music 
    music.play(); 
    */ 

    Spaceship spaceship(window); 
    sf::Clock sf_clock; 


    // Start the game loop 
    while (window.isOpen()) { 

     // Process events 
     sf::Event event; 
     while (window.pollEvent(event)) { 
      // Close window: exit 
      if (event.type == sf::Event::Closed) { 
       window.close(); 
      } 

      // Escape pressed: exit 
      if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { 
       window.close(); 
      } 
      // Move Spaceship 
      if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { spaceship.moveship('l'); } 
      else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { spaceship.moveship('r'); } 
      else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { spaceship.moveship('u'); } 
      else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { spaceship.moveship('d'); } 

     } 
     // Clear screen 
     window.clear(); 

     // Draw the sprite(s) 
     window.draw(sprite); 
     spaceship.drawsprite(window); 

     // Draw the string(s) 
     window.draw(text); 

     // Update the window 
     window.display(); 
    } 

    return EXIT_SUCCESS; 
} 

Spaceship.cpp

#include <SFML/Audio.hpp> 
#include <SFML/Graphics.hpp> 
#include "ResourcePath.hpp" 
#include "Spaceship.hpp" 

Spaceship::Spaceship(sf::RenderWindow& game_window){ 
    auto surface = game_window.getSize(); 
    ss_x = surface.x/2; 
    ss_y = surface.y/2; 
    ss_speed = 5; 
    ss_width = 128; 
    ss_height = 128; 
    ss_radius = ss_width/2; 

} 
void Spaceship::drawsprite(sf::RenderWindow& game_window){ 
    sf::Texture ship; 
    if (!ship.loadFromFile(resourcePath() + "space-shuttle-64.png")) { 
     return EXIT_FAILURE; 
    } 
    sf::Sprite ss_sprite(ship); 
    ss_sprite.setPosition(ss_x - ss_sprite.getGlobalBounds().width/2, ss_y - ss_sprite.getGlobalBounds().height/2); 
    game_window.draw(ss_sprite); 
} 

void Spaceship::moveship(char move){ 
    if(move == 'l'){ ss_x -= ss_speed; } 
    else if(move == 'r'){ ss_x += ss_speed; } 
    else if(move == 'u'){ ss_y -= ss_speed; } 
    else if(move == 'd'){ ss_y += ss_speed; } 
} 

Spaceship::~Spaceship(){} 

Spaceship.hpp

#ifndef Spaceship_hpp 
#define Spaceship_hpp 
#include <iostream> 
#include <SFML/Audio.hpp> 
#include <SFML/Graphics.hpp> 
#include <stdio.h> 

using namespace std; 

class Spaceship { 
public: 
    Spaceship(); 
    Spaceship(sf::RenderWindow&); 
    ~Spaceship(); 
    void moveship(char); 
    void drawsprite(sf::RenderWindow&); 
private: 
    signed int ss_x, ss_y; 
    unsigned int ss_speed; 
    int ss_width, ss_height, ss_radius; 

}; 

#endif /* Spaceship_hpp */ 
+0

Попробуйте с VSync вместо 30 кадров в секунду. – LogicStuff

+0

Я могу порекомендовать прочитать [Fix Your Timestep!] (Http://gafferongames.com/game-physics/fix-your-timestep/) - это относится к вашей проблеме и хорошему чтению. См. Также [Основы интеграции] (http://gafferongames.com/game-physics/integration-basics/). –

+0

@LogicStuff VSync предназначен для устранения проблем с размыванием текстур, а не для исправления частоты кадров или решения проблем с плавным перемещением. –

ответ

1

Как уже было предложено в комментариях, проблема здесь заключается в том, что вы не принимая во внимание прошедшее время между двумя кадрами в ваших вычислениях. Следовательно, происходит то, что вы добавляете фиксированную скорость в любом кадре, игнорируя тот факт, что двум последовательным кадрам может понадобиться совсем другое время для завершения.

Существует еще одна проблема (которая, как представляется, является основным источником ваших проблем): вы проверяете, нажаты ли клавиши внутри цикл событий. Это неверно, это будет верно, только если ваш ключ нажат И были другие события во время цикла. Вам нужно проверить это на каждом кадре.

Другой способ решения последней проблемы, чтобы иметь логические флаги, которые вы установили для true/false, когда вы обнаружили press/release для ключей, которые вы хотите проверить. Если вы посмотрите на метод isKeyPressed, вы также узнаете, что этот второй метод более эффективен, чем первый.

Самый простой способ изменить свой основной цикл, чтобы получить истекшее время в каждом кадре, according to the docs что-то вроде этого:

sf::Clock sf_clock; 

// Start the game loop 
while (window.isOpen()) { 
    // Get time elapsed since last frame 
    float dt = clock.restart().asSeconds(); 

    // Process events 
    sf::Event event; 
    while (window.pollEvent(event)) { 
     // Close window: exit 
    } 

    // Move Spaceship, this must be done outside of the pollEvent loop ! 
     if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) spaceship.moveship(dt, 'l'); 
    else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) spaceship.moveship(dt, 'r'); 
     if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) spaceship.moveship(dt, 'u'); 
    else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) spaceship.moveship(dt, 'd'); 

    } 
    // Draw, etc.. 
} 

Тогда вы должны принять dt во внимание moveship метода (я изменил свой if/else if в switch case, который я нахожу уборщик в этом случае и должен быть более эффективными):

void Spaceship::moveship(float dt, char move) { 
    switch (move) { 
     case 'l': ss_x -= dt * ss_speed_x; break; 
     case 'r': ss_x += dt * ss_speed_x; break; 
     case 'u': ss_y -= dt * ss_speed_y; break; 
     case 'd': ss_y += dt * ss_speed_y; break; 
    } 
} 

Как предложенное Jesper Juhl, я сделал некоторые integration здесь (и вы действительно должны взглянуть на the article).

Кстати, я хотел бы предложить некоторые изменения в коде:

// First of all, you should use floating-point values that you convert 
// in screen space at render time for your coordinates 
// You could also think about using vectors instead, I won't here 
class Spaceship { 
private: 
    float ss_x, ss_y; 
    float ss_speed_x, ss_speed_y; 

    // You should also store your sprite instead of creating it over 
    // and over again 
    sf::Sprite ss_sprite; 
}; 


Spaceship::Spaceship(sf::RenderWindow& game_window) { 
    // You can then take those modifications into account 
    // in your constructor: 
    auto surface = game_window.getSize(); 
    ss_x = ss_y = 0.5f; 
    ss_speed_x = 5.f/surface.x; 
    ss_speed_y = 5.f/surface.y; 
    ss_width = 128; 
    ss_height = 128; 
    ss_radius = ss_width/2; 

    sf::Texture ship; 
    if (!ship.loadFromFile(resourcePath() + "space-shuttle-64.png")) { 
     // This is really an awful way to handle an error, but I won't 
     // go into details here. A better way would be to have an Init() 
     // method that returns an error code on failure for example. 
     exit(EXIT_FAILURE); 
    } 

    ss_sprite = sf::Sprite(ship); 
    // http://www.sfml-dev.org/documentation/2.4.1/classsf_1_1Transformable.php#a56c67bd80aae8418d13fb96c034d25ec 
    ss_sprite.setOrigin(ss_width/2, ss_height/2); 
} 

// Finally, you reflect those modifications in your draw code 
void Spaceship::drawsprite(sf::RenderWindow& game_window){ 
    auto size = game_window.getSize(); 
    ss_sprite.setPosition(ss_x * size.x, ss_y * size.y); 
    game_window.draw(ss_sprite); 
} 
+0

Итак, я попытался с внесенными вами изменениями, и корабль движется очень медленно, увеличивая ss_speed_x и ss_speed_y, не помогло. –

+0

также не хочет двигаться плавно –

+1

Я написал все эти без каких-либо испытаний, я посмотрю на это – Zouch

 Смежные вопросы

  • Нет связанных вопросов^_^