Sun*_*ius 4 c++ windows sdl event-loop
我正在做一个Sdl游戏,它是2D射手.我正在使用SDL导入曲面和OpenGL在屏幕上绘制它们(这样做是因为它比SDL更快地工作).我有两个线程正在运行,一个用于处理东西和渲染,另一个用于输入.基本上,处理一个占我CPU的1-2%,而输入循环需要25%(在四核上,所以它是1个全核).我尝试在每个之前做SDL_Delay(1)while (SDL_PollEvent(&keyevent)),它的工作原理!整个过程将CPU负载降低至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);
}
}
Run Code Online (Sandbox Code Playgroud)
计算/渲染循环代码:
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));
}
}
Run Code Online (Sandbox Code Playgroud)
有没有人有任何想法?
我对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
}
Run Code Online (Sandbox Code Playgroud)
这意味着无论键盘上是否发生任何事实,您都在计算和设置数据.
如果设置数据很昂贵(提示:同步数据),那么它将花费更多.
您必须等待事件发生,而不是您写的旋转导致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 ;
}
Run Code Online (Sandbox Code Playgroud)
注意:这是单线程的.我稍后会谈论线程.
注2:关于两个"m_isRedrawing ..."布尔值的观点是当其中一个布尔值为真时,以及当计时器询问问题时强制重绘.通常,没有重绘.
我的代码和你的代码之间的区别在于,你不会让线程"等待".
我想,在处理键盘事件时会出现问题.
你的代码:
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.
}
Run Code Online (Sandbox Code Playgroud)
假设您按下LEFT,然后按RIGHT,然后按下LEFT.我期待的是:
在您的情况下,您有:
你做错了,因为:
我稍后会找到该链接,但你应该做的是为按键设置一个布尔状态数组.就像是:
// C++ specialized vector<bool> is silly, but...
std::vector<bool> m_aKeyIsPressed ;
Run Code Online (Sandbox Code Playgroud)
您可以使用可用键的大小初始化它:
m_aKeyIsPressed(SDLK_LAST, false)
Run Code Online (Sandbox Code Playgroud)
然后,在关键事件上:
void MyContext::onKeyUp(const SDL_KeyboardEvent & p_oEvent)
{
this->m_aKeyIsPressed[p_oEvent.keysym.sym] = false ;
}
Run Code Online (Sandbox Code Playgroud)
关键时刻:
void MyContext::onKeyDown(const SDL_KeyboardEvent & p_oEvent)
{
this->m_aKeyIsPressed[p_oEvent.keysym.sym] = true ;
}
Run Code Online (Sandbox Code Playgroud)
这样,当您定期检查(以及检查零件的时间很重要)时,您就知道键盘的确切瞬时状态,并且您可以对其做出反应.
线程很酷但是,你必须确切知道你正在处理什么.
例如,事件循环线程调用以下方法:
MainSurvivor->SetMovementDirection
Run Code Online (Sandbox Code Playgroud)
分辨率(渲染)线程调用以下方法:
MainSurvivor->MyTurn(Iterator);
Run Code Online (Sandbox Code Playgroud)
说真的,你在两个不同的线程之间共享数据吗?
如果你(我知道你是),那么你有:
我要做的是将更改从一个线程传递到另一个线程(例如,通过消息传递给同步队列).
无论如何,线程是一个很重要的问题,因此在将它与SDL和OpenGL混合之前,您应该熟悉这个概念.Herb Sutter的博客是关于线程的精彩文章集.
你应该做的是:
您显然正在使用C++(例如void GameWorld::Movement()),因此使用1或0代替true或false不会使您的代码更清晰或更快.
| 归档时间: |
|
| 查看次数: |
2700 次 |
| 最近记录: |