3

你好安卓开发者,

我正在使用 OpenGLES 1.0 在 Eclipse 中为 Android 开发一个简单的游戏。我正在使用三星 Galaxy S2 Android(2.3) 作为开发设备。

我有一个关于双核和使帧速率恒定的问题。

所以我已经设法创建 GLSurfaceView 并覆盖 onDrawFrame() 函数,我在其中调用 LogicUpdate(deltatime) 函数和 Render() 函数。

是的,现在全部在单线程中。

我遇到的问题是双核。如果我通过转到设置->省电并检查系统省电来禁用双核,我意识到渲染自动锁定在 30 FPS。但是,如果我通过取消选中系统省电来启用双核,我会看到渲染锁定在 60 FPS,但是手机会变热并且会很快耗尽电池电量。

所以这个想法是让我的游戏以 30 FPS 运行以节省一些电池。

所以要做到这一点,我使用下面的代码。

在我进行逻辑更新之前,我称之为代码和平,记住所有这些都是在 onDrawFrame() 中完成的。

if( CONST_FPS > 0 && StartTime > 0 )
{           
    /////////////////////////////////////////////////////////////////
    // Get frame time
    ////////////////////////////////////////////////////////////////
    long endTime  = System.currentTimeMillis(); 
    long time     = endTime - StartTime;
    //              
    long wantedtime = 1000 / CONST_FPS;
    //
    long wait = 0;
    if( time < wantedtime )
    {
        wait = wantedtime - time;
        //                  
        Thread.sleep(wait);
    }
    else
    {
        //Time to big game will slow down
    }                           
}

其中 CONST_FPS = 30

接着

StartTime = System.currentTimeMillis(); //

UpdateLogic(1.0 / CONST_FPS);
Render();

30 FPS 的游戏非常流畅,主要是因为它不需要锁定 FPS。但是,当试图将 60FPS 锁定到 30 FPS 时,我会卡住。我做了一些研究,发现 Thread.Sleep() 并不精确。这是真的?锁定 60FPS 到 30FPS 时,我还能做些什么让游戏更流畅。

感谢你的回答 ...

4

2 回答 2

7

您应该使用经过的时间来缩放所有运动,以使运动在不同的 FPS 速率下保持平滑。您可以像这样获得经过的时间:

long currentTime = System.currentTimeMillis();
float elapsed = (System.currentTimeMillis() - lastFrameTime) * .001f;//convert ms to seconds
lastFrameTime = currentTime; 

然后以每秒单位表示你的速度,并像这样更新位置:

sprite.x += (sprite.xspeed * elapsed);
于 2012-07-11T04:20:25.603 回答
3
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1);
    GLES20.glEnable(GLES20.GL_DEPTH_TEST);
    GLES20.glDepthFunc(GLES20.GL_LEQUAL);
    GLES20Renderer.programLight = GLES20.glCreateProgram();
    int vertexShaderLight       = GLES20Renderer.loadShader(GLES20.GL_VERTEX_SHADER, GLES20Renderer.vertexShaderCodeLight);
    int fragmentShaderLight     = GLES20Renderer.loadShader(GLES20.GL_FRAGMENT_SHADER, GLES20Renderer.fragmentShaderCodeLight);
    GLES20.glAttachShader(GLES20Renderer.programLight, vertexShaderLight);
    GLES20.glAttachShader(GLES20Renderer.programLight, fragmentShaderLight);
    GLES20.glLinkProgram(GLES20Renderer.programLight);
    System.gc();
}

public void onSurfaceChanged(GL10 gl, int width, int height) {
    gl.glViewport(0, 0, width, height);
    float ratio = (float) width / height;
    Matrix.setLookAtM(GLES20Renderer.ViewMatrix, 0, 0, 0, 5f, 0, 0, 0, 0, 1, 0);
    Matrix.frustumM(GLES20Renderer.ProjectionMatrix, 0, -ratio, ratio, -1, 1, 2, 8);
    Matrix.setIdentityM(LightModelMatrix, 0);
    Matrix.translateM(LightModelMatrix, 0, -1.0f, 0.0f, 3f);
    Matrix.multiplyMV(LightPosInWorldSpace, 0, LightModelMatrix, 0, LightPosInModelSpace, 0);
    Matrix.multiplyMV(LightPosInEyeSpace, 0, ViewMatrix, 0, LightPosInWorldSpace, 0);

    System.gc();
}

public void onDrawFrame(GL10 gl) {
    long deltaTime,startTime,endTime;
    startTime   = SystemClock.uptimeMillis() % 1000;
    gl.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    synchronized (this) {
        updateModel(GLES20Renderer._xAngle, GLES20Renderer._yAngle, GLES20Renderer._zAngle);            
    }
    renderModel(gl);
    endTime     = SystemClock.uptimeMillis() % 1000;
    deltaTime   = endTime - startTime;
    if (deltaTime < 30) {
        try {
            Thread.sleep(30 - deltaTime);
        } catch (InterruptedException e) {
            //e.printStackTrace();
        }
    }

    System.gc();
}

注意力:

  1. 永远不要在 UI 线程中使用 sleep;非常非常非常糟糕
  2. 使用静态:虽然这是不好的 OOP 实践,但在这里很有用
  3. 使用顶点缓冲区对象
  4. 在 onsurfacechanged 中初始化并在此处获取位置 (glget__location)
  5. 这不是你的错,因为 Froyo 垃圾收集器很糟糕,然后在 Gingerbread 上,触摸驱动程序触发了太多事件(谷歌这个)所以逐渐切换到你的应用程序/游戏的其他输入法,不使用像 Android 传感器这样的触摸
  6. 升级到姜饼
于 2012-08-03T05:38:11.397 回答