Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Unity_в_действии_Джозеф_Хокинг_Рус.pdf
Скачиваний:
83
Добавлен:
21.06.2022
Размер:
26.33 Mб
Скачать

12.3. Сборки для мобильных устройств: iOS и Android      311

В а а External Tools

а Unity Preferences

Рис. 12.5. Задание пути к Android SDK в окне Unity Preferences

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

Найдите в диспетчере файлов свой APK-файл и установите приложение. Как видите, в своей основе процесс сборки для платформы Android намного проще, чем для платформы iOS. К сожалению, с процессами настройки сборки и добавления внешних модулей ситуация ровно обратная. В данном случае все намного сложнее, чем для iOS. Подробности вы узнаете в разделе 12.3.3, а пока давайте поговорим о сжатии текстур.

12.3.2. Сжатие текстур

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

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

Сжатие текстур в данном случае за вас выполнит Unity; в большинстве инструментов разработки эту процедуру приходится выполнять вручную, в то время как Unity, как правило, импортирует несжатые изображения, постфактум указывая вариант сжатия в настройках импорта (рис. 12.6).

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

312      Глава 12. Развертывание игр на устройствах игроков

В

а а Advanced

а

а а а а

Щ а • а• Android settings

а а€а

€ а ‚

Ф а Override settings for Android • а а а

а † • а

Рис. 12.6. Настройки сжатия текстур на панели Inspector

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

А именно, все устройства iOS используют графические ускорители PowerVR; соответственно, в приложениях для iOS может применяться оптимизированный формат сжатия PVR. Эти графические ускорители порой встречаются и у устройств Android, но с такой же частотой встречаются ускорители Adreno от Qualcomm, Mali от ARM и другие варианты. В результате приложения для Android в общем случае пользуются алгоритмом Ericsson (Ericsson Texture Compression, ETC), который поддерживается всеми устройствами Android. К сожалению, этот алгоритм, как и появившийся позже ETC1, и разрабатываемый сейчас ETC2, не поддерживает альфа-канал, соответственно, снабженные им изображения с его помощью сжиматься не могут.

При переходе на другую платформу Unity подвергает изображение повторному сжатию. В случае платформы Android ограничение на работу с каналом прозрачности обходится путем преобразования изображения к 16-битному цвету вместо его сжатия. Размер файла при этом уменьшается, но за счет ухудшения качества изображения. Поэтому периодически приходится вручную возвращать настройки сжатия отдельных изображений в исходное состояние, определяя для каждого изображения в отдельности, где требуется обработка канала прозрачности, а где можно воспользоваться алгоритмом ETC (обеспечив лучшее качество изображения), и выбирая, какие из изображений с альфа-каналом нуждаются в уменьшении размера, а какие можно оставить без сжатия.

Для коррекции способа сжатия текстуры пользуйтесь настройками, показанными на рис. 12.6. Для доступа к ним выберите в раскрывающемся списке Texture Type вариант Advanced и прейдите на вкладку, отмеченную значком Android, чтобы переопределить исходные настройки сжатия.

Эта процедура является важной деталью оптимизации при работе с платформой Android. Тема же следующего раздела одинаково важна как для iOS, так и для Android. Мы поговорим о разработке их собственных подключаемых модулей.

12.3.3. Разработка подключаемых модулей

Инструмент Unity обладает богатейшей встроенной функциональностью, но по большей части это общая для всех платформ функциональность. Использование же

Объект создается в этой статической функции, поэтому в редакторе его создавать не нужно.

12.3. Сборки для мобильных устройств: iOS и Android      313

привязанных к конкретной платформе наборов инструментов (таких, как Play Game Services для Android) часто требует для Unity дополнительных модулей.

СОВЕТ  Для функциональных особенностей, связанных с платформами iOS и Android, доступно множество готовых модулей. Принцип управления ими аналогичен описываемому в этом разделе, просто вы пользуетесь уже готовым кодом, написанным специально для вас.

Процесс обмена данными с внутренними модулями аналогичен процессу обмена данными с браузером. Со стороны Unity есть специальные команды, вызывающие функции внутри модулей. Модули же, со своей стороны, для отправки сообщений объектам в Unity-сценах пользуются методом SendMessage(). Конкретная реализация кода зависит от платформы, но принцип функционирования во всех случаях сохраняется.

