恒定FPS Android OpenGLES

时间:2012-07-08 07:47:15

标签: android opengl-es constants frame frame-rate

Hello android开发者,

我正在使用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时,我还能做些什么来使游戏更加流畅。

感谢您的回答......

2 个答案:

答案 0 :(得分:6)

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

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

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

sprite.x += (sprite.xspeed * elapsed);

答案 1 :(得分: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. 使用vertexbuffer对象
  4. 在这里初始化onsurfacechanged和获取位置(glget__location)
  5. 这不是你的错,因为Froyo垃圾收集器很糟糕,然后在Gingerbread上,触摸驱动程序会触发太多事件(谷歌这个)所以逐渐切换到其他输入方法,为你的应用程序/游戏不使用触摸像Android传感器
  6. 升级至Gingerbread