Способ сделать очередь событий клавиатуры одновременно отзывчивой и не потреблять всю мощность процессора
Я делаю игру Sdl, это 2d шутер. Я использую SDL для импорта поверхностей и OpenGL для их рисования на экране (потому что он работает намного быстрее, чем просто SDL). У меня запущены два потока: один для обработки и рендеринга, а другой для ввода. По сути, один процессор занимает 1-2% моего ЦП, а цикл ввода занимает 25% (на четырехъядерном, то есть 1 полное ядро). Я пытался сделать SDL_Delay (1) перед каждымwhile (SDL_PollEvent(&keyevent))
и это работает! Уменьшает нагрузку на процессор до 3% для всего процесса. Тем не менее, есть неприятный побочный эффект. Весь ввод программы затруднен: он не обнаруживает нажатие всех клавиш, и, например, чтобы заставить персонажа двигаться, иногда требуется до 3 секунд нажатия клавиатуры, чтобы он реагировал.
Я также пытался решить это с помощьюSDL_PeepEvent()
а такжеSDL_WaitEvent()
однако это вызывает такую же (очень долгую!) задержку.
Код цикла событий:
void GameWorld::Movement()
{
SDL_Event keyevent;
bool x1, x2, y1, y2, z1, z2, z3, m; // Booleans to determine the
x1 = x2 = y1 = y2 = z1 = z2 = z3 = m = 0; // movement direction
SDL_EnableKeyRepeat(0, 0);
while (1)
{
while (SDL_PollEvent(&keyevent))
{
switch(keyevent.type)
{
case SDL_KEYDOWN:
switch(keyevent.key.keysym.sym)
{
case SDLK_LEFT:
x1 = 1;
x2 = 0;
break;
case SDLK_RIGHT:
x1 = 0;
x2 = 1;
break;
case SDLK_UP:
y1 = 1;
y2 = 0;
break;
case SDLK_DOWN:
y1 = 0;
y2 = 1;
break;
default:
break;
}
break;
case SDL_KEYUP:
switch(keyevent.key.keysym.sym)
{
case SDLK_LEFT:
x1 = x2 = 0;
break;
case SDLK_RIGHT:
x1 = x2 = 0;
break;
case SDLK_UP:
y1 = y2 = 0;
break;
case SDLK_DOWN:
y1 = y2 = 0;
break;
default:
break;
}
break;
case SDL_QUIT:
PrintToFile("The game was closed manually.\n");
CleanUp();
return;
break;
default:
break;
}
}
m = x1 || x2 || y1 || y2;
if (m) // if any button is pushed down, calculate the movement
{ // direction and assign it to the player
z1 = (x1 || x2) && (y1 || y2);
z2 = !x1 && (x2 || y2);
z3 = (!y1 && x1) || (!y2 && x2);
MainSurvivor->SetMovementDirection(4 * z1 + 2 * z2 + z3);
}
else // if no button is pushed down, reset the direction
MainSurvivor->SetMovementDirection(-1);
}
}
Код для расчета / рендеринга цикла:
void GameWorld::GenerateCycles()
{
int Iterator = 0;
time_t start;
SDL_Event event;
Render();
_beginthread(MovementThread, 0, this);
while (1)
{
// I know I check this in input loop, but if I comment
SDL_PollEvent(&event); // out it from here, that loop cannot
if (event.type == SDL_QUIT) // see any of the events (???)!
{
PrintToFile("The game was closed manually.\n");
CleanUp();
} // It never closes through here though
start = clock();
Iterator++;
if (Iterator >= 232792560)
Iterator %= 232792560;
MainSurvivor->MyTurn(Iterator);
for (unsigned int i = 0; i < Survivors.size(); i++)
{
Survivors[i]->MyTurn(Iterator);
if (Survivors[i]->GetDiedAt() != 0 && Survivors[i]->GetDiedAt() + 25 < clock())
{
delete Survivors[i];
Survivors.erase(Survivors.begin() + 5);
}
}
if (Survivors.size() == 0)
SpawnSurvivors();
for (int i = 0; i < int(Zombies.size()); i++)
{
Zombies[i]->MyTurn(Iterator);
if (Zombies[i]->GetType() == 3 && Zombies[i]->GetDiedAt() + 25 < Iterator)
{
delete Zombies[i];
Zombies.erase(Zombies.begin() + i);
i--;
}
}
if (Zombies.size() < 3)
SpawnZombies();
// No need to render every cycle, gameplay is slow
if (Iterator % 2 == 0)
Render();
if (Interval - clock() + start > 0)
SDL_Delay(Interval - clock() + int(start));
}
}
У кого-нибудь есть какие-либо идеи?