ПРИМЕЧАНИЕ  Как и исходный процесс сборки, процесс разработки подключаемых модулей для мобильных устройств часто меняется — не со стороны Unity, а со стороны кода аппаратной платформы. Я описываю в этой главе общий принцип, а актуальную информацию, касающуюся деталей реализации, вы можете найти в Интернете.

Кроме того, модули для обеих платформ Unity хранит в одном и том же месте. Со­ здайте на вкладке Project папку Plugins; она относится к папкам, которые Unity обрабатывает особым способом, как, к примеру, папку Editor. В данном случае Unity автоматически ищет в папке Plugins файлы подключаемых модулей. Внутри этой папки создайте еще две — для Android и для iOS; их содержимое Unity будет копировать в процессе сборки.

Подключаемые модули iOS

Подключаемый модуль — это всего лишь некий код для аппаратной платформы, к которому обращается Unity. Поэтому начните с создания сценария TestPlugin (скопируйте в него код следующего листинга).

Листинг 12.4. Сценарий TestPlugin, вызывающий из Unity код для iOS

using UnityEngine; using System;

using System.Collections;

using System.Runtime.InteropServices;

public class TestPlugin : MonoBehaviour { private static TestPlugin _instance;

public static void Initialize() { ¬ if (_instance != null) {

Debug.Log("TestPlugin instance was found. Already initialized"); return;

}

Debug.Log("TestPlugin instance not found. Initializing...");

GameObject owner = new GameObject("TestPlugin_instance");

314      Глава 12. Развертывание игр на устройствах игроков

_instance = owner.AddComponent<TestPlugin>(); DontDestroyOnLoad(_instance);

}

#region iOS ¬ Тег, определяющий раздел кода; сам по себе он ничего не делает.

[DllImport("__Internal")]

private static extern float _TestNumber(); │ Ссылка на функцию в коде iOS.

[DllImport("__Internal")]

private static extern string _TestString(string test); #endregion iOS

public static float TestNumber() { float val = 0f;

if (Application.platform == RuntimePlatform.IPhonePlayer) val = _TestNumber(); ¬ Вызывается в случае платформы IPhonePlayer. return val;

}

public static string TestString(string test) { string val = "";

if (Application.platform == RuntimePlatform.IPhonePlayer) val = _TestString(test);

return val;

}

}

Первым делом обратите внимание, что статическая функция Initialize() создает

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

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

Именно здесь происходит основное действие, в том числе использование атрибута DLLImport и статических внешних команд. Именно они связывают Unity с функциями написанного вами кода для устройств. Затем эти функции вызываются в методах сценария (с условной инструкцией, проверяющей, что код запущен на платформе iPhone/iOS).

Теперь нужно протестироватьфункции модуля. СоздайтесценарийMobileTestObject, а также пустой объект сцены, с которым нужно будет связать этот сценарий. Код сценария скопируйте из следующего листинга.

Листинг 12.5. Использование подключаемого модуля в сценарии MobileTestObject

using UnityEngine;

using System.Collections;

public class MobileTestObject : MonoBehaviour { private string _message;

void Awake() {

TestPlugin.Initialize(); ¬ Инициализация модуля в начале кода.

12.3. Сборки для мобильных устройств: iOS и Android      315

}

//Используем это для инициализации void Start() {

_message = "START: " + TestPlugin.TestString("ThIs Is A tEsT");

}

//Функция Update вызывается в каждом кадре

void Update() {

// Проверяем, коснулся ли пользователь экрана if (Input.touchCount==0){return;}

Touch touch = Input.GetTouch(0); ¬ Ответ на ввод данных методом касания. if (touch.phase == TouchPhase.Began) {

_message = "TOUCH: " + TestPlugin.TestNumber();

}

}

void OnGUI() {

GUI.Label(new Rect(10, 10, 200, 20), _message); ¬ Отображение сообщения в углу экрана.

}

}

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

Теперь осталось написать код для устройства, на который ссылается сценарий TestPlugin. Для устройств iOS он пишется на языке Objective C и/или C, поэтому нам потребуется как файл заголовка с расширением .h, так и файл реализации с расширением .mm. Как уже упоминалось, такие файлы должны находиться в папке Plugins/ iOS/ на вкладке Project. Создайте в этой папке сценарии TestPlugin.h и TestPlugin.mm; в файл с расширением .h скопируйте код следующего листинга.

