За последний месяц движок разрабатываемой игры Survive Alone претерпел значительные оптимизации и доработки. Подробности разработки и несколько скриншотов под катом.
Итак, начнём по порядку.
Первое, что я был вынужден сделать - переписать движок на использование игровых объектов независимо от основных классов (Activity и SurfaceView). Да, в игре изначально всё было в куче и было очень сложно добавлять новый функционал. Сразу не подумал об этом, пришлось переписывать почти весь код, зато теперь всё "по полочкам" и стало гораздо удобнее добавлять новые объекты в игру.
Второе, что я сделал - оптимизировал игровой поток. Он работал так же, как и во всех моих предшествующих проектах, по схеме "каждый фрейм - новый поток", т.е. имеется Runnable с кодом фрейма и Handler, которой запускает каждый следующий фрейм, когда отрисуется текущий с задержкой в 1000/FPS миллисекунд. Довольно простой и эффективный с точки зрения многопоточности метод. Каждый новый фрейм мог выполняться на любом ядре процессора, что с одной стороны хорошо, а с другой стороны может вызвать подергивания и провалы вплоть до 15 FPS. Это не круто и портит впечатление от игры. К тому же постоянный вызов методов создания и удаления потока снижали число FPS. Было решено переписать основной игровой поток на схему "все фреймы - один единственный дополнительный поток" (что более грамотно с точки зрения игростроения).
Всё получилось не сразу. Типовой код, коего очень много в интернете, например здесь или здесь был усовершенствован и переписан под игру. Теперь главный игровой поток держит 40 FPS стабильно и без провалов, а если не держит (на слабых девайсах), то автоматически проглатывает нужное число циклов отрисовки и рисует сразу несколько обработанных фреймов, что ускоряет игру при низких значениях FPS в соответствующее пропущенным фреймам число раз, т.е. если устройство держит только 15 FPS, то поток ускорит игру в 40/15≈3 раза, т.е. будет 15×3=45 фиктивных FPS. Думаю, что я изобрел велосипед (ибо похожая схема уже существует и работает во многих проектах), но именно мой "велосипед" лучше всего удовлетворяет поставленной задаче, к тому же он достаточно простой в реализации.
Всё хорошо, но иногда поток нарывается на исключения при отрисовке или просчете нового фрейма и падает, что, конечно же, тянет за собой падение всего приложения. Долго же я танцевал с бубном, перехватывая исключения, пытаясь угодить сразу двум требованиям:
- При незначительной ошибке поток не должен обращать на неё внимания и отрисовать фрейм ещё раз;
- При фатальной ошибке поток должен безопасно завершить свою работу и выдать сообщение об ошибке на экран пользователю в виде всплывающего окна.
Объясню почему меня не устраивает стандартное сообщение об ошибке при падении приложения. Дело в том, что после краха приложения, вызванного не основным потоком, само приложение закрывается, а дополнительный поток странным образом не всегда выгружается из памяти, хотя этот поток не фоновый. Это не позволяет запустить такой же поток при рестарте игры, и игра снова вылетает, и так до тех пор, пока сборщик мусора не очистит из оперативной памяти зависший поток. Так что в данной ситуации мой способ работоспособнее, хотя не исключаю тот факт, что можно было сделать по-другому/более правильно.
С потоками всё. Теперь передо мной встала другая задача: как сделать систему освещения в игре? Игра наделена временем, оно идёт постоянно, наступает ночь, становится темно, и на небе сверкают звезды. Романтика, да и только. Но с горящим в темноте костром было бы ещё приятнее. Костер я сделал, но вот темнота ночью создавалась следующим образом: рисовались звезды, затем фон, затем все объекты, а потом обычная заливка темно синим цветом с режимом смешивания DARKEN (Темнее). Как же сделать так, чтобы свет от костра был реалистичным, и пространство вокруг него становилось видимым лучше, чем всё вокруг?
На помощь пришёл способ, собранный по кусочкам из разных источников:
- Создаем новый Bitmap и его Canvas величиной с экран;
- Заливаем созданный холст темно синим цветом;
- Рисуем все маски освещенности с режимом смешивания DST_OUT;
- Рисуем полученный Bitmap с режимом смешивания DARKEN и прозрачностью в зависимости от времени суток на основном холсте.
- Создаем новый Bitmap и его Canvas величиной с экран;
- Заливаем созданный холст темно синим цветом;
- Рисуем маску звездного неба с режимом смешивания DST_OUT;
- Рисуем маску фона, находящегося перед звездным небом с режимом SRC_IN и фильтром на один цвет - темно синий;
- Рисуем маски освещенности с режимом смешивания DST_OUT;
- Рисуем полученный Bitmap с режимом смешивания DARKEN и прозрачностью в зависимости от времени суток на основном холсте.
Сейчас движок игры полностью готов, осталось наполнить его ресурсами и функционалом. На это уйдет значительное время, но я всё же постараюсь снять ролик и выпустить демку к Новому 2014 Году.
На этом всё. Спасибо, что дочитали до конца.
И напоследок несколько скриншотов:
Комментариев нет:
Отправить комментарий