Статьи

Разработка 3D-игр с помощью ShiVa3D Suite: развертывание проекта

Хотите узнать о разработке 3D-игр? Сейчас время учиться! В этой серии из пяти частей будет продемонстрировано, как создать простую игру с помощью ShiVa3D Suite, кроссплатформенного игрового движка 3D и инструмента для разработки. Это последняя часть серии, в которой вы узнаете, как настроить игру в Eclipse как проект и развернуть ее на платформе Android. Поскольку ShiVA 3D — это кроссплатформенный инструмент, мы также обсудим, как перенести игру на iOS.


В четвертой части этой серии мы закончили кодирование оставшихся AIModels, EggAI и MainAI. Мы также выполнили юнит-тестирование путем анимации игры. Затем мы экспортировали игру из редактора ShiVa для импорта в Shiva Authoring Tool. Наконец, мы обсудили два различных варианта разработки в Shiva Authoring Tool: один для создания исполняемого файла Android, а другой для создания проекта Eclipse. В части 5 мы сначала поговорим о настройке игры в Eclipse. Это будет включать настройку проекта Eclipse, изменения кода, а также сборку кода Java и собственных библиотек. К этому моменту мы завершим учебник с точки зрения разработки и развертывания. В оставшейся части части 5 мы рассмотрим код, обсудим, как перенести игру на устройства iOS, и дадим несколько заключительных замечаний.

В приведенных ниже инструкциях мы предполагаем, что выходной файл Duck_Android.zip из Shiva Authoring Tool находится в папке D: \ temp (см. Раздел «Создание проекта» в предыдущем уроке). Если вы выбрали другое место, измените эти инструкции соответствующим образом. Кроме того, вы должны выполнить шаги, описанные в части 4 в разделе «Подготовка устройства Android к установке».

Прежде чем импортировать проект в среду Eclipse, убедитесь, что плагин Eclipse ADT указывает на правильное расположение Android SDK в вашей локальной системе. Чтобы проверить это, в меню Eclipse перейдите в Window -> Preferences -> Android. В окне «Местоположение SDK» должно быть указано местоположение Android SDK. После правильной настройки вы должны увидеть нечто похожее на пример ниже.

Настройки Eclipse

Рисунок 75. Настройки Eclipse

Чтобы импортировать проект из меню Eclipse, выберите «Файл» -> «Импорт». Затем в мастере импорта файлов выберите Общие -> Существующие проекты в рабочую область, как показано ниже.

Диалог импорта проекта

Рисунок 76. Диалог импорта проекта.

На следующей странице мастера выберите Выбрать архивный файл: и найдите, где находится Duck_Android.zip в вашей файловой системе. Окно Projects будет автоматически заполнено, если проект Duck уже выбран. Это показано ниже. Нажмите кнопку Готово, чтобы завершить импорт.

Импорт проекта

Рисунок 77. Импорт проекта.

Eclipse создаст приложение автоматически после импорта. Теперь вы должны увидеть проект Duck в Project Explorer.

Обратите внимание, что в этот момент вы можете получить сообщение об ошибке: «Для Android требуется уровень соответствия компилятора 5.0 или 6.0. Вместо этого найдено« 1.4 ». Используйте Инструменты Android> Исправить свойства проекта». Это результат неправильной версии компилятора Java, назначенной вашему проекту. Чтобы устранить проблему, выделите проект в Project Explorer и в контекстном меню выберите «Свойства». Вы увидите свойства для утки. Выделите Java-компилятор и установите флажок Включить параметры, специфичные для проекта. Под уровнем соответствия компилятора вы должны выбрать 1.6, как показано ниже. Нажмите ОК.

Компилятор Java

Рисунок 78. Компилятор Java.

На этом этапе проект Duck должен успешно скомпилироваться. При необходимости в меню Eclipse выберите «Проект -> Очистить …», чтобы заставить Eclipse скомпилировать проект.

После первоначальной компиляции проекта нам нужно собрать собственные библиотеки. Вам необходимо отредактировать Application.mk для правильных настроек сборки. В Project Explorer дважды щелкните файл Application.mk в папке jni, как показано ниже, чтобы открыть его в редакторе.

Папка JNI

Рисунок 79. Папка JNI.

Вам необходимо установить следующие параметры конфигурации:

  • APP_PROJECT_PATH: = / cygdrive / <Project_Unix_Path>
  • APP_PROJECT_LIBPATH: = <Project_Windows_Path>

Во-первых, <Project_Windows_Path> — это путь к вашему проекту Duck в Eclipse, где обратная косая черта (\) заменяется прямой косой чертой (/). В Eclipse Project Explorer выделите Duck (корневая папка верхнего уровня для проекта) и в контекстном меню выберите «Свойства». В диалоговом окне «Свойства» выделите «Ресурс» слева. В атрибуте Location вы увидите путь Windows к вашему проекту Duck. Введите его в качестве значения APP_PROJECT_LIBPATH, заменив обратную косую черту косой чертой, т.е. APP_PROJECT_LIBPATH: = D: / eclipse / DuckTest / Duck

