0

在我的游戏中,我有多个线程。一个线程向计算线程发送信号,该线程收到信号后,也渲染游戏。

主线程看起来像这样,我改编自我见过的其他一些游戏循环:

while(isRunning) {
    long now = System.nanoTime();
    float elapsed = (now - mStartTime) / 1000000000f;
    mStartTime = now;


    try {
        Log.d("GameThread", "setElapsed = "+elapsed);
        mController.setElapsedTime(elapsed);

        // send signal to logic barrier to start logic-threads
        BaseThread.LogicBarrier.await(); // 1/4

        // logic done!
        BaseThread.AfterLogicBarrier.await(); // 1/4

        // render!
        Log.d("GameThread", "RENDERING! -> localTime="+localTime+", Options.TIMESTEP="+Options.TIMESTEP+", interpol = "+(Options.TIMESTEP / localTime));



        mController.render((localTime / Options.TIMESTEP));

    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }

    // sleep maybe
    diff = System.nanoTime() - mStartTime;
    mController.fireGameDataChange(GameDataListener.FPS, (int) 1000000000/diff);
    if (1000000000 * Options.TIMESTEP - (diff) > 1000000) {
        try {
            Thread.sleep(0, 999999);
        } catch (InterruptedException ex) {
        }
    }
}

对于每个存在的 ViewObject,呈现函数如下所示:

mMatrix.reset();
mMatrix.setTranslate(view.getX() - view.getVelocityX()  * interpolation), view.getY() + view.getVelocityY)) * interpolation));
mMatrix.preScale((1.0f * view.getWidth() / mBitmap.getWidth()), (1.0f * view.getHeight() / mBitmap.getHeight()));
mMatrix.postRotate(view.getRotation(), view.getX() + view.getWidth()/2f, view.getY()  + view.getHeight()/2f);
mCanvas.drawBitmap(mBitmap, mMatrix, mBasicPaint);

这是逻辑线程中发生的事情:

