Я не очень разбираюсь в SDL или программировании игр, но вот несколько случайных идей:
Реакция на изменения состояния
Ваш код:
while (1)
{
while (SDL_PollEvent(&keyevent))
{
switch(keyevent.type)
{
// code to set keyboard state
}
}
// code to calculate movement according to keyboard state
// then act on that movement
}
Это означает, что независимо от того, что на вашей клавиатуре ничего не происходит, вы вычисляете и настраиваете данные.
Если настройка данных дорогая (подсказка: синхронизированные данные), то это будет стоить вам еще больше.
SDL_WaitEvent: измерение состояния
Вы должны ждать, пока не произойдет событие, а не вращение, которое вы написали, что вызывает 100% использование одного процессора.
Вот вариант событияЦикл, который я написал для теста дома:
while(true)
{
// message processing loop
::SDL_Event event ;
::SDL_WaitEvent(&event) ; // THIS IS WHAT IS MISSING IN YOUR CODE
do
{
switch (event.type)
{
// etc.
}
}
while(::SDL_PollEvent(&event)) ;
// re-draw the internal buffer
if(this->m_isRedrawingRequired || this->m_isRedrawingForcedRequired)
{
// redrawing code
}
this->m_isRedrawingRequired = false ;
this->m_isRedrawingForcedRequired = false ;
}
Примечание: это был однопоточный.Я расскажу о потоках позже.
Примечание 2: Смысл в двух логических значениях "m_isRedrawing ..." состоит в том, чтобы принудительно перерисовывать, когда один из этих логических значений равен true, и когда таймер задает вопрос.Обычно перерисовка отсутствует.
Разница между моим и вашим кодом заключается в том, что вы ни в коем случае не позволяете потоку "ждать".
События клавиатуры
СуществуетЯ полагаю, это проблема с обработкой событий клавиатуры.
Ваш код:
case SDL_KEYDOWN:
switch(keyevent.key.keysym.sym)
{
case SDLK_LEFT:
x1 = 1;
x2 = 0;
break;
case SDLK_RIGHT:
x1 = 0;
x2 = 1;
break;
// etc.
}
case SDL_KEYUP:
switch(keyevent.key.keysym.sym)
{
case SDLK_LEFT:
x1 = x2 = 0;
break;
case SDLK_RIGHT:
x1 = x2 = 0;
break;
// etc.
}
Допустим, вы нажимаете ВЛЕВО, а затем ВПРАВО, а затем нажимаете ВЛЕВО.То, что я ожидал бы:
- нажмите ВЛЕВО: символ уходит влево
- нажмите ВПРАВО: символ останавливается (при нажатии и ВЛЕВО и ВПРАВО)
- unpress LEFT: символ идет вправо, потому что RIGHT по-прежнему нажата
В вашем случае:
- нажмите LEFT: символ идет влево
- нажмите ВПРАВО: символ перемещается вправо (как теперь LEFT игнорируется, с x1 = 0)
- unpress LEFT: символ останавливается (потому что вы сбросили оба x1 и x2.), несмотря на то, что RIGHT все еще нажата
Вы делаете это неправильно, потому что:
- Вы реагируете непосредственно на событие, а не используете таймер, чтобы реагировать на ситуацию каждую n-ю миллисекунду
- вы смешиваете события вместе.
Ссылку я найду позже, но вам нужно иметь массив логических состояний для нажатых клавиш.Что-то вроде:
// C++ specialized vector<bool> is silly, but...
std::vector<bool> m_aKeyIsPressed ;
Вы инициализируете его с размером доступных ключей:
m_aKeyIsPressed(SDLK_LAST, false)
Затем, при событии key up:
void MyContext::onKeyUp(const SDL_KeyboardEvent & p_oEvent)
{
this->m_aKeyIsPressed[p_oEvent.keysym.sym] = false ;
}
и по ключуdown:
void MyContext::onKeyDown(const SDL_KeyboardEvent & p_oEvent)
{
this->m_aKeyIsPressed[p_oEvent.keysym.sym] = true ;
}
Таким образом, когда вы проверяете через равные промежутки времени (и , когда вы проверяете партию , важно знать точное мгновенное состояние клавиатуры, и вы можетереагировать на это.
Потоки
Потоки крутые, но тогда вы должны точно знать, с чем имеете дело.
Например, поток цикла событий вызывает следующий метод:
MainSurvivor->SetMovementDirection
Поток разрешения (рендеринга) вызывает следующий метод:
MainSurvivor->MyTurn(Iterator);
Серьезно, вы разделяете данные между двумя разными потоками?
Если вы(и я знаю, что вы есть), тогда у вас есть:
- , если вы не синхронизировали доступы, проблема согласованности данных из-за кэшей процессора.Проще говоря, нет никакой гарантии, что один набор данных одним потоком будет рассматриваться как «измененный» другим в разумные сроки.
- , если вы синхронизировали доступы (с мьютексом, атомарной переменной и т. Д.)), тогда у вас будет снижение производительности, потому что вы (например) блокируете / разблокируете мьютекс, по крайней мере, один раз на поток за итерацию цикла.
Вместо этого я бы сообщил об изменении из одного потокак другому (например, через сообщение в синхронизированную очередь).
В любом случае многопоточность является серьезной проблемой, поэтому вам следует ознакомиться с этой концепцией, прежде чем смешивать ее с SDL и OpenGL. Блог Херба Саттера - изумительная коллекция статей по темам.
Что вам нужно сделать, это:
- Попробуйте написать вещь в одном потоке, используя события, опубликованные сообщения и таймеры.
- Если вы обнаружите проблемы с производительностью, переместите событие или поток рисунка в другое место, но продолжайте работать с событиями, отправленными сообщениями и таймерами для связи
PS: Что не так с вашими логическими значениями?
Вы, очевидно, используете C ++ (например, void GameWorld::Movement()
), поэтому использование 1 или 0 вместо true
или false
не сделает ваш код более понятным или быстрым.