Свойство Ресурса

Рисунок 80. Свойство ресурса.

<Project_Unix_Path> идентичен <Project_Windows_Path>, за исключением того, что двоеточие (:) удалено, то есть D / eclipse / DuckTest / Duck. В результате APP_PROJECT_PATH: = / cygdrive / D / eclipse / DuckTest / Duck

Окончательная версия Application.mk показана ниже. Не забудьте сохранить его.

Application.mk

Рисунок 81. Application.mk

Теперь откройте консоль Cygwin. Измените каталог на значение APP_PROJECT_PATH, например, cd / cygdrive / D / eclipse / DuckTest / Duck.

Если вы наберете «ls», вы увидите файлы и папки прямо под корневой утилитой проекта, вот так:

1
2
3
4
5
$ ls
AndroidManifest.xml build.xml lint.xml project.properties
ant.properties gen local.properties res
assets jni obj src
bin libs proguard.cfg

Теперь мы вызовем утилиту Android NDK ndk-build для сборки библиотек. Например, если Android NDK установлен в D: \ android-ndk-r7-windows, то в консоли Cygwin введите полный путь к утилите ndk-build: $ / cygdrive / d / android-ndk-r7- окна / NDK-сборка. Это должно собрать библиотеки без ошибок.

Последние несколько строк вывода консоли из процесса сборки показаны ниже.

Консольный выход

Рисунок 82. Вывод на консоль.

Теперь вернитесь в Eclipse, выберите папку проекта верхнего уровня Duck и в контекстном меню выберите «Обновить». Мы должны сделать это, потому что сборка была выполнена вне Eclipse, и поэтому Eclipse должен быть уведомлен об изменениях в папках проекта.

Далее нам нужно скопировать два файла:

  • В Project Explorer откройте эти папки:
    • Утка -> obj -> местный -> armeabi
    • Утка -> obj -> местный -> armeabi-v7a
    • Утка -> libs -> armeabi
    • Утка -> libs -> armeabi-v7a
  • Скопируйте libopenal.so из Duck -> obj -> local -> armeabi в Duck -> libs -> armeabi.
  • Скопируйте libopenal.so из Duck -> obj -> local -> armeabi-v7a в Duck -> libs -> armeabi-v7a.

Eclipse создаст проект Duck автоматически после копирования / вставки.

Наконец, нам нужно скопировать еще два файла. Одним из недостатков использования ShiVa Authoring Tool для ОС Android является то, что он ограничивает размер иконки запуска до 48×48 пикселей, а изображение заставки — до 480×800 пикселей. Когда вы настраиваете проект через Eclipse, вы можете заменить значок запуска и изображение заставки файлами, которые имеют больший размер пикселя, более подходящий для планшета. По этой причине мы предоставили app_icon_72x72.png и app_splash_800x1280.png в наборе 2 архива загрузки. Переименуйте эти файлы как app_icon.png и app_splash.png, соответственно, и в Project Explorer скопируйте их в Duck -> res -> drawable папку. Это перезаписывает исходные app_icon.png и app_splash.png.

Eclipse создаст проект Duck автоматически после копирования / вставки (в файле Duck_Android_final.zip, который является частью архива загрузки, папка Duck -> res -> drawable уже имеет значок запуска размером 72×72 пикселей и размер 800×1280 пикселей всплеск изображения).

Нам нужно отредактировать Duck.java, чтобы подключить системный вызов openURL к функции вибрации на устройстве Android. В Project Explorer дважды щелкните Duck -> src -> com.shiva3d.demo -> Duck.java, чтобы открыть его в редакторе. Сделайте следующие два изменения. В файле Duck_Android_final.zip, который является частью архива загрузки, эти изменения уже внесены.

1. Измените функцию onVibrate, чтобы установить длительность 100 миллисекунд:

01
02
03
04
05
06
07
08
09
10
11
private static void onVibrate ( boolean b )
{
  if ( b )
  {
    oVibrator.vibrate ( 100 ) ;
  }
  else
  {
    oVibrator.cancel ( ) ;
  }
}

2. Измените onOpenURL для вызова onVibrate.

1
2
3
4
public static void onOpenURL ( String sURL, String sTarget )
{
  onVibrate(true);
}

Сохраните Duck.java и подождите, пока Eclipse завершит компиляцию и сборку. Теперь игра готова к развертыванию на вашем устройстве через Eclipse. Подключите устройство к ПК через USB-кабель. В Project Explorer выберите проект Duck и в контекстном меню «Отладка как» -> «Приложение Android», как показано ниже. Это установит игру на ваше устройство.

Установка игры

Рисунок 83. Установка игры.


В этом разделе мы рассмотрим код Lua, используемый в редакторе ShiVa, и изменения в коде Java, сделанные с помощью Eclipse.

