【问题标题】:SFML 2D game – Smooth spaceship movementSFML 2D 游戏 - 平滑的宇宙飞船运动
【发布时间】:2017-06-06 08:26:44
【问题描述】:

我正在尝试通过使用 xcode 作为编译器和 SFML 作为库来用 C++ 制作一个简单的游戏。到目前为止,我已经创建了一个 GUI、一个背景和一个精灵(用于宇宙飞船)。为了能够移动对象,我还添加了箭头键检测,但问题是当我移动对象时它移动不顺畅,你可以看到它有点“跳跃”。

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

宇宙飞船.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(){}

宇宙飞船.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 */

【问题讨论】:

  • 尝试使用 VSync 而不是 30 FPS。
  • 我可以推荐阅读Fix Your Timestep! - 它与您的问题相关并且值得阅读。另请参阅Integration Basics
  • @LogicStuff VSync 用于修复纹理撕裂问题,而不是真正用于修复帧速率或解决平滑运动问题。

标签: c++ user-interface sfml


【解决方案1】:

正如 cmets 中所建议的,这里的问题是您在计算中没有考虑两帧之间经过的时间。因此发生的情况是,您在任何帧都添加了固定量的速度,而忽略了两个连续帧可能需要非常不同的时间来完成这一事实。

还有另一个问题(这似乎是您的问题的主要来源):您正在检查是否在事件循环按下了键。这是不正确的,只有当您的键被按下并且循环期间有其他事件时才会如此。你需要在每一帧都检查一下。

解决最后一个问题的另一种方法是,当您检测到要测试的键的 press / release 时,将布尔标志设置为 true / false。如果您查看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..
}

那么你必须在moveship 方法中考虑dt(我在switch case 中更改了你的if / else if,我觉得在这种情况下更简洁,应该更有效):

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

【讨论】:

  • 所以我尝试了你所做的修改,船的移动非常缓慢,增加 ss_speed_x 和 ss_speed_y 没有帮助。
  • 也不想移动顺利
  • 这些都是我自己写的,没有做任何测试,我看看这个
  • 再次查看您的代码,您必须将 if (sf::Keyboard::IsKeyPressed() 内容移到您的事件 while 循环之外。这应该修复平滑度和非常缓慢的运动
  • 我认为这确实是问题所在,我试过了,效果很好!我更新了答案以考虑到这一点。我还将速度值更改为 250 而不是 5(这确实非常慢)。 Here is the code I used.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-30
  • 2012-12-14
  • 1970-01-01
  • 1970-01-01
  • 2013-12-07
相关资源
最近更新 更多