while(isRunning) {
    try {
        BaseThread.LogicBarrier.await(); // 3/4
        int numSubSteps = 0;
        if (Options.SKIP_TICKS != 0) {
            // fixed timestep with interpolation
            localTime += mController.getElapsedTime();
            Log.e("Environment", "localTime="+localTime+" >= "+Options.TIMESTEP+", from elapsed = "+mController.getElapsedTime());
            if (localTime >= Options.TIMESTEP) {
                //numSubSteps = (int) (localTime/Options.TIMESTEP);
                numSubSteps = (int) (localTime/Options.TIMESTEP);
                localTime -= numSubSteps * Options.TIMESTEP;
            }
        }
        Log.e("EnvironmentThread", "localTime="+localTime+", numSub="+numSubSteps);
        if (numSubSteps != 0) {
            // clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
            int clampedSubSteps = (numSubSteps > Options.SKIP_TICKS) ? Options.SKIP_TICKS: numSubSteps;
            for (int i = 0; i < clampedSubSteps; i++) {
                // todo: update game logic -> in time step so no interpolation
                mController.refresh(1);
            }
        }
        BaseThread.AfterLogicBarrier.await(); // 3/4
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
}

同步线程等的过程工作得很好,如果我将帧限制为 60,我每秒会获得 60 次刷新和尽可能多的渲染。但是有一个问题,我从昨天开始就试图解决这个问题——没有成功。

查看对象正在加速。它们没有加速度,它们只是从速度中获得收益。但是它们存在的时间越长,它们的速度就越快。有谁知道为什么会这样?渲染函数根本不应该干扰视图的位置/速度,因为它只使用 getter 方法来获取位图、当前位置和速度。

所以我的想法是,这是由于插值造成的,我会觉得这有点令人困惑,因为它不是反复出现的正弦式速度增加,而是在每个视图对象的生命周期内增加。

任何帮助,任何想法,将不胜感激。

这是逻辑线程和主线程的一些 LogCat 输出,显示了插值以及一切完成的时间:

08-29 17:09:57.603  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.017999122 >= 0.033333335, from elapsed = 0.017043
08-29 17:09:57.603  26183-26207/com.example.pckg E/EnvironmentThread: numSubSteps = 0.0
08-29 17:09:57.603  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.017999122, numSub=0
08-29 17:09:57.603  26183-26204/com.example.pckg D/GameThread: RENDERING! -> localTime=0.017999122, Options.TIMESTEP=0.033333335, interpol = 1.8519423
08-29 17:09:57.623  26183-26204/com.example.pckg D/GameThread: setElapsed = 0.017807
08-29 17:09:57.623  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.03580612 >= 0.033333335, from elapsed = 0.017807
08-29 17:09:57.623  26183-26207/com.example.pckg E/EnvironmentThread: numSubSteps = 0.0
08-29 17:09:57.623  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.0024727844, numSub=1
08-29 17:09:57.623  26183-26204/com.example.pckg D/GameThread: RENDERING! -> localTime=0.0024727844, Options.TIMESTEP=0.033333335, interpol = 13.480082
08-29 17:09:57.633  26183-26204/com.example.pckg D/GameThread: setElapsed = 0.013305
08-29 17:09:57.633  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.015777785 >= 0.033333335, from elapsed = 0.013305
08-29 17:09:57.633  26183-26207/com.example.pckg E/EnvironmentThread: numSubSteps = 0.0
08-29 17:09:57.633  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.015777785, numSub=0
08-29 17:09:57.633  26183-26204/com.example.pckg D/GameThread: RENDERING! -> localTime=0.015777785, Options.TIMESTEP=0.033333335, interpol = 2.1126752
08-29 17:09:57.653  26183-26204/com.example.pckg D/GameThread: setElapsed = 0.018212
08-29 17:09:57.653  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.033989787 >= 0.033333335, from elapsed = 0.018212
08-29 17:09:57.653  26183-26207/com.example.pckg E/EnvironmentThread: numSubSteps = 0.0
08-29 17:09:57.653  26183-26207/com.example.pckg E/EnvironmentThread: localTime=6.5645203E-4, numSub=1
08-29 17:09:57.653  26183-26204/com.example.pckg D/GameThread: RENDERING! -> localTime=6.5645203E-4, Options.TIMESTEP=0.033333335, interpol = 50.778023
08-29 17:09:57.673  26183-26204/com.example.pckg D/GameThread: setElapsed = 0.01754
08-29 17:09:57.673  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.018196452 >= 0.033333335, from elapsed = 0.01754
08-29 17:09:57.673  26183-26207/com.example.pckg E/EnvironmentThread: numSubSteps = 0.0
08-29 17:09:57.673  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.018196452, numSub=0
08-29 17:09:57.673  26183-26204/com.example.pckg D/GameThread: RENDERING! -> localTime=0.018196452, Options.TIMESTEP=0.033333335, interpol = 1.831859
08-29 17:09:57.683  26183-26204/com.example.pckg D/GameThread: setElapsed = 0.014516
08-29 17:09:57.683  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.032712452 >= 0.033333335, from elapsed = 0.014516
08-29 17:09:57.683  26183-26207/com.example.pckg E/EnvironmentThread: numSubSteps = 0.0
08-29 17:09:57.683  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.032712452, numSub=0
08-29 17:09:57.683  26183-26204/com.example.pckg D/GameThread: RENDERING! -> localTime=0.032712452, Options.TIMESTEP=0.033333335, interpol = 1.01898
08-29 17:09:57.703  26183-26204/com.example.pckg D/GameThread: setElapsed = 0.017108
08-29 17:09:57.703  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.049820453 >= 0.033333335, from elapsed = 0.017108
08-29 17:09:57.703  26183-26207/com.example.pckg E/EnvironmentThread: numSubSteps = 0.0
08-29 17:09:57.703  26183-26207/com.example.pckg E/EnvironmentThread: localTime=0.016487118, numSub=1

不幸的是,我的大脑现在落后了,我现在不明白我做错了什么。

编辑:

谢谢你推动我的大脑。添加更多调试语句后发现错误。没有数学错误,没有线程错误同步,而是在逻辑部分(refresh() 方法)中缺少一个简单的“List.clear()”,这导致列表永远不会被清除,从而导致相同的对象被移动每个计算框架更频繁 - 我什至没有在这里发布,因为我没想到错误会在那里。

还是谢谢你。

4

3 回答 3

1

我建议你看看LWJGL。Minecraft 基于此,它可以为您提供一些主题。

通常的“游戏循环”本身涉及一系列步骤,主要包括:

  1. 初始化:第一次设置。
  2. Load : 加载要使用的游戏内容
  3. 更新:我认为这就是你所说的游戏逻辑。在这里,元素被更新(运动、碰撞等)。
  4. 绘制:(渲染)
  5. 如果你还没有完成 GoTo 3
  6. 卸载:通过分配不同的资源(例如纹理、精灵表、音效或背景音乐)释放您消耗的内存。

正如您现在所做的那样,您有两个线程,一个要渲染,另一个要更新。如果更新线程发疯了,那么您的游戏将渲染无意义的东西,例如加速精灵。

以一些现有框架为例(例如,即将被弃用的 XNA Game Studio,其精神继承者 MonoGame 或 PSM),它们在宏观层面处理游戏循环,具有组件(Level例如) 执行包含在循环中的完全相同的步骤。

话虽如此,循环本身在每个元素(特别是UpdateRender )上“执行” ,将 Game Loop 注入到Level对象上。

于 2013-08-29T15:31:33.853 回答
1

写的游戏循环比你能动摇的还要多,我认为你做错了。

你需要两个循环。一个循环是你的game loop. 这将游戏状态向前一步迭代。事物移动,事物死亡,事物碰撞,事物改变方向等。

第二个循环是你的render loop. 这只是获取游戏状态并吐出它的图像。

如果关闭渲染循环,屏幕会变黑,但游戏仍在游戏循环中进行。

如果您关闭游戏循环,则屏幕不会改变,直到您重新打开游戏循环,因为它会继续呈现相同(未更改)的状态。

现在,看起来您实际上并没有游戏循环。您有一个渲染循环,它尝试一次一点点地模拟游戏循环,而 Admiral Ackbar 对此有一两件事(实际上只有一件)要说。

因此,制作一个完全独立于渲染循环的适当游戏循环,这个问题(以及其他十几个问题)就会消失。一般来说,添加新功能也会容易得多。

于 2013-08-29T15:26:22.673 回答
0

也许这就是即时编译器???它通过将方法编译为本机代码来使方法更快。但这不会发生在多次调用同一方法之前。

-> 关闭 JIT 编译器并检查行为。使用 Oracle 的 Java SE 这可以通过添加参数来完成 -Djava.compiler=NONE

看看http://artiomg.blogspot.de/2011/10/just-in-time-compiler-jit-in-hotspot.html

于 2013-08-29T15:25:40.143 回答