Гладкий игровой цикл Android

У меня проблемы с плавной прокруткой в ​​OpenGL (тестирование на SGS2 и ACE)
Я создал простые приложения – только фиксированную скорость горизонтальной прокрутки изображений или только одно изображение (плеер), движущееся на ускорителе, но это движение не является гладким 🙁
Я пробовал много различных кодов, но без удовлетворения …

Сначала я пробовал работать с GLSurfaceView.RENDERMODE_CONTINUOUSLY, и я поместил весь код в onDrawFrame:

public void onDrawFrame(GL10 gl) { updateGame(gl, 0.017f); drawGame(gl); } 

Это самый простой и абсолютно гладкий! – но это зависит от скорости аппаратного обеспечения (= бесполезно)

  public void onDrawFrame(GL10 gl) { frameTime = SystemClock.elapsedRealtime(); elapsedTime = (frameTime - lastTime) / 1000; updateGame(gl, elapsedTime); drawGame(gl); lastTime = frameTime; } 

Это лучший из всех, но это не так гладко, как предыдущий, иногда щелчок

Второй Я попробовал GLSurfaceView.RENDERMODE_WHEN_DIRTY , в onDrawFrame у меня есть только чертежные объекты и этот код в отдельном потоке:

  while (true) { updateGame(renderer, 0.017f); mGLSurfaceView.requestRender(); next_game_tick += SKIP_TICKS; sleep_time = next_game_tick - System.currentTimeMillis(); if (sleep_time >= 0) { try { Thread.sleep(sleep_time); } catch (Exception e) {} } else { Log.d("running behind: ", String.valueOf(sleep_time)); } } 

Это негласно, и это не проблема с «бегом позади»,

Моя цель – плавное движение изображения, как в первом примере кода выше.

Возможная ошибка в другом месте, я смотрю. Пожалуйста, может кто-нибудь помочь мне с этим?
Лучше использовать RENDERMODE_WHEN_DIRTY или RENDERMODE_CONTINUOUSLY?

Спасибо.

Я боролся с тем же вопросом в течение нескольких дней. Цикл выглядит гладко, без привязки к времени в Android, но как только он включает в себя любой тип «синхронизации времени», внешние факторы из управления разработкой android приводят к серьезным прерываниям к окончательному результату.

В основном, эти факторы:

  • EglSwapInterval не реализован в Android, поэтому трудно узнать момент, когда аппаратное обеспечение отображает финальную ничью на экране (синхронизация аппаратного экрана)
  • Thread.sleep не является точным. Поток может спать больше или меньше запрошенного.
  • SystemClock.uptimeMillis () System.nanoTime (), System.currentTimeMillis () и другие измерения, связанные с измерением, не точны (точнее).

Проблема не зависит от технологии рисования (рисунок, openGL 1.0 / 1.1 и 2.0) и метода игрового цикла (шаг фиксированного времени, интерполяция, шаг изменения времени). Как и вы, я пытался использовать Thread.sleep, сумасшедшие интерполяции, таймеры и т. Д. Не имеет значения, что вы будете делать, у нас нет контроля над этими факторами.

Согласно множеству вопросов и ответов на этом сайте, основными правилами для создания плавной непрерывной анимации являются:

  • Уменьшите как минимум GC, удалив все запросы динамической памяти.
  • Отказывать кадры так быстро, что аппаратное обеспечение может обрабатывать их (от 40 до 60 кадров в секунду в большинстве устройств Android).
  • Используйте фиксированные временные шаги с интерполяцией или переменными шагами времени.
  • Оптимизируйте физику обновления и нарисуйте процедуры, которые будут выполняться в относительном постоянном времени без высокой дисперсии пиков.

Конечно, вы сделали много предыдущей работы, прежде чем опубликовать этот вопрос, оптимизируя ваши updateGame () и drawGame () (без заметного GC и относительного постоянного времени выполнения), чтобы получить гладкую анимацию в вашем основном цикле, как вы упомянули: Простой и абсолютно гладкий ».