Листинг 12.6. Заголовок TestPlugin.h для iOS-кода

#import <Foundation/Foundation.h>

@interface TestObject : NSObject { NSString* status;

}

@end

Объяснение таких сложных вопросов, как программирование для iOS, выходит за рамки темы данной книги, поэтому, чтобы понять смысл данного кода, обратитесь к справочной документации. Код следующего листинга нужно ввести в файл с расширением .mm.

Листинг 12.7. Реализация TestPlugin.mm

#import "TestPlugin.h"

@implementation TestObject

316      Глава 12. Развертывание игр на устройствах игроков

@end

NSString* CreateNSString (const char* string)

{

if (string)

return [NSString stringWithUTF8String: string]; else

return [NSString stringWithUTF8String: ""];

}

char* MakeStringCopy (const char* string)

{

if (string == NULL) return NULL;

char* res = (char*)malloc(strlen(string) + 1); strcpy(res, string);

return res;

}

extern "C" {

const char* _TestString(const char* string) { NSString* oldString = CreateNSString(string); NSString* newString = [oldString uppercaseString]; return MakeStringCopy([newString UTF8String]);

}

float _TestNumber() {

return (arc4random() % 100)/100.0f;

}

}

И снова подробное объяснение кода выходит за рамки темы нашей книги. Обратите внимание, сколько функций используется для преобразования Unity-строк в форму, которую понимает данный код.

СОВЕТ  В рассмотренном примере взаимодействие осуществлялось только в одном направлении: из Unity к модулю. Но код модуля также может отправлять данные в Unity методом UnitySendMessage(). Сообщения отправляются именованному объекту в сцене; в процессе инициализации модуля создается предназначенный именно для этого объект TestPlugin_instance.

Теперь, когда у нас есть код для аппаратной платформы, можно сгенерировать приложение для iOS и протестировать его работу на устройстве. Потрясающе! Но мы пока научились создавать модули только для iOS, давайте посмотрим, как этот процесс выглядит для платформы Android.

Модули для Android

Создание модуля для Android со стороны Unity представляет собой практически аналогичный процесс. Нам даже не нужно вносить правки в сценарий MobileTestObject. Дополнения, показанные в следующем листинге, нужно вставить только в сценарий

TestPlugin.

12.3. Сборки для мобильных устройств: iOS и Android      317

Листинг 12.8. Редактирование сценария TestPlugin под платформу Android

...

#region iOS [DllImport("__Internal")]

private static extern float _TestNumber();

[DllImport("__Internal")]

private static extern string _TestString(string test); #endregion iOS

#if UNITY_ANDROID

 

private static Exception _pluginError;

 

private static AndroidJavaClass _pluginClass;

private static AndroidJavaClass GetPluginClass() {

Обеспечиваемая Unity

if (_pluginClass == null && _pluginError == null) {

функциональность AndroidJNI.

AndroidJNI.AttachCurrentThread();

try {

_pluginClass = new

Имя запрограммированного

AndroidJavaClass("com.companyname.testplugin.TestPlugin");

¬ нами класса; измените его,

} catch (Exception e) {

если нужно.

_pluginError = e;

 

}

 

}

 

return _pluginClass;

 

}

private static AndroidJavaObject _unityActivity; private static AndroidJavaObject GetUnityActivity() {

if (_unityActivity == null) { AndroidJavaClass unityPlayer = new

AndroidJavaClass("com.unity3d.player.UnityPlayer"); ¬ Unity создает экран для приложения Android. _unityActivity =

unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

}

return _unityActivity;

}

#endif

public static float TestNumber() { float val = 0f;

if (Application.platform == RuntimePlatform.IPhonePlayer) val = _TestNumber();

#if UNITY_ANDROID

if (!Application.isEditor && _pluginError == null)

val = GetPluginClass().CallStatic<int>("getNumber"); ¬ Обращение к функциям в модуле .jar. #endif

return val;

}

public static string TestString(string test) { string val = "";

if (Application.platform == RuntimePlatform.IPhonePlayer) val = _TestString(test);

#if UNITY_ANDROID

if (!Application.isEditor && _pluginError == null)

318      Глава 12. Развертывание игр на устройствах игроков

val = GetPluginClass().CallStatic<string>("getString", test); #endif

return val;

}

}