Первый набор кодов, который мы будем проверять, связан с MainAI.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
function MainAI.onInit ( )
———————————————————————————
    application.setCurrentUserScene ( «MyScene» )
    application.setOption ( application.kOptionViewportRotation,3 )
     
    local lCamera = application.getCurrentUserActiveCamera ( )
    object.setTranslation ( lCamera, 6, 7, 5, object.kGlobalSpace )
    object.lookAt ( lCamera, 0, 1.9, -1, object.kGlobalSpace, 1 )
     
    this.egg (scene.getTaggedObject (application.getCurrentUserScene ( ),»egg» ) )
     
    input.enableMultiTouch ( this.getUser ( ), true )
    this.reset()
———————————————————————————
end
———————————————————————————

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

  • application.setCurrentUserScene («MyScene») определяет сцены игры с MyScene, созданной ранее.
  • application.setOption (application.kOptionViewportRotation, 3) поворачивает дисплей на 90 градусов. Вам нужно повернуть устройство, чтобы оно стояло на левой стороне, чтобы играть в игру. Расположенный между руками, левый большой палец пользователя будет находиться около верхней части устройства, а большой палец правой руки будет находиться около нижней части устройства (значение по умолчанию для kOptionViewportRotation равно 0. Чтобы повернуть область просмотра на -90 градусов, установите kOptionViewportRotation = 1, чтобы повернуть на 180 градусов , установите kOptionViewportRotation = 1).
  • Затем мы получаем дескриптор камеры по умолчанию в игре через application.getCurrentUserActiveCamera (). Положение камеры устанавливается через setTranslation (), в (x = 6, y = 7, z = 5). Затем камера указывает на (x = 0, y = 1.9, z = -1) через lookAt (). Эти значения были найдены методом проб и ошибок. Мы хотели расположить камеру как можно ближе к действию, чтобы на переднем плане небольшая часть утки выходила за пределы области, которую может видеть камера.
  • В рамках обработки сенсорных событий мы отправляем события в EggAI.onCaptureInput из MainAI. По этой причине существует локальная переменная типа объекта с именем «egg». Мы получаем дескриптор модели Egg, используя тег, когда он был добавлен на сцену, и инициализируем переменную egg:
    this.egg (scene.getTaggedObject (application.getCurrentUserScene (), «egg»))
  • Затем мы включаем мультитач через enableMultiTouch (this.getUser (), true) и вызываем сброс (). Функция reset () инициализирует переменные, используемые в мультитач-обработке, как мы увидим позже.
1
2
3
4
5
6
7
function MainAI.reset ( )
———————————————————————————
    this.prevTouchCount (0)
    this.prevY0 (0)
———————————————————————————
end
———————————————————————————

Комментарии: Эта функция устанавливает значения prevTouchCount и prevY0 в 0.

1
2
3
4
5
6
function MainAI.requestRestart ( )
———————————————————————————
    application.restart ( )
———————————————————————————
end
———————————————————————————

Комментарии: эта функция вызывает application.restart () для перезапуска игры. Эта функция будет вызвана, когда пользователь захочет перезапустить игру с помощью действия мультитач, которое будет рассмотрено позже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
function MainAI.displayRestart ( )
———————————————————————————
    local lUser = application.getCurrentUser ( )
    local lComp = hud.newComponent (lUser, hud.kComponentTypeLabel)
    if ( lComp ~= nil )
    then
        hud.setComponentPosition (lComp, 50, 50 )
        hud.setComponentSize (lComp, 100, 10 )
        hud.setComponentBackgroundColor (lComp, 0, 0, 0, 0 )
        hud.setComponentBorderColor (lComp, 0, 0, 0, 0 )
        hud.setComponentVisible (lComp, true )
        hud.setComponentZOrder (lComp, 255 )
        hud.setLabelText (lComp, «Game Restarting» )
        hud.setDefaultFont (lUser, «DefaultFont» )
        hud.setLabelTextHeight (lComp, 100 )
        hud.setLabelTextAlignment (lComp, hud.kAlignCenter, hud.kAlignCenter )
    end
———————————————————————————
end
———————————————————————————

Комментарии: Эта функция отображает информационный текст «Перезапуск игры» до перезапуска игры.

  • Мы создаем ярлык через hud.newComponent (lUser, hud.kComponentTypeLabel).
  • Затем мы устанавливаем положение и размер метки через setComponentPosition и setComponentSize, соответственно.
  • Затем мы вызываем setComponentBackgroundColor и setComponentBorderColor для определения цвета фона и границы метки. В каждом случае последний параметр передается как 0, что определяет соответствующий элемент (цвет фона или границы) как полностью прозрачный.
  • Затем мы вызываем setComponentVisible (), чтобы убедиться, что метка видна.
  • Функция setComponentZOrder () определяет, как метка расположена спереди. Параметр должен быть в диапазоне от 0 до 255. Чем больше введенное целое число, тем ближе на переднем плане появится компонентная сеть.
  • Затем мы устанавливаем текст метки «Перезапуск игры».
  • Вызов функции setLabelTextHeight () определяет высоту текста надписи в процентах от высоты самой надписи. Мы установили это на 100%.
  • Наконец, с помощью вызова функции setLabelTextAlignment () мы выравниваем текст по центру метки.

В этой игре нас интересуют два типа сенсорных событий:

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