В вашем конкретном случае с переменным шагом и без особых требований, чтобы быть в идеальной синхронизации с событиями реального времени (например, музыкой), решение прост: «сгладьте шаг« Временная переменная ».
Решение работает с другими схемами игровых циклов (фиксированный шаг времени с переменным рендерингом) и легко переносит концепцию (сглаживает объем смещения, создаваемого updateGame, и часы реального времени на нескольких кадрах).

  // avoid GC in your threads. declare nonprimitive variables out of onDraw float smoothedDeltaRealTime_ms=17.5f; // initial value, Optionally you can save the new computed value (will change with each hardware) in Preferences to optimize the first drawing frames float movAverageDeltaTime_ms=smoothedDeltaRealTime_ms; // mov Average start with default value long lastRealTimeMeasurement_ms; // temporal storage for last time measurement // smooth constant elements to play with static final float movAveragePeriod=40; // #frames involved in average calc (suggested values 5-100) static final float smoothFactor=0.1f; // adjusting ratio (suggested values 0.01-0.5) // sample with opengl. Works with canvas drawing: public void OnDraw(Canvas c) public void onDrawFrame(GL10 gl){ updateGame(gl, smoothedDeltaRealTime_ms); // divide 1000 if your UpdateGame routine is waiting seconds instead mili-seconds. drawGame(gl); // Moving average calc long currTimePick_ms=SystemClock.uptimeMillis(); float realTimeElapsed_ms; if (lastRealTimeMeasurement_ms>0){ realTimeElapsed_ms=(currTimePick_ms - lastRealTimeMeasurement_ms); } else { realTimeElapsed_ms=smoothedDeltaRealTime_ms; // just the first time } movAverageDeltaTime_ms=(realTimeElapsed_ms + movAverageDeltaTime_ms*(movAveragePeriod-1))/movAveragePeriod; // Calc a better aproximation for smooth stepTime smoothedDeltaRealTime_ms=smoothedDeltaRealTime_ms +(movAverageDeltaTime_ms - smoothedDeltaRealTime_ms)* smoothFactor; lastRealTimeMeasurement_ms=currTimePick_ms; } // Optional: check if the smoothedDeltaRealTIme_ms is too different from original and save it in Permanent preferences for further use. 

Для фиксированной схемы шага времени может быть реализовано промежуточное updateGame для улучшения результатов:

 float totalVirtualRealTime_ms=0; float speedAdjustments_ms=0; // to introduce a virtual Time for the animation (reduce or increase animation speed) float totalAnimationTime_ms=0; float fixedStepAnimation_ms=20; // 20ms for a 50FPS descriptive animation int currVirtualAnimationFrame=0; // useful if the updateGameFixedStep routine ask for a frame number private void updateGame(){ totalVirtualRealTime_ms+=smoothedDeltaRealTime_ms + speedAdjustments_ms; while (totalVirtualRealTime_ms> totalAnimationTime_ms){ totalAnimationTime_ms+=fixedStepAnimation_ms; currVirtualAnimationFrame++; // original updateGame with fixed step updateGameFixedStep(currVirtualAnimationFrame); } float interpolationRatio=(totalAnimationTime_ms-totalVirtualRealTime_ms)/fixedStepAnimation_ms; Interpolation(interpolationRatio); } 

Протестировано с рисунком canvas и openGlES10 со следующими устройствами: SG SII (57 FPS), SG Note (57 FPS), вкладка SG (60 FPS), ненавязчивый медленный эмулятор Android 2.3 (43 FPS), работающий в Windows XP (8 FPS). Испытательная платформа рисует около 45 объектов + 1 огромный фон (текстура из исходного изображения 70MP), перемещаясь по пути, указанному в реальных физических параметрах (км / ч и G), без всплесков или щелчков между несколькими устройствами (ну, 8 FPS на эмуляторе Не выглядит хорошо, но его поток с постоянной скоростью, как ожидалось)

Проверьте Графики того, как андроид сообщает время. В некоторых случаях Android сообщает о большом дельта-времени и только в следующем цикле он меньше среднего, что означает смещение при чтении значения realTime.

Введите описание изображения здесь

С более подробной информацией: Введите описание изображения здесь

Как ограничить частоту кадров при использовании Android GLSurfaceView.RENDERMODE_CONTINUOUSLY?

System.currentTimeMillis vs System.nanoTime

Действительно ли метод System.currentTimeMillis () действительно возвращает текущее время?