Думаю, вы заметили, что большинство дополнений появилось внутри определений платформы UNITY_ANDROID; как объяснялось в начале этой главы, эти директивы компилятора приводят к тому, что код применяется только при работе на определенной платформе, во всех остальных случаях он просто игнорируется. Если код для iOS на остальных платформах не производил никаких действий (он ничего не делал, но и не вызывал ошибок), код модулей для Android будет компилироваться, только если инструмент Unity настроен на работу с платформой Android.

В частности, обратите внимание на обращения к объекту AndroidJNI. Эта система в Unity обеспечивает связь с кодом устройства для Android. Другим, возможно, не совсем понятным элементом будет класс Activity; в приложениях для Android он соответствует экрану приложения. Экраном в нашем случае будет служить Unity, значит, коду модуля нужен доступ к этому экрану, чтобы в случае необходимости обойти его.

Наконец, вам требуется код для Android. Если код для iOS пишется на таких языках, как Objective C и C, приложения для Android программируются на языке Java. Но для модуля мы не можем взять и написать код на Java; модуль должен находиться в JAR-архиве. К сожалению, в данном случае подробное рассмотрение происходящего выходит за рамки книги. Впрочем, для примера вы можете посмотреть приведенный далее листинг, демонстрирующий файл сборки Ant (укажите собственные варианты пути к файлам; особое внимание обратите на Unity-файл classes.jar, применяемый при сборке модулей для Android), а также листинг 12.10 с кодом Java для используемого модуля.

Листинг 12.9. Сценарий build.xml, генерирующий JAR-архив из Java-кода

<?xml version="1.0" encoding="UTF-8"?> <project name="TestPluginJava">

<!-- Измените это в соответствии с вашей конфигурацией -->

<property name="sdk.dir" value="LOCATION OF ANDROID SDK"/>

<property name="target" value="android-18"/> <property name="unity.androidplayer.jarfile"

value="/Applications/Unity/Unity.app/Contents/PlaybackEngines/

AndroidPlayer/development/bin/classes.jar"/>

<!-- Исходная папка -->

<property name="source.dir"

value="LOCATION OF THIS PROJECT/Assets/Plugins/ Android/TestPlugin" />

<!—Выходная папка для файлов .class -->

<property name="output.dir"

value="LOCATION OF THIS PROJECT/Assets/Plugins/ Android/TestPlugin/classes"/>

<!-- Имя создаваемого архива jar. Обратите внимание, что оно должно совпадать с именем класса и с именем,

указанным в файле AndroidManifest.xml-->

<property name="output.jarfile" value="../TestPlugin.jar"/>

<!-- Создает выходные папки, если они пока отсутствуют. -->

12.3. Сборки для мобильных устройств: iOS и Android      319

<target name="-dirs" depends="message">

<echo>Creating output directory: ${output.dir} </echo> <mkdir dir="${output.dir}" />

</target>

<!-- Компилирует файлы .java этого проекта в файлы .class. -->

<target name="compile" depends="-dirs"

description="Compiles project's .java files into .class files"> <javac encoding="ascii" target="1.6" debug="true"

destdir="${output.dir}" verbose="${verbose}" includeantruntime="false">

<src path="${source.dir}" /> <classpath>

<pathelement location="${sdk.dir}\platforms\${target}\android.jar"/>

<pathelement location="${unity.androidplayer.jarfile}"/> </classpath>

</javac>

</target>

<target name="build-jar" depends="compile">

<zip zipfile="${output.jarfile}" basedir="${output.dir}" /> </target>

<target name="clean-post-jar"> <echo>Removing post-build-jar-clean</echo> <delete dir="${output.dir}"/>

</target>

<target name="clean"

description="Removes output files created by other targets."> <delete dir="${output.dir}" verbose="${verbose}" />

</target>

<target name="message">

<echo>Android Ant Build for Unity Android Plugin</echo> <echo> message: Displays this message.</echo>

<echo> clean: Removes output files created by other targets. </echo>

<echo> compile: Compiles .java files into .class files.</echo> <echo> build-jar: Compiles .class files into .jar file.</echo>

</target>

</project>

Листинг 12.10. Файл TestPlugin.java, компилируемый в JAR-архив package com.companyname.testplugin;

public class TestPlugin { private static int number = 0;

public static int getNumber() { number++;

return number;

}

public static String getString(String message) { return message.toLowerCase();

}

}