Независимо от того, движется ли один или два больших пальца, нам нужно иметь дело с последовательными событиями касания. В MainAI мы реализуем сенсорные события, чтобы определить, перемещает ли пользователь один большой палец или два больших пальца. Если один большой палец, EggAI отвечает за действия пользователя. В результате из обработчика событий в MainAI мы вызываем EggAI.onCaptureInput для отправки информации одним движением большого пальца. (Обработчик EggAI.onCaptureInput будет рассмотрен позже.) Если два больших пальца движутся, MainAI перезапускает приложение.

Теперь мы приступим к рассмотрению пользовательских обработчиков сенсорных событий. Необходимо реализовать три обработчика: onTouchSequenceBegin, onTouchSequenceEnd и onTouchSequenceChange. OnTouchSequenceBegin уведомляет ваше приложение о том, что сенсорные события начались. OnTouchSequenceChange предоставляет подробную информацию о количестве «касаний» (касаний) и координатах каждого касания. Наконец, onTouchSequenceEnd уведомляет о завершении события касания.

Обратите внимание, что эти обработчики полезны только в том случае, если на устройстве включена поддержка мультитач, и вы включили мультитач в своем приложении (см. Выше на Init).

Давайте сначала посмотрим на onTouchSequenceBegin и onTouchSequenceEnd. Как видно из листингов кода ниже, оба обработчика вызывают функцию сброса, рассмотренную выше, чтобы установить значения переменных prevTouchCount и prevY0 в 0.

1
2
3
4
5
6
function MainAI.onTouchSequenceBegin ( )
———————————————————————————
    this.reset ( )
———————————————————————————
end
———————————————————————————

Для onTouchSequenceEnd:

1
2
3
4
5
6
function MainAI.onTouchSequenceEnd ( )
———————————————————————————
    this.reset ( )
———————————————————————————
end
———————————————————————————

Список для onTouchSequenceChange приведен ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function MainAI.onTouchSequenceChange ( nTaps0, nX0, nY0, nTaps1, nX1, nY1, nTaps2, nX2, nY2, nTaps3, nX3, nY3, nTaps4, nX4, nY4 )
———————————————————————————
    local touchCount = 0
    if(nTaps0 > 0) then touchCount = 1 end
    if(nTaps1 > 0) then touchCount = 2 end
    if(nTaps2 > 0) then touchCount = 3 end
    if(nTaps3 > 0) then touchCount = 4 end
    if(nTaps4 > 0) then touchCount = 5 end
 
    if ( touchCount == this.prevTouchCount())
    then
        if ( touchCount == 1 )
        then
            if(nY0 > this.prevY0())
            then
                local d = -1
                object.sendEvent ( this.egg ( ), «EggAI», «onCaptureInput», d )
            elseif(nY0 &lt; this.prevY0())
            then
                local d = 1
                object.sendEvent ( this.egg ( ), «EggAI», «onCaptureInput», d )
            end
        elseif(touchCount == 2)
        then
        if(math.abs (nY0 — nY1) > 0.2)
            then
                this.displayRestart ( )
                this.postEvent (1,»requestRestart» )
            end
        end
    end
     
    — store this event’s variables for the next event
    this.prevTouchCount (touchCount)
    this.prevY0 (nY0)
———————————————————————————
end
———————————————————————————

Давайте сначала объясним параметры в обработчике событий.

  • Если на экране только одно касание, nTaps0 равно 1, а nTapsi = -1, i = 1, …, 4
  • Если на экране только два касания, nTaps0, nTaps1 равны 1, а nTapsi = -1, i = 2, …, 4
  • Если на экране только три касания, nTaps0, nTaps1 и nTaps2 равны 1, а nTapsi = -1, i = 3, 4

В зависимости от количества нажатий, nXi, nYi дают координаты соответствующего ответвления. Например, если есть два ответвления, (nX0, nY0) и (nX1, nY1) являются координатами первого и второго ответвлений. Обратите внимание, что структура гарантирует, что координаты находятся в последовательности с предыдущим вызовом onTouchSequenceChange. Другими словами, давайте предположим, что существует последовательность двойного касания, то есть nTaps0, nTaps1 равны 1, а nTapsi = -1, i = 2, …, 4. Вы двигаете большими пальцами по экрану. Между onTouchSequenceBegin и onTouchSequenceEnd каждый вызов onTouchSequenceChange будет обеспечивать (nX0, nY0) уникальное движение одного из ваших больших пальцев и аналогично для (nX1, nY1).

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

