Библиографическое описание:

Бутенко В. В. Слушатель события обновления сцены. Кадронезависимое движение [Текст] // Технические науки в России и за рубежом: материалы III междунар. науч. конф. (г. Москва, июль 2014 г.). — М.: Буки-Веди, 2014. — С. 1-3.

Приводится решение кадронезависимого движения в приложениях под Android на примере классов фреймворка PeeKaBoo. Описывается создание слушателя обновления сцены, зависящего от времени, выбранного разработчиком игры.

Ключевые слова: GLSurfaceView, Android, PeeKaBoo, кадронезависимое движение, обновление экрана, алгоритмы, программирование.

Введение

Предположим, что приложение будет запущено на устройстве с частотой обновления экрана 60 кадров в секунду. При использовании GLSurfaceView, объект будет двигаться с наступлением события onDrawFrame на такое количество пикселов, которое будет задано для его перемещения.

При движении на один пиксел, за сто кадров объект переместиться на сто пикселов за 1,66 секунды. Но запустив эту же игру на устройстве с частотой обновления экрана 48 кадров в секунду, это произойдет за 2,08 секунды [1].

Таким образом, характеристики устройства могут полностью изменить показания производительности игры. Решение состоит в создании слушателя, зависящего от времени введенного разработчиком игры, а не от частоты обновления экрана.

Класс UpdateHandler

Создаем класс UpdateHandler (Листинг 1), который будет содержать две переменные (интервал обновления в миллисекундах и время с начала запуска игры, также в миллисекундах) и пять методов.

class UpdateHandler {

            private int  intervalMsec = 0;

            private long lastTimeMillis = SystemClock.uptimeMillis();

            UpdateHandler(int interval) {

                        intervalMsec = interval;

            }

            int interval() {

                        return intervalMsec;

            }

            void update(long msec) {

                        lastTimeMillis = msec;

            }

            long elapsed(long msec) {

                        return msec - lastTimeMillis;

            }

            void reset() {

                        lastTimeMillis = 0;

            }

}

Листинг 1. Класс UpdateHandler

Одноименный метод класса UpdateHandler принимает один параметер — интервал обновления, и приравнивает его значение переменной intervalMsec, а interval() возвращает это значение. update() принимает время в миллисекундах, прошедшее с начала загрузки, и приравнивает его переменной lastTimeMillis. elapsed() возвращает разницу между текущим временем от запуска игры и временем сохраненным в lastTimeMillis. reset() обнуляет lastTimeMillis.

Алгоритм работы слушателя

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

C использованием класса GLSurfaceView и его встроенного интерфейса Renderer, получаем доступ к трем абстрактным методам: onDrawFrame, onSurfaceCreated и onSurfaceChanged. Метод onSurfaceChanged вызывается при изменении экрана, onSurfaceCreated — при его создании, а onDrawFrame — при его обновлении, этот метод и поможет нам реализовать слушателя (Листинг 2) [2, 3].

public void onDrawFrame(GL10 gl) {

            if (updateIntervalMsec > 0 &&

                        SystemClock.uptimeMillis() - lastUpdateMsec > updateIntervalMsec) {

                        for (SceneUpdateListener listener : updateListeners.keySet()) {

                                   UpdateHandler handler = updateListeners.get(listener);

                                   if (handler.interval() <= 0) continue;

                                   long now = SystemClock.uptimeMillis();

                                   long elapsed = handler.elapsed(now);

                                   if (elapsed > handler.interval()) {

                                               listener.onUpdateScene(this, elapsed, gl);

                                               handler.update(now);

                                   }

                        }

                        lastUpdateMsec = SystemClock.uptimeMillis();

            }

}

Листинг 2. Алгоритм работы слушателя

Проверяем, инициализирован ли слушатель onUpdateScene (updateIntervalMsec > 0) и время прошедшее с момента последнего его вызова больше указанного при его инициализации (SystemClock.uptimeMillis() — lastUpdateMsec > updateIntervalMsec). SceneUpdateListener — это интерфейс (Листинг 3), содержащий абстрактный метод onUpdateScene, который будет в последствии переопределяться.

public interface SceneUpdateListener {

    void onUpdateScene(PeeKaBooScene scene, long elapsedMsec, GL10 gles10);

}

Листинг 3. Интерфейс для получения уведомления о события обновления сцены

updateListeners — хэш-таблица у которой, ключем является класс-слушатель SceneUpdateListener, а значением — UpdateHandler.

Важно внести возможность добавления и удаления слушателя (Листинг 4). Делаем это двумя методами: registerUpdateListener и unregisterUpdateListener.

public void registerUpdateListener(int intervalMsec, SceneUpdateListener listener) {

            UpdateHandler handler = new UpdateHandler(intervalMsec);

            updateListeners.put(listener, handler);

            int interval = intervalMsec > 100 ? intervalMsec / 100 : 1;

            if (updateIntervalMsec == 0) {

                        updateIntervalMsec = interval;

            } else {

                        updateIntervalMsec = Math.min(updateIntervalMsec, interval);

            }

}

public void unregisterUpdateListener(SceneUpdateListener listener) {

            updateListeners.remove(listener);

}

Листинг 4. Методы для добавления и удаления слушателя.

В методе registerUpdateListener создаем экземпляр класса UpdateHandler и добавляем ключ и значение в хэш-таблицу. Если интервал обновления был указан как ноль, меняем его на единицу. Если было введено число больше ста, делим это число на сто. Этим действием получаем десятые доли секунды.

В методе unregisterUpdateListener удаляем ключ из хэш-таблицы.

Получение уведомлений о события обновления сцены

Остаётся только реализовать интерфейс SceneUpdateListener, переопределить метод onUpdateScene и зарегестрировать слушателя методом registerUpdateListener (Листинг 5).

import pack.peekaboo.PeeKaBooActivity;

import pack.peekaboo.PeeKaBooScene;

public class MyActivity extends PeeKaBooActivity implements SceneUpdateListener {

            @Override

            public PeeKaBooScene onLoadScene() {

                        PeeKaBooScene scene = new PeeKaBooScene();

                        scene.registerUpdateListener(1, this);

                        return scene;

    }

            @Override

            public void onUpdateScene(PeeKaBooScene scene, long elapsedMsec,

                                                                                                                                 GL10 gles10) {

            }

Листинг 5. Пример использования слушателя события обновления сцены

В приведенном примере класс MyActivity наследуется от класса PeeKaBooActivity. Это класс фреймворка PeeKaBoo для работы с дополненной реальностью. В этом же фреймворке и применяется описанный выше алгоритм слушателя.

Заключение

В завершении, несколько слов о том, где может быть полезен описанный метод. Это могут быть различные приложения дополненной реальности, дистанционного обучения, социальные сервисы, виджеты, игры, все те приложения, которым необходима работа с графикой.

Литература:

1.                 Марио Цехнер. Программирование игр под Android: [перевод.: Е. А. Сидорович, Е. В. Зазноба]; М.: Питер, 2013. — 688 с.

2.                 Android Developers. GLSurfaceView [Электронный ресурс]. — URL: http://developer.android.com/reference/android/opengl/GLSurfaceView.html (21.05.2014).

3.                 Android Developers. GLSurfaceView.Renderer [Электронный ресурс]. — URL: http://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html (21.05.2014).

Обсуждение

Социальные комментарии Cackle