Статьи

Родная игра на Android – Портирование Quest of Dungeons

Quest of Dungeons выполнен на C ++ и OpenGL, он работает на Windows, Mac и Linux в собственном коде. iOS тоже не проблема, так как у меня просто тонкий слой Objective-C для доступа к некоторым функциям, таким как сенсорный, акселерометр и т. д., а остальная часть кода просто запускается.

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

Еще в 2010 году я провел некоторые исследования, и NDK был довольно плохим, я не думаю, что в то время он даже имел, но с тех пор все изменилось. Я решил вскочить и попытаться перенести все это. В качестве основной цели я хочу сохранить как можно больше встроенного кода, потому что он уже работает, его перенос на Java может привести только к дальнейшим ошибкам в процессе.

инструменты

После некоторых копаний я обнаружил, что вы можете использовать Visual Studio для написания кода для Android (о, счастливого дня).

Visual Studio 2010http://www.visualstudio.com/pt-br/visual-studio-homepage-vs.aspx
VS-Androidhttps://code.google.com/p/vs-android/
Android NDK, Версия 10  :  https://developer.android.com/tools/sdk/ndk/index.html
Android SDK  :  http://developer.android.com/sdk/index.html
ant-1.9.4http: // ant.apache.org/bindownload.cgi
jdk 1.8.0http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

зависимости

Моей следующей заботой были некоторые зависимости, которые у меня есть от других версий:

OpenAL — для доступа к звуковой карте
Vorbis — для загрузки / анализа OGG
FreeType 2 — для загрузки ttf
TinyXML — парсер XML

К счастью, все это удалось собрать на Android.

заявка

В качестве точки входа я использовал android_native_app_glue, который поставляется с NDK, таким образом, вы можете пропустить всю Java (у меня в игре 0 строк Java-кода)

Еще одна важная вещь — эта строка, сообщающая нашему файлу манифеста, что у нас нет Java-кода

<application android:label="@string/app_name" android:hasCode="false"

Загрузка ресурса

Обычно вам просто нужно сделать fopen (имя файла), чтобы открыть файл. Для каждой платформы мне просто нужно знать корневую папку, где находятся ресурсы. Но Android работает по-разному, все ресурсы сжаты, и вам нужно использовать AAssetManager для их загрузки. Затем происходит распаковка файла и получение указателя на него. По крайней мере, это то, что мне удалось понять из процесса.

AAsset* asset = AAssetManager_open(android_asset_manager, filename, 0);

Но это вызвало большую проблему, некоторые зависимости, такие как TinyXML, получают строку и продолжают загружать файл с помощью fopen (). Это означало переделку всех фреймворков, что не очень хорошая перспектива. У меня был код, использующий fopen, и тот факт, что некоторые внешние библиотеки использовали его, тоже представлял проблему. На самом деле я потратил пару дней, размышляя о том, что будет лучшим подходом, но после некоторых исследований я нашел очень простое решение.

#define fopen(name, mode) android_file_open(name, mode)
FILE* android_file_open(const char* fname, const char* mode);

Теперь каждый fopen будет заменен на android_file_open

Что касается самой функции, она попытается открыть файл с помощью AssetManager и вернуть FILE *.
Я также сделал небольшой хак, что если вы попытаетесь открыть файл с правами записи ‘w’, он будет использовать стандартный fopen, таким образом, мы может правильно записывать файлы в пользовательские документы, для сохранения игровых данных и прочего.

FILE* android_file_open(const char* fname, const char* mode) 
{
        if(mode[0] == 'w'){
#undef  fopen
		return fopen(fname,mode);
	}
#define fopen(name, mode) android_fopen(name, mode)
        AAsset* asset = AAssetManager_open(android_asset_manager, fname, 0);
        if(asset) return funopen(asset, android_file_read, android_file_write, android_file_seek, android_file_close);
#undef  fopen
	return fopen(fname,mode); 
}
 
static int android_file_read(void* cookie, char* buf, int size) {
  return AAsset_read((AAsset*)cookie, buf, size);
}
 
static int android_file_write(void* cookie, const char* buf, int size) {
  return 0; 
}
 