С этой информацией, давайте попробуем понять код.

  • Переменная touchCount хранит количество нажатий. После выполнения оператора «if (nTaps4> 0) then touchCount = 5 end» мы определили количество нажатий.
  • Переменная prevTouchCount хранит количество нажатий, которые мы получили при предыдущем вызове onTouchSequenceChange. Если предыдущее и текущее количество нажатий равно, мы продолжаем обрабатывать информацию.
  • Если количество нажатий (touchCount) равно 1, то пользователь должен перемещать яйцо. Информация о координатах (nX0, nY0) относится к двумерному экранному пространству. Если текущая координата касания по оси Y больше, чем предыдущая, то движение касания происходит в положительной оси Y в двумерном пространстве экрана. Это подразумевает, что направление движения прикосновения направлено в сторону отрицательной оси z в трехмерном пространстве (см. Рисунок ниже). В результате нам нужно вызвать EggAI.onCaptureInput, передав -1 в качестве входного параметра.
    • Вызов функции осуществляется через:
      object.sendEvent (this.egg (), «EggAI», «onCaptureInput», d). Здесь this.egg () возвращает дескриптор экземпляра модели Egg. EggAI ссылается на AIModel, присоединенную к модели Egg, а onCaptureInput ссылается на пользовательский обработчик, который мы хотим вызвать.
  • Если текущая координата касания по оси Y меньше, чем предыдущая, то движение касания происходит по отрицательной оси Y в двумерном пространстве экрана. Мы выполняем шаги, описанные выше, чтобы вызвать EggAI.onCaptureInput, передав 1 в качестве входного параметра. Это перемещает яйцо в положительном направлении вдоль оси Z в трехмерном пространстве.
  • Если количество нажатий равно 2, то пользователь должен перезапустить приложение. Однако иногда, даже когда пользователь перемещает один большой палец на экране, мы можем получить событие двойного касания. Чтобы отфильтровать такие ошибочные условия, мы проверяем разницу между y-координатами двух событий касания. Обратите внимание, что всегда верно, что -1 <= nYi <= 1, i = 0,1. Мы запускаем перезапуск игры только в том случае, если абсолютное значение разности между nY0 и nY1 составляет 0,2, то есть 10% от всей длины двумерной координаты Y. (Экспериментально, если событие двойного касания запускается одним большим пальцем, точки касания на оси Y будут меньше разнесены.)
  • Чтобы перезапустить игру, мы сначала вызываем displayRestart() рассмотренный ранее. Эта функция отображает на экране сообщение «Перезапуск игры», чтобы уведомить пользователя о перезапуске игры.
  • Затем выполняется оператор this.postEvent(1,"requestRestart") . Первый аргумент postEvent указывает задержку в секундах, а вторым параметром является имя функции, которая будет выполнена после указанной задержки. Другими словами, мы просим, ​​чтобы функция requestRestart, которая была рассмотрена выше, была выполнена с задержкой в ​​одну секунду. Это приводит к тому, что информационное сообщение будет отображаться на экране в течение одной секунды до перезапуска игры.
  • Наконец, мы сохраняем touchCount и nY0 в prevTouchCount и prevY0 соответственно, чтобы использовать их при следующем вызове onTouchSequenceChange. (Этот шаг не важен, если игра перезапускается.)
Двумерные и Трехмерные Оси.

Рисунок 84. Двумерные и трехмерные оси.

Теперь мы проверим код для DuckAI.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function DuckAI.onInit ( )
———————————————————————————
    object.setTranslation ( this.getObject ( ), 2, 3, 2, object.kGlobalSpace )
    local lUser = application.getCurrentUser ( )
    local lComp = hud.newComponent (lUser, hud.kComponentTypeLabel, «HUD.disp» )
    if ( lComp ~= nil )
    then
        hud.setComponentPosition ( lComp, 75, 7 )
        hud.setComponentSize ( lComp, 30, 10 )
        hud.setComponentBackgroundColor ( lComp, 0, 0, 0, 0 )
        hud.setComponentBorderColor ( lComp, 0, 0, 0, 0 )
        hud.setComponentVisible ( lComp, true )
        hud.setComponentZOrder ( lComp, 255 )
        hud.setLabelText ( lComp, string.format («Score: %2i»,this.score ( ) ) )
        hud.setDefaultFont ( lUser, «DefaultFont» )
        hud.setLabelTextHeight ( lComp, 100 )
        hud.setLabelTextAlignment ( lComp, hud.kAlignCenter, hud.kAlignCenter )
    end
———————————————————————————
end
———————————————————————————

Комментарии: в этом обработчике мы инициализируем переменные для модели Утки.

  • Вызов функции object.setTranslation помещает Duck в (x = 2, y = 3, z = 2) в глобальном пространстве.
  • Остальная часть кода устанавливает hud для отображения счета пользователя. Здесь мы создаем текстовую метку, видимую на экране.
  • Инструкция ‘hud.newComponent (lUser, hud.kComponentTypeLabel, «HUD.disp»)’ создает дескриптор объекта для метки. Обратите внимание, что мы даем компоненту имя «HUD.disp». Мы получим доступ к компоненту в обработчике onSensorCollisionBegin () позже, используя это имя.
  • Если метка успешно создана, мы устанавливаем различные свойства метки.
    Мы указываем позицию, чтобы быть (х = 75, у = 7). Обратите внимание, что setComponentPosition () требует 0 <= x <= 100, 0 <= y <= 100.
    Размер метки установлен равным 30 вдоль оси x и 10 вдоль оси y. SetComponentSize () требует 0 <= x <= 100, 0 <= y <= 100.
  • Затем мы устанавливаем цвет фона и границы метки. В обеих функциях setComponentBackgroundColor () и setComponentBorderColor () последний параметр передается как 0, что определяет соответствующий элемент (цвет фона или границы) как полностью прозрачный.
  • Затем мы вызываем setComponentVisible (), чтобы убедиться, что метка видима.
  • Функция setComponentZOrder () определяет, как метка расположена спереди. Параметр должен быть между 0 и 255, и больше, чем параметр, компонент находится дальше на переднем плане.
  • Затем мы устанавливаем текст метки. Здесь мы добавляем значение переменной Score к постоянному тексту «Score:» и форматируем его как двузначное число.
  • Вызов функции setLabelTextHeight () определяет высоту текста надписи в процентах от высоты самой надписи. Мы установили это на 100%.
  • Мы наконец выравниваем текст по центру метки.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
