最近我一直在研究一些SDL包裝以供我自己使用,而且自從我首次開始使用SDL庫以來,我遇到了一直存在的問題。C++ SDL幀速率脈衝
看到,像許多其他人一樣,我一直在使用一個類似於這個http://lazyfoo.net/SDL_tutorials/lesson14/index.php的計時器來調節我的幀率,並且運動永遠不會平滑。它似乎不是一個雙緩衝或vsync問題,而是運動平穩但週期性地跳過和斷斷續續(實際上它有一定的脈衝和節奏)。無論何時只要調整幀率,脈衝消失 - 當然,一切都變得無法使用),所以我確信它必須處理它。
我已經把一個只包含計時器和一個紅色正方形的小應用程序組合在一起。我將把代碼放在這篇文章的末尾。代碼是垃圾,不會使用任何類型的精靈或任何東西:只要知道它無論施加多少壓力(比如說,添加更多移動的東西或僅更新屏幕的一部分)就可以結束。我嘗試過不同的幀速率設置,並且脈衝總是出現(只是具有不同的「速度」)。不用說我已經在幾臺機器(linux機器上,都是這樣)上試過了。
無論如何,我一直在閱讀和看到其他人有這個問題,但沒有真正的答案。其中之一就是關於三角洲計時技術,並樂意嘗試,但我的問題在於計時器本身就是這個非常簡單的應用程序。它爲什麼口吃? SDL_GetTicks的精度和準確性有問題嗎?我能做些什麼來改善它?
所以,這裏是爲那些誰想要嘗試它的代碼:
#include <SDL/SDL.h>
class fps_control
{
private:
bool apply;
Uint32 ticks_frame_count;
Uint32 ticks_frame_end;
Uint32 ticks_frame_begin;
Uint32 diff;
unsigned int frame_count; //Visible to the outside
unsigned int frame_count_inner; //Keeps count until a second has passed, then overwrites former to give the elapsed frames in a second.
unsigned int frame_length;
unsigned int desired_framerate;
private:
void calculate_frame_length()
{
this->frame_length=1000/this->desired_framerate;
}
public:
fps_control(unsigned int p_f=30):desired_framerate(p_f), apply(true), ticks_frame_count(0), ticks_frame_end(0), ticks_frame_begin(0), diff(0), frame_count(0), frame_count_inner(0), frame_length(0)
{
this->calculate_frame_length();
}
unsigned int get_frame_count() const {return this->frame_count;}
unsigned int get_desired_framerate() const {return this->desired_framerate;}
void framerate_increase(){this->set_framerate(this->desired_framerate+1);}
void framerate_decrease(){this->set_framerate(this->desired_framerate+1);}
void set_framerate(unsigned int p_param)
{
this->desired_framerate=p_param;
this->calculate_frame_length();
}
void toggle_apply() {this->apply=!this->apply;}
void init()
{
//Call this once before starting, to set the beginning frame count to the initial values.
this->ticks_frame_count=SDL_GetTicks();
this->ticks_frame_begin=this->ticks_frame_count;
this->frame_count_inner=0;
this->frame_count=0;
}
void turn()
{
//Call this when all drawing and logic is done.
if(!this->apply) return; //Only apply when asked for.
//Ask for time before drawing and logic to calculate the difference. Add a frame.
this->ticks_frame_end=SDL_GetTicks();
this->frame_count_inner++;
//Whenever a second has passed, update the visible frame count.
if((this->ticks_frame_end - this->ticks_frame_count) > 1000)
{
this->frame_count=this->frame_count_inner;
this->frame_count_inner=0;
this->ticks_frame_count=SDL_GetTicks();
}
//Calculate difference and apply delay when needed.
this->diff=this->ticks_frame_end - this->ticks_frame_begin;
if(this->diff < this->frame_length) SDL_Delay(this->frame_length-this->diff);
//Get the beginning time and start again.
this->ticks_frame_begin=SDL_GetTicks();
}
};
class Box
{
private:
SDL_Rect position;
int movement_x;
int movement_y;
public:
Box():movement_x(4), movement_y(4)
{
this->position.x=100;
this->position.y=100;
this->position.w=30;
this->position.h=30;
}
SDL_Rect get_position() {return this->position;}
void move_around()
{
//Won't touch the edges, but doesn't really matter.
if(this->position.x<=0 || this->position.x>=800-this->position.w) this->movement_x=-this->movement_x;
if(this->position.y<=0 || this->position.y>=600-this->position.h) this->movement_y=-this->movement_y;
this->position.x+=this->movement_x;
this->position.y+=this->movement_y;
}
};
bool init_sdl(){return SDL_Init(SDL_INIT_VIDEO) >= 0;}
void quit_sdl(){SDL_Quit();}
void fill_screen(SDL_Surface * screen)
{
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,0,0,0));
}
void update_screen(SDL_Surface * screen, Box& box)
{
SDL_Rect b=box.get_position();
SDL_FillRect(screen, &b, SDL_MapRGB(screen->format, 200, 20, 20));
SDL_Flip(screen);
}
int get_input()
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
if(event.type==SDL_QUIT) return 1;
else if(event.type==SDL_KEYDOWN)
{
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE: return 1; break;
case SDLK_SPACE: return 2; break;
case SDLK_UP: return 3; break;
case SDLK_DOWN: return 4; break;
default: break;
}
}
}
return 0;
}
int main(int argc, char **argv)
{
if(!init_sdl())
{
return 1;
}
else
{
//Init things...
// SDL_Surface * screen=SDL_SetVideoMode(800, 600, 16, SDL_DOUBLEBUF | SDL_HWSURFACE); /*SDL_SWSURFACE | SDL_ANYFORMAT);*/
SDL_Surface * screen=SDL_SetVideoMode(800, 600, 16, SDL_SWSURFACE | SDL_ANYFORMAT);
Box box=Box();
fps_control fps=fps_control(60); //Framerate is set to 60.
bool run=true;
int input=0;
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,255,0,0));
//Main loop
fps.init();
while(run)
{
input=get_input();
switch(input)
{
case 1: run=false; break;
case 2: fps.toggle_apply(); break;
case 3: fps.framerate_increase(); break;
case 4: fps.framerate_decrease(); break;
default: break;
}
box.move_around();
fill_screen(screen);
update_screen(screen, box);
fps.turn();
}
quit_sdl();
}
}
它是自包含的(並再次,純垃圾),所以正好與SDL鏈接,並嘗試...你在這裏看到任何口吃脈衝?
我會嘗試應用增量計時以查看問題是否消失。我只是想知道爲什麼會發生這種簡單的應用程序。非常感謝。
編輯:最後一件事,我知道我可能不應該去SDL_Delay(關於系統睡至少問值),但我真的很困惑這種看似強大的機器上的行爲。
編輯:修正了一下「set_framerate」,感謝Yno對它的評論。
編輯2:因爲我改變了所有的代碼,使用增量時間值而不是設置幀率,所有的東西都變得更好了。我仍然得到一些週期性的減速(這次每次四十秒),但我可以責怪那些在系統上,因爲根據我使用的Linux桌面(比如Gnome,Unity,Gnome2d ...),幀率和減速變化很大。
感謝您的回答......其實,幀率降低爲錯誤的,因爲它得到(有些錯字我有沒有),但calculate_frame_length()在構造函數中,對嗎?該代碼實際上是西班牙文原件的翻譯,因此可能會有一些不一致之處。至於其他方面,我會說這是全部,只有其他形式,對吧? [繼續,打破「輸入鍵」] –
即使有些東西可能是越野車或奇怪的(比如第一個t(ticks_frame_begin是在最後計算的),我不認爲我應該期待這個脈衝.. 。除非SDL_Delay有什麼錯誤,當然,一切都會有幫助,非常感謝:)。 –
'calculate_frame_length()'應當每當所需的幀率發生變化時調用,即在'set_framerate()'中。我將編輯我的帖子。 – Yno