static fpos_t android_file_seek(void* cookie, fpos_t offset, int whence) {
  return AAsset_seek((AAsset*)cookie, offset, whence);
}
 
static int android_file_close(void* cookie) {
  AAsset_close((AAsset*)cookie);
  return 0;
}

Теперь просто добавьте эту строку в make-файл, чтобы заставить игру / библиотеки предварительно включить наш файл перед всем остальным

LOCAL_CFLAGS := -g -include android_file_open.h

Так что теперь весь мой fopen-код работает без каких-либо изменений.

OpenGL

Я уже использовал GLES для версии iOS, поэтому у меня не было проблем с самим кодом, однако у меня были некоторые проблемы с производительностью на старых устройствах, например, на устройствах 2010 года с версией 2.3 (игра нацелена на 2.3+)

Особенно по этой функции  glCopyTexSubImage2D .

В игре используются 2d мягкие тени для придания приятной атмосферы, она обновляется время от времени, и я просто использовал glCopyTexSubImage2D с текстурой 256 × 256 вместо VBO, потому что она отлично работала на всем остальном (включая все устройства iOS), но поворачивает на старых устройствах Android это происходит очень медленно, поэтому я добавил опцию в игру, чтобы отключить эти эффекты. Таким образом, более новые устройства могут иметь все навороты, а старые устройства по-прежнему могут нормально работать. Одним из тестирующих устройств был Samsung Galaxy S1, и он вырос с 5 FPS до 30, что более чем приемлемо для игры. Все 4.0+ устройства не должны иметь проблем с запуском этого, и даже если они имеют, они могут просто отключить эту опцию.

Цикл применения

Существует большая вероятность того, что представление GL сбрасывается, когда приложение переходит в фоновый режим, для сохранения ресурсов представление воссоздается каждый раз, когда вы возвращаетесь из фона в приложение. Это создает проблему, поскольку все объекты gl, текстуры, шейдеры, VBO также уничтожаются. Не существует безопасного способа гарантировать, что представление никогда не будет уничтожено во время работы вашего приложения, поэтому я должен был убедиться, что каждый раз, когда представление воссоздается, я перезагружаю все необходимые ресурсы еще раз. Действительно не доволен перезагрузкой всего, но это единственный безопасный способ, если вы обнаружите, что представление было уничтожено, а приложение все еще работает, у вас нет другого выбора. Для этой игры это не имеет большого значения, у меня мало ресурсов, и они быстро загружаются, поэтому пользователь даже не может сказать. Если бы у меня была большая игра с большим количеством больших текстур, мне, вероятно, пришлось бы снова показывать экран загрузки.

Размер экрана

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

int density = AConfiguration_getDensity(engine->app->config);
int screen_size = AConfiguration_getScreenSize(engine->app->config);
 
switch (density)
{
case ACONFIGURATION_DENSITY_DEFAULT:
case ACONFIGURATION_DENSITY_LOW:
case ACONFIGURATION_DENSITY_MEDIUM:
case ACONFIGURATION_DENSITY_HIGH:
break;
}
 
switch (screen_size)
{
case ACONFIGURATION_SCREENSIZE_SMALL:
case ACONFIGURATION_SCREENSIZE_NORMAL:
case ACONFIGURATION_SCREENSIZE_LARGE:
case ACONFIGURATION_SCREENSIZE_XLARGE:
	break;
}

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

окончательный

Теперь, когда я закончил порт, кажется, что мне не пришлось так сильно менять, на самом деле сам код игры почти не тронут, просто движок был изменен для Android. Портирование заняло у меня ~ 2 месяца (у меня дневная работа) и пока работала над расширением для игры. Это не весь хаос во вселенной Android, хотя фрагментация оборудования создает некоторые очень серьезные проблемы, некоторые устройства могут быть слишком медленными, чтобы запускать даже более простые игры.

Если у вас есть вопросы о чем-то еще, что я не освещал в этой статье, не стесняйтесь спрашивать.

Если вы заинтересованы в игре, она доступна в  SteamApp Store  и  Google Play , не стесняйтесь проверить ее:  http://www.questofdungeons.com