function DuckAI.onEnterFrame ( )
———————————————————————————
    local lObject = this.getObject ( )
    if(this.isPositive ( ))
    then
        object.rotateAroundTo( lObject, 0,0,0,0,0.75,0, object.kGlobalSpace,0.5)
    else
        object.rotateAroundTo( lObject, 0,0,0,0,-0.75,0, object.kGlobalSpace,0.5)
    end
     
    local x,y,z = object.getRotation(lObject,object.kLocalSpace)
    object.setRotation (lObject,x+0.3,y+0.5,z+1, object.kLocalSpace )
———————————————————————————
end
———————————————————————————

Комментарии: этот обработчик вращает утку в глобальном и локальном пространствах. В глобальном пространстве утка вращается только вокруг оси y, на фиксированной плоскости, определяемой y = 3 (начальное значение для координаты y утки) в направлении по часовой стрелке или против часовой стрелки. Если булева переменная isPositive имеет значение true (изначально имеет место), вращение происходит по часовой стрелке и наоборот.

  • Сначала обработчик получает дескриптор объекта для утки через this.getObject ().
  • Вызов функции rotateAroundTo определяет глобальное вращение:
    • Для вращения по часовой стрелке object.rotateAroundTo (lObject, 0,0,0,0,0.75,0, object.kGlobalSpace, 0.5) указывает, что вращение происходит вокруг начала координат (x = 0, y = 0, z = 0, первые три параметра, следующие за дескриптором объекта), а величина поворота равна (x = 0, y = 0.75, z = 0)
    • Конечный параметр, коэффициент гладкости, всегда находится в диапазоне от 0 до 1 и установлен на 0,5.
  • Для вращения против часовой стрелки вызов функции такой же, за исключением того, что величина вращения вокруг оси у равна -0,75.
  • Для вращения вокруг локального пространства мы сначала получаем текущие значения вращения через object.getRotation (lObject, object.kLocalSpace).
  • Затем мы увеличиваем эти значения на (0.3,0.5,1) вокруг локальной (x, y, z) оси и устанавливаем новое вращение через object.setRotation (lObject, x + 0.3, y + 0.5, z + 1, object .kLocalSpace). Это соответствует повороту, описанному на рисунке 3.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
