我正在使用Linux上的OpenGL和SDL从头开始构建一个3D游戏作为业余爱好,并了解有关这一编程领域的更多信息。
想知道在游戏运行时模拟时间的最佳方法。显然我有一个看起来像这样的循环:
void main_loop()
{
while(!quit)
{
handle_events();
DrawScene();
...
SDL_Delay(time_left());
}
}
我正在使用SDL_Delay和time_left()来保持大约33 fps的帧速率。
我原以为我只需要一些全局变量,比如
int current_hour = 0;
int current_min = 0;
int num_days = 0;
Uint32 prev_ticks = 0;
然后是一个函数:
void handle_time()
{
Uint32 current_ticks;
Uint32 dticks;
current_ticks = SDL_GetTicks();
dticks = current_ticks - prev_ticks; // get difference since last time
// if difference is greater than 30000 (half minute) increment game mins
if(dticks >= 30000) {
prev_ticks = current_ticks;
current_mins++;
if(current_mins >= 60) {
current_mins = 0;
current_hour++;
}
if(current_hour > 23) {
current_hour = 0;
num_days++;
}
}
}
然后在主循环中调用handle_time()函数。
它编译并运行(目前使用printf将时间写入控制台),但我想知道这是否是最好的方法。是否有更简单的方法或更有效的方式?
答案 0 :(得分:13)
我之前在其他游戏相关主题中已经提到了这一点。一如既往,请遵循Glenn Fiedler在Game Physics series
中提出的建议你想要做的是使用常数时间步长,你可以通过累积时间增量来获得。如果你想每秒33次更新,那么你的恒定时间步长应该是1/33。您也可以将其称为更新频率。您还应该将游戏逻辑与渲染分离,因为它们不属于一起。您希望能够在渲染速度与机器允许的情况下一样快地使用较低的更新频率。以下是一些示例代码:
running = true;
unsigned int t_accum=0,lt=0,ct=0;
while(running){
while(SDL_PollEvent(&event)){
switch(event.type){
...
}
}
ct = SDL_GetTicks();
t_accum += ct - lt;
lt = ct;
while(t_accum >= timestep){
t += timestep; /* this is our actual time, in milliseconds. */
t_accum -= timestep;
for(std::vector<Entity>::iterator en = entities.begin(); en != entities.end(); ++en){
integrate(en, (float)t * 0.001f, timestep);
}
}
/* This should really be in a separate thread, synchronized with a mutex */
std::vector<Entity> tmpEntities(entities.size());
for(int i=0; i<entities.size(); ++i){
float alpha = (float)t_accum / (float)timestep;
tmpEntities[i] = interpolateState(entities[i].lastState, alpha, entities[i].currentState, 1.0f - alpha);
}
Render(tmpEntities);
}
这可以处理欠采样和过采样。如果你使用像这里完成的整数运算,你的游戏物理应该接近100%确定性,无论机器有多慢或多快。这是在固定时间间隔内增加时间的优点。用于渲染的状态是通过在先前状态和当前状态之间进行插值来计算的,其中时间累加器内的剩余值被用作插值因子。无论时间步长多大,这都可确保渲染顺利。
答案 1 :(得分:1)
除了已经指出的问题(您应该使用时间结构并将其传递给handle_time()并且您的分钟将每半分钟递增一次)您的解决方案可以很好地跟踪游戏中的时间。
然而,对于大多数需要经常发生的游戏事件,你应该将它们放在主游戏循环而不是实际时间之外,这样它们就会以不同的fps以相同的比例发生。
答案 2 :(得分:0)
您真正想要阅读的Glenn的帖子之一是Fix Your Timestep!。在查看此链接后,我注意到Mads已将您引导至answer中的同一位置。
答案 3 :(得分:-1)
我不是Linux开发人员,但你可能想看一下使用Timers而不是轮询ticks。
http://linux.die.net/man/2/timer_create
修改:
SDL似乎支持计时器:SDL_SetTimer