我正在制作一款安卓游戏,目前性能还不如我所期望的。我有一个独立线程中的游戏循环更新物体的位置。渲染线程会遍历这些对象并将它们绘制出来。当前的行为看起来是不流畅和不均匀的移动。我无法解释的是,在将更新逻辑放入独立线程之前,我将它放在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毫秒,并将额外的时间存储在余数中。如果后续的更新可以处理它,余数将分配给接下来的几个时间差。这种想法可以防止所有突然的长跳,基本上平滑了多个帧的时间峰值。这样做给了我很好的结果。
这种方法的缺点是,在一小段时间内,物体的位置不会与时间准确匹配,实际上会加速以弥补差异。但是这样会更平滑,变化的速度并不会非常明显。
最后,我决定按照上述两个想法重新编写我的引擎,而不是修补我最初做的引擎。我对线程同步进行了一些优化,也许有人可以评论一下。
我的当前线程交互方式如下:
-更新线程更新当前缓冲区(双缓冲系统以便同时更新和绘制),然后将此缓冲区提供给渲染器,如果已经绘制了前一帧。 -如果前一帧尚未绘制或正在绘制,则更新线程将等待渲染线程通知它已经绘制。 -渲染线程等待更新线程通知已发生更新。 -当渲染线程进行绘制时,它设置一个“last drawn variable”,指示其最后绘制了哪个缓冲区,并在它正在等待先前的缓冲区被绘制时通知更新线程。
这可能有点复杂,但这样做既充分利用了多线程的优势,因为它可以在绘制n-1帧时执行n帧的更新,同时还可以防止渲染器花费很长时间时出现多次更新迭代的情况。更进一步地说,这种多次更新的情况由更新线程锁定处理,如果它检测到lastDrawn缓冲区等于刚刚更新的缓冲区,则锁定。如果它们相等,这表明更新线程前一帧还没有被绘制。
到目前为止,我得到了不错的结果。如果有人对我所做的任何事情有任何评论,我很乐意听取您的想法,无论是正确的还是错误的。
谢谢