- PVSM.RU - https://www.pvsm.ru -
В конце данной [1] статьи мною был озвучен план сделать порт под Android. Тут я попытаюсь описать проблемы, с которыми я столкнулся и методы их решения. Сразу хочу оговорится, что опыта работы с Android на данный момент ровно 2 месяца и возможно некоторые решения опасны или даже не приемлемы на данной платформе.
Движок (хобби) находится в разработке уже 10 лет.
Движок полностью написан на C/C++, до начала портации на Android поддерживал iOS и Windows.
Логика, рендеринг, звук — все на C/C++.
Важная особенность которая сделала портацию на Android очень простой — это файловая система движка.
Псевдо код:
class IStream { void setName(string name ) = 0; open() = 0; write() = 0; … = 0; } class FileStream : public IStream { имплементация через fopen,fread... POSIX API. } class DataPackStream : public IStream { имплементация для работы с .pak файлами Core::Meta* m_packFileStreamMeta;; /// m_packFileStreamMeta->Create() IStream* этого типа будет использован для работы непосредственно с .pak файлом }
Итак: вся работа с файлами в движке основана на интерфейсе IStream, архитектура движка поддерживает фабрику объектов, и её кастомизацию.
Пример кода:
Core::FileStream::Type()->setFactoryMeta(DataPack::DataPackStream::Type()); FileStream* filestream1 = FileStream::Create(); // тоже самое будет и при new FileStream() FileStream* filestream2 = FileStream::CreateExact(); filestream1 будет типа DataPackStream filestream2 будет типа FileStream
Ничего нового и все довольно стандартно.
Так как все ресурсы запрепроцесенны в .pak файл, то для Android системы пришлось написать свою реализацию IStream специально для работы с Java.
Инициализация данного ресурса на Java
AssetFileDescriptor RawAssetsDescriptor = this.getApplicationContext() .getResources().openRawResourceFd(R.raw.data000); if (RawAssetsDescriptor != null) { FileInputStream fis = RawAssetsDescriptor.createInputStream(); NativeMethods.dataPackChannel = fis.getChannel(); NativeMethods.dataPackOffset = RawAssetsDescriptor.getStartOffset(); NativeMethods.dataPackSize = RawAssetsDescriptor.getLength(); }
Нейтив класс все вызовы open,read,seek транслирует опять в Java
class AndroidPackStream : public IStream { //.... // пример работы read size_t read(void *buffer, size_t size, size_t count) { if( JavaHelpers::m_pClass ) { jmethodID mid = JavaHelpers::GetEnv()->GetStaticMethodID(JavaHelpers::m_pClass, "freadDataPack", "(I)I"); int res = JavaHelpers::GetEnv()->CallStaticIntMethod(JavaHelpers::m_pClass, mid, (int)size*count); jfieldID field = JavaHelpers::GetEnv()->GetStaticFieldID(JavaHelpers::m_pClass,"byteBuffer","Ljava/nio/MappedByteBuffer;"); jobject obj = JavaHelpers::GetEnv()->GetStaticObjectField(JavaHelpers::m_pClass,field); uint8_t* pData=(uint8_t*)JavaHelpers::GetEnv()->GetDirectBufferAddress(obj); memcpy(buffer,pData,size*count); JavaHelpers::GetEnv()->DeleteLocalRef(obj); return res/size; } }
Java реализации чтения данного стрима с возвратом данных назад в нейтив
public static int freadDataPack(int count) { long curPos = dataPackChannel.position(); int countReaded = count; byteBuffer = dataPackChannel.map(MapMode.READ_ONLY,dataPackChannel.position(), count); byteBuffer.load(); dataPackChannel.position(curPos+countReaded); return countReaded; }
Важный момент — сборщик apk сжимает все ресурсы, а мы хотим читать файл как будто он просто лежит в файловой системе. Для того чтобы наш .pak файл не сжимался внутри apk — вы должны сменить ему расширение на одно из тех которые будут говорить упаковщику не сжимать данные файлы при упаковке (детали ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/ [2]). Я выбрал .imy.
Загрузка ресуров таким способом очень быстра, например на Kindle Fire работает быстрее чем на iPad 1
Оговорю стразу что такой финт можно провернуть если у вас не очень много данных.
Для большого объема данных — вы можете сами распаковать данные на внутрений носитель(например при первом запуске) и напрямую их использовать посредством функций fopen fread (в моем случае, через не подмененный FileStream) и.т.д.
Для Windows и iOS был использован OpenAL. OpenAL для Android был собран благодаря pielot.org/2010/12/14/openal-on-android/. [3] Заработал не сразу, а только после исправлений которые описаны в комментариях на вебсайте.
За обработку вызовов платформо зависимых реализаций отвечает очень простой класс
Псевдо код:
class IPlatfomCommandFeedback { void onResponse(string&); }; class IPlatfomCommand { string execute(string& command, IPlatfomCommandFeedback* feebback); };
Для iOS своя реализация на Objective-C а для Android своя реализация на JNI->Java.
Нейтив часть работает с использованием OpenGL, для Android были сделаны минимальные изменения. Как в последствии оказалось этого было недостаточно. Дело в том что на Windows и на iOS текстуры не теряются когда приложение уходит в бакграунд, а вот для Android это происходит всегда. В движке уже был менеджер текстур (в основном для отладки) и добавить перезагрузку ресурсов оказалось не сложно.
В самой первой [4] статье я писал о том, что изначально компилировал iOS с использованием toolchain и у меня были настроенные makefile. Вот тут то мне это и пригодилось. Были дописаны таргеты для сборки с использованием Android NDK, добавлен степ в билдеры Eclipse и все взлетело. Да, можно использовать билд систему из Android NDK samples. Её я использовал только для выяснения параметров которые используются для вызовов gcc.
Интеграция обоих библиотек прошла без проблем — четко по инструкции разработчиков
Тут все более или менее понятно — нужно только позаботится о том чтобы ваши JNI Native бинды не обускейтились
Железо: Kindle Fire, и пару планшетов и мобильных телефонов друзей(для тестирования).
Софт: Eclipse с плагинами
Android Market это не AppStore. Там все по другому. Там нет категории New.
Для сравнения на AppStore в первый день релиза было 800+ скачек, во второй 2000+, на Andoid Market официальная первая сотня скачек была получена только на третий день. Активность по раскрутке для обоих платформ была одинакова
Если ваше приложение бесплатное вы можете свободно распространять apk, любой человек может проинсталировать себе данное приложение с любого источника. Если это сделаете не вы, то за вас это сделают другие.
Бестиарий устройств велик. Количество проблем соответственно.
Процесс ревью на Amazon AppStore for Android занимает неделю, плюс еще несколько дней пока приложение появится в списке AppStore на Kindle Fire. Количество скачек и динамика соответствует Android Market
Лучи ненависти Корпорации Добра — уже неделя как рекламный баннер был отправлен на апрув. До сих пор тишина.
На данный момент количество денег (напомню в игре только реклама) которые приносит iOS версия в 40 (сорок) раз больше чем версия для Android. Понятно, что сравнение не корректно и надо подождать как минимум еще пару месяцев пока не стабилизируется количество постоянных игроков в день.
Автор: PavloG
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/2967
Ссылки в тексте:
[1] данной : http://habrahabr.ru/blogs/gdev/128953/
[2] ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/: http://ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/
[3] pielot.org/2010/12/14/openal-on-android/.: http://pielot.org/2010/12/14/openal-on-android/.
[4] первой: http://habrahabr.ru/blogs/gdev/122351/
Нажмите здесь для печати.