sfml2d游戏&x2013;太空船平稳运行 我试图用XCODE作为编译程序,用SFML作为库,在C++中做一个简单的游戏。到目前为止,我已经为宇宙飞船创建了一个GUI、一个背景和一个精灵。我还添加了箭头键检测,以便能够移动对象,但问题是,当我移动对象时,它移动不平稳,你可以看到它有点“跳跃” main.cpp #包括 #包括 #包括“Spaceship.hpp” #包括 //这里有一个小帮手给你!看一看。 #包括“ResourcePath.hpp” int main(int,字符常量**) { //创建主窗口 sf::渲染窗口(sf::视频模式(800600),“航天飞机”); 设置帧率限制(30); //不带对象参数调用非静态成员函数 //设置图标 sf::图像图标; 如果(!icon.loadFromFile(resourcePath()+“space sputter.png”)){ 返回退出失败; } setIcon(icon.getSize().x,icon.getSize().y,icon.getPixelsPtr()); //加载要显示的精灵 sf::纹理; 如果(!texture.loadFromFile(resourcePath()+“bg.png”)){ 返回退出失败; } 雪碧雪碧(纹理); //创建要显示的图形文本 sf::字体; 如果(!font.loadFromFile(resourcePath()+“sansation.ttf”)){ 返回退出失败; } sf::文本文本(“航天飞机K1LLM33K”,字体,50); text.setFillColor(sf::Color::White); text.setPosition(100.0130.0); //加载要播放的音乐 /*sf::Music Music;if(!Music.openFromFile(resourcePath()+“nice_Music.ogg”){return EXIT_FAILURE;} //演奏音乐 音乐。播放(); */ 太空船太空船(窗口); sf::时钟sf_时钟; //开始游戏循环 while(window.isOpen()){ //处理事件 sf::事件; while(window.pollEvent(事件)){ //关闭窗口:退出 如果(event.type==sf::event::Closed){ window.close(); } //按下退出按钮:退出 if(event.type==sf::event::KeyPressed&&event.key.code==sf::Keyboard::Escape){ window.close(); } //移动宇宙飞船 如果(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){spaceship.moveship('l');} 如果(sf::Keyboard::iskeypress(sf::Keyboard::Right)){spaceship.moveship('r');} 如果(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){spaceship.moveship('u');} 如果(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)){spaceship.moveship('d');} } //清屏 window.clear(); //画精灵 窗口。绘制(精灵); 太空船。drawsprite(窗口); //画线 窗口。绘制(文本); //更新窗口 window.display(); } 返回退出成功; } 太空船 #包括 #包括 #包括“ResourcePath.hpp” #包括“Spaceship.hpp” 太空船::太空船(sf::渲染窗口和游戏窗口){ auto surface=game_window.getSize(); ss_x=表面x/2; ss_y=表面y/2; ss_速度=5; ss_宽度=128; ss_高度=128; ss_半径=ss_宽度/2; } 虚空宇宙飞船::drawsprite(sf::渲染窗口和游戏窗口){ sf::纹理船; 如果(!ship.loadFromFile(resourcePath()+“space-shutter-64.png”)){ 返回退出失败; } sf::雪碧ss_雪碧(船); ss_sprite.setPosition(ss_x-ss_sprite.getGlobalBounds().width/2,ss_y-ss_sprite.getGlobalBounds().height/2); 游戏窗口绘制(ss_sprite); } void Spaceship::moveship(字符移动){ 如果(move='l'){ss_x-=ss_speed;} 如果(move='r'){ss_x+=ss_speed;} else if(move='u'){ss_y-=ss_speed;} 如果(move='d'){ss_y+=ss_speed;} } 太空船::~Spaceship(){} 太空船 #ifndef飞船(水电站) #定义太空船 #包括 #包括 #包括 #包括 使用名称空间std; 二级宇宙飞船{ 公众: 宇宙飞船(); 宇宙飞船(sf::RenderWindow&); ~宇宙飞船(); 无效船舶(char); 虚空drawsprite(sf::RenderWindow&); 私人: 署名int ss_x,ss_y; 无符号整数速度; int ss_宽度、ss_高度、ss_半径; }; #endif/*宇宙飞船水电站*/
正如评论中所建议的,这里的问题是,在计算中没有考虑两帧之间经过的时间。因此,所发生的情况是,您在任何帧上添加了固定数量的速度,忽略了两个连续帧可能需要非常不同的完成时间这一事实 还有一个问题(这似乎是问题的主要来源):您正在检查事件循环中是否按下了键。这是不正确的,只有当按下您的键并且在循环过程中发生其他事件时,这才是正确的。你需要在每一帧检查这个 解决最后一个问题的另一种方法是,当检测到要测试的按键按下时,将布尔标志设置为sfml2d游戏&x2013;太空船平稳运行 我试图用XCODE作为编译程序,用SFML作为库,在C++中做一个简单的游戏。到目前为止,我已经为宇宙飞船创建了一个GUI、一个背景和一个精灵。我还添加了箭头键检测,以便能够移动对象,但问题是,当我移动对象时,它移动不平稳,你可以看到它有点“跳跃” main.cpp #包括 #包括 #包括“Spaceship.hpp” #包括 //这里有一个小帮手给你!看一看。 #包括“ResourcePath.hpp” int main(int,字符常量**) { //创建主窗口 sf::渲染窗口(sf::视频模式(800600),“航天飞机”); 设置帧率限制(30); //不带对象参数调用非静态成员函数 //设置图标 sf::图像图标; 如果(!icon.loadFromFile(resourcePath()+“space sputter.png”)){ 返回退出失败; } setIcon(icon.getSize().x,icon.getSize().y,icon.getPixelsPtr()); //加载要显示的精灵 sf::纹理; 如果(!texture.loadFromFile(resourcePath()+“bg.png”)){ 返回退出失败; } 雪碧雪碧(纹理); //创建要显示的图形文本 sf::字体; 如果(!font.loadFromFile(resourcePath()+“sansation.ttf”)){ 返回退出失败; } sf::文本文本(“航天飞机K1LLM33K”,字体,50); text.setFillColor(sf::Color::White); text.setPosition(100.0130.0); //加载要播放的音乐 /*sf::Music Music;if(!Music.openFromFile(resourcePath()+“nice_Music.ogg”){return EXIT_FAILURE;} //演奏音乐 音乐。播放(); */ 太空船太空船(窗口); sf::时钟sf_时钟; //开始游戏循环 while(window.isOpen()){ //处理事件 sf::事件; while(window.pollEvent(事件)){ //关闭窗口:退出 如果(event.type==sf::event::Closed){ window.close(); } //按下退出按钮:退出 if(event.type==sf::event::KeyPressed&&event.key.code==sf::Keyboard::Escape){ window.close(); } //移动宇宙飞船 如果(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){spaceship.moveship('l');} 如果(sf::Keyboard::iskeypress(sf::Keyboard::Right)){spaceship.moveship('r');} 如果(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){spaceship.moveship('u');} 如果(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)){spaceship.moveship('d');} } //清屏 window.clear(); //画精灵 窗口。绘制(精灵); 太空船。drawsprite(窗口); //画线 窗口。绘制(文本); //更新窗口 window.display(); } 返回退出成功; } 太空船 #包括 #包括 #包括“ResourcePath.hpp” #包括“Spaceship.hpp” 太空船::太空船(sf::渲染窗口和游戏窗口){ auto surface=game_window.getSize(); ss_x=表面x/2; ss_y=表面y/2; ss_速度=5; ss_宽度=128; ss_高度=128; ss_半径=ss_宽度/2; } 虚空宇宙飞船::drawsprite(sf::渲染窗口和游戏窗口){ sf::纹理船; 如果(!ship.loadFromFile(resourcePath()+“space-shutter-64.png”)){ 返回退出失败; } sf::雪碧ss_雪碧(船); ss_sprite.setPosition(ss_x-ss_sprite.getGlobalBounds().width/2,ss_y-ss_sprite.getGlobalBounds().height/2); 游戏窗口绘制(ss_sprite); } void Spaceship::moveship(字符移动){ 如果(move='l'){ss_x-=ss_speed;} 如果(move='r'){ss_x+=ss_speed;} else if(move='u'){ss_y-=ss_speed;} 如果(move='d'){ss_y+=ss_speed;} } 太空船::~Spaceship(){} 太空船 #ifndef飞船(水电站) #定义太空船 #包括 #包括 #包括 #包括 使用名称空间std; 二级宇宙飞船{ 公众: 宇宙飞船(); 宇宙飞船(sf::RenderWindow&); ~宇宙飞船(); 无效船舶(char); 虚空drawsprite(sf::RenderWindow&); 私人: 署名int ss_x,ss_y; 无符号整数速度; int ss_宽度、ss_高度、ss_半径; }; #endif/*宇宙飞船水电站*/,c++,user-interface,sfml,C++,User Interface,Sfml,正如评论中所建议的,这里的问题是,在计算中没有考虑两帧之间经过的时间。因此,所发生的情况是,您在任何帧上添加了固定数量的速度,忽略了两个连续帧可能需要非常不同的完成时间这一事实 还有一个问题(这似乎是问题的主要来源):您正在检查事件循环中是否按下了键。这是不正确的,只有当按下您的键并且在循环过程中发生其他事件时,这才是正确的。你需要在每一帧检查这个 解决最后一个问题的另一种方法是,当检测到要测试的按键按下时,将布尔标志设置为true/false。如果您查看一下isKeyPressed方法,您还会
true
/false
。如果您查看一下isKeyPressed
方法,您还会发现第二种方法比第一种方法更有效
更改主循环以获取每帧经过的时间的最简单方法如下:
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
(我在切换案例中更改了if/else if
,我发现这种情况下更干净,应该更有效):
正如Jesper Juhl所建议的,我在这里做了一些(你真的应该看看)
顺便说一句,我建议您对代码进行一些修改:
// 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);
}
尝试使用VSync而不是每秒30帧。我可以推荐阅读-这与你的问题相关,也是一本好的阅读。另请参见。@LogicStuff VSync用于修复纹理撕裂问题,而不是用于固定帧速率或解决问题
#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 */
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..
}
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;
}
}
// 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);
}