function DuckAI.onSensorCollisionBegin ( nSensorID, hTargetObject, nTargetSensorID )
———————————————————————————
    this.score ( this.score ( )+1)
    local lComp = hud.getComponent (application.getCurrentUser ( ) , «HUD.disp» )
    if(lComp)
    then
        hud.setLabelText (lComp, string.format («Score: %2i»,this.score ( ) ) )
    end
    if(this.isPositive ( ))
    then
        this.isPositive (false )
    else
        this.isPositive (true )
    end
     
    system.openURL («http://www.stonetrip.com»,»»)
———————————————————————————
end
———————————————————————————

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

  • Оператор this.score (this.score () +1) увеличивает переменную с именем score.
  • Затем мы получаем указатель на компонент hud с именем ‘HUD.disp’ и отображаем новый счет.
  • Затем направление вращения, сохраненное в переменной isPositive, переключается.
  • Наконец, существует системный вызов system.openURL («http://www.stonetrip.com», «»). Реализация этого вызова функции по умолчанию состоит в том, чтобы открыть веб-браузер на устройстве, чтобы посетить URL, переданный в качестве параметра. Во время настройки в Eclipse обработчик по умолчанию будет перезаписан для вибрации устройства. Примечание. Если вы не хотите вибрировать устройство, просто удалите эту строку. Затем вы можете собрать и установить игру на Android-устройство напрямую с помощью инструмента Shiva Authoring, не настраивая его в Eclipse.

Теперь мы проверим код EggAI.

1
2
3
4
5
6
function EggAI.onInit ( )
———————————————————————————
    object.setTranslation ( this.getObject ( ), 0, 3, 0, object.kGlobalSpace )
———————————————————————————
end
———————————————————————————

Комментарии: Здесь мы устанавливаем начальную позицию яйца в глобальном пространстве через object.setTranslation (this.getObject (), 0, 3, 0, object.kGlobalSpace). Это соответствует (x = 0, y = 3, z = 0).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function EggAI.onEnterFrame ( )
———————————————————————————
    local lObject = this.getObject ( )
     
    local x,y,z = object.getRotation(lObject,object.kLocalSpace)
    object.setRotation (lObject,x+1,y,z, object.kLocalSpace )
     
    local xp, yp, zp = object.getTranslation ( this.getObject ( ) , object.kGlobalSpace )
    if(zp &lt; -5)
    then
        this.isBack (false )
    elseif(zp &gt; 5)
    then
        this.isBack (true )
    end
     
    if(this.isBack ( ))
    then
        object.setTranslation ( this.getObject ( ) , 0, 3, zp-0.005-math.abs ( this.jump ( ) ), object.kGlobalSpace )
    else
        object.setTranslation ( this.getObject ( ) , 0, 3, zp+0.005+math.abs ( this.jump ( ) ), object.kGlobalSpace )
    end
    this.jump (0 )
———————————————————————————
end
———————————————————————————

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

  • Вращение вокруг локальной оси X.
  • Движение по прямой линии, определенной (x = 0, y = 3), параллельной оси z. Это движение ограничено двумя точками (x = 0, y = 3, z = 5) спереди и (x = 0, y = 3, z = -5) сзади.

Код можно интерпретировать следующим образом.

  • Сначала мы получаем дескриптор утки через локальный lObject = this.getObject ().
  • Далее мы определим вращение яйца в его локальном пространстве:
    • local x, y, z = object.getRotation (lObject, object.kLocalSpace) дает нам текущий поворот в локальном пространстве
    • object.setRotation (lObject, x + 1, y, z, object.kLocalSpace) устанавливает новый поворот в локальном пространстве, изменяя только значение для оси x. (Это соответствует повороту на рисунке 4.)
  • Затем определяется движение по прямой. Здесь действуют два разных фактора: автоматическое движение яйца и движение, вызванное событием касания пользователя.
  • Яйцо будет автоматически перемещаться вдоль линии, назад и вперед, независимо от события касания пользователя. Количество движения, вносимого автоматическим движением, фиксировано и установлено равным 0,005 по величине. Логическая переменная isBack определяет направление движения. Если isBack имеет значение true, движение в направлении отрицательной оси Z и наоборот.
  • Чтобы убедиться, что яйцо остается между двумя граничными точками (x = 0, y = 3, z = 5) и (x = 0, y = 3, z = -5), направление движения переключается, когда яйцо достигает одна из этих граничных точек.
  • Числовая переменная jump представляет количество движения, вызванное событием касания пользователя. В дополнение к значению прыжка, сенсорное событие пользователя также влияет на значение isBack. Манипулирование переменными jump и isBack на основе события касания пользователя производится в onCaptureInput, который рассматривается ниже.
  • Наконец, значение прыжка устанавливается в ноль, как только вклад события касания учитывается в движении.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
function EggAI.onCaptureInput ( d )
———————————————————————————
    if(d==1)
    then
        this.jump ( 0.1)
        this.isBack ( false)
    elseif(d==-1)
    then
        this.jump ( -0.1)
        this.isBack ( true)
    end
———————————————————————————
end
———————————————————————————

Комментарии:
Это пользовательский обработчик, в котором значения переменных jump и isBack обрабатываются на основе события касания пользователя.

  • Обработчик принимает числовой параметр d, который должен быть 1 или -1, в соответствии с направлением события касания. Если d равно 1, предполагается, что направление события касания направлено в сторону положительной оси z; если d равно -1, предполагается, что направление события касания направлено в сторону отрицательной оси z.
  • Значение isBack устанавливается в соответствии со значением d.
  • Для простоты значение прыжка всегда устанавливается на фиксированное значение 0,1. Сравните 0,1 с 0,005, что является значением автоматического движения (см. OnEnterFrame). Обратите внимание, что событие касания пользователя обеспечивает мгновенный стимул в 20 раз больше, чем автоматическое движение яйца. Мы определили эти значения методом проб и ошибок, чтобы найти компромисс между скоростью и плавностью движения. Если целевое устройство может поддерживать высокую частоту кадров, вы можете увеличить эти значения, чтобы ускорить движение яйца. В устройстве, которое не поддерживает высокие частоты кадров, слишком большое увеличение этих значений может привести к потере плавности, поскольку яйцо будет двигаться с перерывами, хотя и быстро.

В этом разделе мы рассмотрим настройки, сделанные в Duck.java

01
02
03
04
05
06
07
08
09
10
11
private static void onVibrate ( boolean b )
{
  if ( b )
  {
    oVibrator.vibrate ( 100 ) ;
  }
  else
  {
    oVibrator.cancel ( ) ;
  }
}

Комментарии: Переменная oVibrator является экземпляром класса android.os.Vibrator и инициализируется в методе Duck.onCreate (). Он используется для управления вибратором на устройстве. Если логический входной параметр имеет значение true, вибратор активируется на 100 миллисекунд.

1
2
3
4
public static void onOpenURL ( String sURL, String sTarget )
{
  onVibrate(true);
}

Это системный вызов для открытия URL в браузере устройства. Поведение по умолчанию — запуск нового действия на основе намерения типа Intent.ACTION_VIEW. Мы перезаписываем реализацию по умолчанию для вызова onVibrate.


В этом руководстве рассматривается разработка 3D-игр для ОС Android с использованием пакета ShiVa3D Suite. Однако с небольшими изменениями вы можете создать игру для устройств на iOS, iPhone / iPod Touch или iPad / iPad2. На высоком уровне нужно будет выполнить следующие шаги, чтобы портировать игру на iOS.

  • Редактор ShiVa:
    • Для простоты предположим, что устройство не будет вибрировать, когда утка и яйцо столкнутся. По этой причине удалите system.openURL («http://www.stonetrip.com», «») в функции onSensorCollisionBegin.
    • При экспорте игры выберите iOS для пакета времени выполнения.
  • Авторский инструмент Шивы:
    • Shiva Authoring Tool необходимо запускать в среде Mac OS.
    • На начальной странице Shiva Authoring Tool выберите вкладку iPhone для iPhone / iPod Touch или вкладку iPad для iPad / iPad2.
    • Поставьте значок запуска и изображения заставки с размерами, подходящими для устройства iOS. Например, для iPhone / iPod Touch значок запуска и изображения заставки будут 114×114 пикселей и 640×960 пикселей соответственно. Для iPad / iPad2 значок запуска и изображения заставки будут 72×72 пикселей и 768×1024 пикселей соответственно.
    • Экраны Shiva Authoring Tool немного отличаются для устройств iOS. Некоторые параметры, доступные для авторизации под ОС Android, недоступны для авторизации под iOS и наоборот. В частности, вы не можете напрямую установить игру на подключенное устройство iOS. Вместо этого на шаге 3 выберите тип сборки в качестве разработки, чтобы создать проект xCode. Затем создайте проект xCode на своем компьютере Mac OS, как если бы вы делали это с другими проектами xCode, и установите игру на свое устройство через xCode.
    • Кроме того, вам необходимо иметь действительный профиль обеспечения при установке приложения на устройство iOS через xCode. Профиль обеспечения и подписывающий идентификатор будут предоставлены Shiva Authoring Tool на шаге 2. (На этом шаге также выберите тип Authoring в качестве проекта.)
  • Инструмент Eclipse никогда не используется для разработки под iOS. Для любой настройки кода используйте среду xCode перед развертыванием конечного продукта на устройстве iOS.

Подробное исследование использования ShiVa3D Suite для разработки игр для iOS приведено в другом руководстве для мобильных tuts + ( https://mobile.tutsplus.com/tutorials/android/create-a-3d-flight-simulator-app-for-ios-and -Android-премиум ). Для получения дополнительной информации см. Эту ссылку.


В этом уроке мы обсуждали разработку 3D-игр для Android с использованием ShiVa3D Suite. Двумя основными элементами в этом наборе являются ShiVa Editor и ShiVa Authoring Tool. Редактор ShiVa является ключевым компонентом для разработки и тестирования 3D-игр с нуля. Игра, разработанная с помощью ShiVa Editor, может быть развернута на разных устройствах с разными операционными системами после ее создания с помощью ShiVa Authoring Tool.

ShiVa Authoring Tool используется для преобразования игры, созданной с помощью ShiVa Editor, в целевую операционную систему. Для ОС Android мы обсудили два возможных варианта создания: Project и APK Package. Используя тип разработки Project, ShiVa Authoring Tool генерирует проект Eclipse с кодом Java и C / C ++, что позволяет разработчику дополнительно настраивать приложение перед его развертыванием на целевых устройствах. В рамках этого урока мы показали, как вибрировать устройство с помощью обработчика событий, переопределяя поведение по умолчанию системного вызова openURL. Другой тип разработки, APK Package, генерирует файл Android apk, который можно легко развернуть на целевом устройстве. Пакет APK — это правильный тип авторинга, если вам не нужны какие-либо настройки игры.

Для тех, кто заинтересован в дальнейшем чтении, вот несколько ссылок на базу знаний ShiVa3D.

Некоторые заключительные комментарии следующие.

  • Анимация сложной трехмерной модели может потребовать значительных возможностей процессора. Поэтому вы должны тщательно определять свои модели и анимацию с учетом целевых устройств.
  • Целевое устройство должно быть способно отображать игру с приемлемой частотой кадров. В связи с этим сложные детали в 3D-модели могут жертвовать производительностью. Кроме того, некоторые детали могут фактически не отображаться в устройствах с небольшими размерами экрана, например в телефонах. Попробуйте использовать модель, которая имеет правильный баланс между визуальными деталями и производительностью. Вам не нужно больше детализации в 3D-модели, чем то, что мобильное устройство может поддерживать с точки зрения его разрешения и размеров экрана.
  • Некоторые функции, такие как физическая клавиатура, могут быть доступны не на всех устройствах. Если вы разрабатываете игру на основе ввода с клавиатуры, предоставьте запасной вариант для устройств, на которых нет клавиатуры. Например, обрабатывайте сенсорные события в дополнение к ключевым событиям, чтобы в игру можно было играть на большем количестве устройств.