Имитация времени в игровом цикле с использованием C ++ - PullRequest
4 голосов
/ 01 декабря 2009

Я создаю 3d-игру с нуля на C ++, используя OpenGL и SDL для Linux в качестве хобби, и чтобы узнать больше об этой области программирования.

Хотите знать, как лучше всего симулировать время во время игры? Очевидно, у меня есть цикл, который выглядит примерно так:

void main_loop()
{
    while(!quit)
    {
         handle_events();
         DrawScene();
         ...
         SDL_Delay(time_left());
    }
}

Я использую SDL_Delay и time_left () для поддержания частоты кадров около 33 кадров в секунду.

Я думал, что мне просто нужно несколько глобальных переменных, таких как

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 для записи времени на консоль в данный момент), но мне интересно, если это лучший способ сделать это. Есть ли более простые или эффективные способы?

Ответы [ 4 ]

13 голосов
/ 01 декабря 2009

Я упоминал об этом раньше в других темах, связанных с игрой. Как всегда, следуйте советам Гленна Фидлера в его серии Game Physics

То, что вы хотите сделать, это использовать постоянный временной шаг , который вы получаете, накапливая дельты времени. Если вы хотите 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 голос
/ 01 декабря 2009

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

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

0 голосов
/ 06 декабря 2009

Одно из сообщений Гленна, которое вы действительно хотите прочитать, - Исправьте ваш временной шаг! . Посмотрев эту ссылку, я заметил, что Мадс направил вас к тому же общему месту в его ответе .

0 голосов
/ 01 декабря 2009

Я не разработчик Linux, но вы, возможно, захотите взглянуть на использование таймеров вместо опроса тиков.

http://linux.die.net/man/2/timer_create

EDIT:
Кажется, SDL поддерживает таймеры: SDL_SetTimer

...