Лучший способ отделить игровую логику от рендеринга для быстро развивающейся игры для Android с OpenGL? - PullRequest
14 голосов
/ 15 декабря 2011

Я некоторое время изучал и делал маленькие игры, и недавно решил, что попытаюсь разработать игры для Android.

Для меня переход с нативного кода C ++ на Android Java не был таким уж сложным, но у меня возникают головные боли, когда я думаю о том, как мне сохранить логику отдельно от рендеринга.

Я читал здесь и на других сайтах, что:

Лучше не создавать для этого другую ветку, просто потому, что У Android наверняка не возникнет проблем с обработкой.

Значение кода будет примерно таким:

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

Но я не уверен, что это будет лучшим подходом. Поскольку я не думаю, что мне нравится, как это будет выглядеть, если я помещу свою логику в метод GLRenderer::onDrawFrame. Насколько я знаю, этот метод предназначен только для рисования, и я могу замедлить кадры, если я добавлю туда логику. Не говоря уже о том, что в моем понимании это вредит понятиям ПОО.

Я думаю, что использование потоков может быть способом, вот как я планировал:

Основная деятельность:

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);
}

GameManager:

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? Заправка моего приложение? Поместите логику в отдельный метод и просто вызовите ее из метод ничьей?

Ответы [ 2 ]

10 голосов
/ 15 декабря 2011

Так что я думаю, что вы можете пойти сюда двумя способами.

  1. Делать все обновления с onDrawFrame(): Это похоже на использование GLUT, и Android будет вызывать эту функцию как можно чаще.(Отключите это с помощью setRenderMode(RENDERMODE_WHEN_DIRTY).) Эта функция вызывается в своем собственном потоке (не в потоке пользовательского интерфейса), и это означает, что вы вызываете обновление своей логики здесь.Ваша первоначальная проблема заключалась в том, что это выглядит немного грязно, и я согласен, но только потому, что функция называется onDrawFrame().Если бы он назывался onUpdateScene(), то обновление логики вполне соответствовало бы этой модели.Но это не самая плохая вещь в мире, она была разработана таким образом, и люди делают это.

  2. Дайте логике свой собственный поток: Это сложнее, так какТеперь вы имеете дело с тремя потоками: потоком пользовательского интерфейса для ввода, потоком рендеринга onDrawFrame() для рисования и логическим потоком для непрерывной работы с входными данными и данными рендеринга.Используйте это, если вам нужна действительно точная модель того, что происходит, даже если ваша частота кадров падает (например, точная реакция на столкновение).Концептуально это может быть немного чище, но практически не чище.

Я бы порекомендовал # 1.Вам действительно не нужен № 2.Если вы это сделаете, вы можете добавить его позже, я думаю, но большинство людей просто используют первый вариант, потому что аппаратное обеспечение достаточно быстрое, поэтому вам не нужно разрабатывать его.

6 голосов
/ 15 декабря 2011

Сделайте ваш дизайн максимально простым :) Стандартный (и, возможно, лучший) подход заключается в следующем:

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

Я бы обратил внимание на некоторые моменты:

  • вам нужно последовательно вызывать методы update и render, избегать вызова update дважды в раз
  • , если вы предпочитаете многопоточность (я этого не делаю), разработайте ваши методы так, чтобы update записывал данные и render только для чтения
  • имейте в виду, что OpenGL имеет свой собственный «поток», поэтому при вызове функции GL он отправляет только некоторую команду в OpenGL (кроме glFlush() - он выполняет все команды)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...