【问题标题】:Best way to separate game logic from rendering for a fast-paced game for Android with OpenGL?使用 OpenGL 将游戏逻辑与渲染分离的最佳方法是用于 Android 的快节奏游戏?
【发布时间】:2026-01-26 22:20:11
【问题描述】:

我研究和制作小游戏已经有一段时间了,最​​近我决定尝试为 Android 开发游戏。

对我来说,从原生 C++ 代码跳转到 Android Java 并不难,但让我头疼的是如何将逻辑与渲染分开。

我一直在这里和其他网站上阅读:

最好不要为它创建另一个线程,因为 Android 肯定不会有处理问题。

意思是代码是这样的:

public void onDrawFrame(GL10 gl) {
    doLogicCalculations();
    clearScreen();
    drawSprites();
}

但我不确定这是否是最好的方法。因为我认为我不喜欢将我的逻辑放在GLRenderer::onDrawFrame 方法中的样子。据我所知,这种方法只是为了绘制,如果我把逻辑放在那里,我可能会减慢帧的速度。更不用说它伤害了我理解的 POO 概念。

我认为使用线程可能是一种方式,这就是我的计划:

主要活动:

public void onCreate(Bundle savedInstanceState) {
    //set fullscreen, etc

    GLSurfaceView view = new GLSurfaceView(this);
    //Configure view

    GameManager game = new GameManager();
    game.start(context, view);

    setContentView(view);
}

游戏管理器:

OpenGLRenderer renderer;
Boolean running;
public void start(Context context, GLSurfaceView view) {
    this.renderer = new OpenGLRenderer(context);
    view.setRenderer(this.renderer);

    //create Texturelib, create sound system...

    running = true;
    //create a thread to run GameManager::update()
}

public void update(){
    while(running){
        //update game logic here
        //put, edit and remove sprites from renderer list
        //set running to false to quit game
    }
}

最后是 OpenGLRenderer:

ListOrMap toDraw;
public void onDrawFrame(GL10 gl) {
    for(sprite i : toDraw)
    {
        i.draw();
    }
}

这是一个粗略的想法,并不完全完整。 这种模式可以将它们全部分开,并且看起来会更好一些,但它对性能来说是最好的吗?

只要我研究过,大多数线程游戏的例子都使用画布或表面视图,这些都不适合我的情况,因为我使用的是 OpenGLES。

所以这是我的问题:

什么是分开我的最好的方法 使用OpenGLES时渲染的游戏逻辑?穿线我的 应用?将逻辑放在一个单独的方法中,然后从 绘制方法?

【问题讨论】:

    标签: android opengl-es


    【解决方案1】:

    所以我认为你可以通过两种方式来这里。

    1. onDrawFrame() 进行所有更新: 这类似于使用 GLUT,Android 会尽可能频繁地调用此函数。 (用setRenderMode(RENDERMODE_WHEN_DIRTY) 关闭它。)这个函数在它自己的线程(不是UI 线程)上被调用,这意味着你在这里调用你的逻辑更新。您最初的问题是这似乎有点混乱,我同意,但这只是因为该函数被命名为onDrawFrame()。如果它被称为onUpdateScene(),那么更新逻辑将非常适合这个模型。但这并不是世界上最糟糕的事情,它是这样设计的,人们也这样做。

    2. 给逻辑自己的线程:这更复杂,因为现在您要处理三个线程:用于输入的 UI 线程,用于绘制内容的渲染线程 onDrawFrame(),以及用于连续处理输入和渲染数据的逻辑线程。如果您需要对正在发生的事情有一个非常准确的模型,即使您的帧速率正在下降(例如,精确的碰撞响应),也可以使用它。这在概念上可能更干净一些,但实际上并不干净。

    我会推荐#1。你真的不需要#2。如果你这样做了,我猜你可以稍后添加它,但大多数人只是使用第一个选项,因为硬件足够快,所以你不必围绕它进行设计。

    【讨论】:

    • 史蒂夫的回答很好,谢谢!我认为即使我不喜欢搞砸设计,我也会遵循#1。我的游戏处于活动状态,但没有进行繁重的计算,例如物理或大量碰撞检查。只是为了确定我的想法,如果我选择它,我在问题中的线程方法将是正确的(逻辑线程写入同步映射并渲染线程读取它)?
    • 哦,还有一件事。人们说我将我的逻辑更新限制为 30fps,渲染也是这样吗?我是否应该将整个函数限制为每次更新等待 33 毫秒,或者如果自上次更新以来已经过了 33 毫秒,则只更新逻辑?
    • 我认为您不必限制它,但也许不要期望在某些手机上会比这更好。这是因为一些第一代 HDPI 手机(我认为大约在 Droid 1 时代)的帧速率是有限的,这意味着它们无法每秒绘制超过 30 次以上的像素。也许 GPU 可以更快地生成帧,但 CPU 不能足够快地将它们移动到屏幕上。我认为在较新的手机上情况并非如此。
    • 不要将 33ms 传递给逻辑代码。如果您需要更多时间来处理逻辑,您可以将自己锁定在 30fps,但这可能还不是问题。
    • 啊,非常感谢您的回答!顺便提一下,我有一个 Droid 1,它将作为我的测试硬件(:
    【解决方案2】:

    让您的设计尽可能简单 :) 标准(也许是最好的)方法如下:

    public void update(){
        while(running){
            updateAll();
            renderAll();
        }
    }
    

    我会注意一些时刻:

    • 你需要依次调用updaterender方法,避免一次调用update两次
    • 如果您更喜欢多线程(我不喜欢),请将您的方法设计为 update 写入数据而 render 只读
    • 请记住,OpenGL 有自己的“线程”,因此当您调用 GL 函数时,它只会向 OpenGL 发送一些命令(glFlush() 除外 - 它会完成所有命令)

    【讨论】:

    • 嗯,正如您最后一个主题所暗示的那样,来自 OpenGLrenderer 的 onDrawFrame 在另一个线程上被调用?还是只是作为请求发送到与 GL 相关的其他线程的 gl.GLxxx()?
    • @Gtoknu 当你接到onDrawFrame() 的电话时,你在另一个线程上。见这里:developer.android.com/reference/android/opengl/…
    • @SteveBlackwell 哦,谢谢,我还没有意识到这一点。但是,如果我不能调用 onDrawFrame() 因为它是由另一个线程自动调用的,那么 brigadir 的回答怎么可能是真的?这些令人困惑。
    • @Gtoknu 你是对的。我不太确定准将的回答如何起作用。你不能打电话给renderAll(),因为onDrawFrame() 是替你打电话的。除非您开始使用CanvasSurfaceView,否则我看不到解决方法。看我的回答。
    • 请原谅我对代码片段的混淆。这个想法是总是在渲染之前处理更新。如果我们在单独的线程中更新我们的场景,有时可能会连续调用两次更新——我们看不到第一次调用的结果,因为没有调用渲染。 Steve Blackwell 在案例 1 中回答了这个问题。