我正在制作一款安卓游戏,目前没有得到我想要的性能。我在自己的线程中有一个游戏循环,可以更新对象的位置。渲染线程将遍历这些对象并绘制它们。当前的行为似乎是波动/不均匀的运动。我无法解释的是,在我将更新逻辑放在它自己的线程中之前,我将它放在 onDrawFrame 方法中,就在 gl 调用之前。在这种情况下,动画非常流畅,只有当我尝试通过 Thread.sleep 限制更新循环时,它才会变得不稳定/不均匀。即使我让更新线程发疯(不休眠),动画也很流畅,只有当涉及到 Thread.sleep 时才会影响动画的质量。
我创建了一个框架项目,看看是否可以重新创建问题,下面是渲染器中的更新循环和 onDrawFrame 方法: 更新循环
@Override
public void run()
{
while(gameOn)
{
long currentRun = SystemClock.uptimeMillis();
if(lastRun == 0)
{
lastRun = currentRun - 16;
}
long delta = currentRun - lastRun;
lastRun = currentRun;
posY += moveY*delta/20.0;
GlobalObjects.ypos = posY;
long rightNow = SystemClock.uptimeMillis();
if(rightNow - currentRun < 16)
{
try {
Thread.sleep(16 - (rightNow - currentRun));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这是我的onDrawFrame方法:
@Override
public void onDrawFrame(GL10 gl) {
gl.glClearColor(1f, 1f, 0, 0);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTranslatef(transX, GlobalObjects.ypos, transZ);
//gl.glRotatef(45, 0, 0, 1);
//gl.glColor4f(0, 1, 0, 0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, uvBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, drawOrder.length,
GL10.GL_UNSIGNED_SHORT, indiceBuffer);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
我查看了副本岛的源代码,他在单独的线程中执行更新逻辑,并使用 Thread.sleep 对其进行限制,但他的游戏看起来非常流畅。有没有人有任何想法或有没有人经历过我所描述的?
--- 编辑:1/25/13---
我有一些时间思考并大大平滑了这个游戏引擎。我如何做到这一点可能是对实际游戏程序员的亵渎或侮辱,因此请随时纠正这些想法。
基本思想是保持更新、绘制...更新、绘制...的模式,同时保持时间增量相对相同(通常不受您的控制)。我的第一个行动方案是同步我的渲染器,使其仅在被通知允许这样做后才绘制。这看起来像这样:
public void onDrawFrame(GL10 gl10) {
synchronized(drawLock)
{
while(!GlobalGameObjects.getInstance().isUpdateHappened())
{
try
{
Log.d("test1", "draw locking");
drawLock.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
当我完成更新逻辑时,我调用 drawLock.notify(),释放渲染线程来绘制我刚刚更新的内容。这样做的目的是帮助建立更新、绘制...更新、绘制...等模式。
一旦我实现了这一点,它就会变得相当流畅,尽管我仍然偶尔会遇到运动跳跃。经过一些测试,我发现在 ondrawFrame 调用之间发生了多次更新。这导致一帧显示两次(或更多)更新的结果,比正常情况跳跃更大。
我为解决这个问题所做的是将两个 onDrawFrame 调用之间的时间增量限制为某个值,例如 18 毫秒,并将额外的时间存储在剩余时间中。如果他们可以处理,这个剩余部分将在接下来的几次更新中分配给后续的时间增量。这个想法防止了所有突然的长跳,基本上平滑了多帧的时间尖峰。这样做给了我很好的结果。
这种方法的缺点是,在一段时间内,物体的位置不会随着时间的推移而准确,实际上会加速以弥补这种差异。但它更平滑,速度变化不是很明显。
最后,我决定根据以上两个想法重写我的引擎,而不是修补我最初制作的引擎。我对线程同步进行了一些优化,也许有人可以评论。
我当前的线程交互如下:
-更新线程更新当前缓冲区(双缓冲区系统,以便同时更新和绘制),然后如果前一帧已绘制,则将此缓冲区提供给渲染器。
- 如果前一帧还没有绘制,或者正在绘制,更新线程将等待,直到渲染线程通知它已经绘制。
-渲染线程一直等待,直到更新线程通知已发生更新。
- 当渲染线程绘制时,它会设置一个“最后绘制的变量”,指示它最后绘制的两个缓冲区中的哪一个,并且如果它正在等待前一个缓冲区被绘制,也会通知更新线程。
这可能有点令人费解,但这样做是为了发挥多线程的优势,因为它可以在帧 n-1 正在绘制时执行帧 n 的更新,同时还可以防止每帧多次更新迭代,如果渲染器正在使用很久。为了进一步解释,如果检测到 lastDrawn 缓冲区等于刚刚更新的缓冲区,则此多次更新场景由更新线程锁定处理。如果它们相等,这向更新线程表明之前的帧还没有被绘制。
到目前为止,我得到了很好的结果。如果有人有任何意见,请告诉我,很高兴听到您对我所做的任何事情的看法,无论是对还是错。
谢谢