Starter классы и конфигурация

Для каждой платформы должен быть написан Starter класс. Этот класс создает экземпляр конкретной back-end реализации Application и ApplicationListener реализует логику приложения. Starter классы зависят от платформы. Далее будет рассмотрено, как создать экземпляр и настроить его для каждого back-end.

Эта статья предполагает, что вы ознакомлены с инструкциями настройки проекта и имеете импортированные Core, Desktop, Android и HTML5 проекты в Eclipse.

Desktop (LWJGL)

При открытии Main.java класса в my-gdx-game проекте можно увидеть следующее:

package com.me.mygdxgame;

import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;

public class Main {
   public static void main(String[] args) {
      LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
      cfg.title = "my-gdx-game";
      cfg.useGL20 = false;
      cfg.width = 480;
      cfg.height = 320;
                
      new LwjglApplication(new MyGdxGame(), cfg);
   }
}

Сначала создается экземпляр LwjglApplicationConfiguration. Этот класс позволяет задать различные параметры конфигурации, такие как начальный размер экрана, использование OpenGL ES 1.x или 2.0 и так далее.

Как только указан объект конфигурации, создается экземпляр LwjglApplication. MyGdxGame класс представляет реализацию ApplicationListener и логики приложения.

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

Android

Android приложение не использует main() метод как точку входа, но вместо этого требуется Activity. Откройте MainActivity.java класс в my-gdx-game-android проекте.

package com.me.mygdxgame;

import android.os.Bundle;

import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;

public class MainActivity extends AndroidApplication {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
        cfg.useGL20 = false;
        
        initialize(new MyGdxGame(), cfg);
    }
}

Главная точка входа - onCreate() метод Activity класса. Обратите внимание, что MainActivity наследуется от AndroidApplication класса, который сам наследуется от Activity. Как и для Desktop starter класса задается конфигурация через создание экземпляра класса AndroidApplicationConfiguration. После настройки вызывается initialize() метод класса AndroidApplication и ему передается ApplicationListener(MyGdxGame).

Android приложения могут иметь несколько Activity. libGDX игры обычно должны состоять из одной Activity. Разные экраны игры реализуются с помощью libGDX, а не как отдельные Activity. Причина этого в том, что создание новой Activity предполагает создание нового OpenGL контекста, который занимает много времени и вызывает перезагрузку всех графических ресурсов.

Файл AndroidManifest.xml

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me.mygdxgame"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="landscape"
            android:configChanges="keyboard|keyboardHidden|orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Целевая версия SDK

Крайне важно задать targetSdkVersion больше или равной 6, если приложение должно работать на версии выше, чем 1.5. Если этот атрибут не задан, то приложение запуститься с высшей версией Android в режиме совместимости. Размеры области рисования будут меньше чем фактическое разрешение экрана.

Ориентация экрана и изменение конфигурации

В дополнение к targetSdkVersion должны быть установлены атрибуты screenOrientation и configChanges.

ScreenOrientation атрибут определяет фиксированную ориентацию экрана приложения. Можно не задавать, если приложение может работать в landscape и portrait режиме.

ConfigChanges атрибут имеет важное значение и всегда должен быть задан как указано выше. Отсутствие этого атрибута означает, что приложение будет перезагружаться каждый раз, когда показывается или скрывается физическая клавиатура или изменяется ориентация устройства. Если ScreenOrientation атрибут не задан, то libGDX приложение будет принимать вызовы ApplicationListener.resize() метода, чтобы уведомить об изменении ориентации.

Права и разрешения

Если приложение должно иметь возможность записи во внешнее хранилище устройства (например, на SD карту), иметь доступ в интернет, использовать вибратор, не давать экрану входить в спящий режим, записывать звук, то нужно добавить следующие разрешения в AndroidManifest.xml файл:

<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>  

Обычно пользователи относятся с подозрением к приложению с множеством разрешений.

Для того, чтобы работал экран блокировки, нужно установить AndroidApplicationConfiguration.useWakeLock в true значение.

Если игра не нуждается в доступе к акселерометру или компасу, то рекомендуется отключить их, установив useAccelerometer и useCompass поля AndroidApplicationConfiguration в значение false.

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

Живые обои

libGDX имеет простой в использовании способ создавать живые обои для Android. Starter класс для живых обоев называется AndroidLiveWallpaperService, вот простой пример:

package com.mypackage;

// imports snipped for brevity 

public class LiveWallpaper extends AndroidLiveWallpaperService {
        @Override
        public ApplicationListener createListener () {
                return new MyApplicationListener();
        }

        @Override
        public AndroidApplicationConfiguration createConfig () {
                return new AndroidApplicationConfiguration();
        }

        @Override
        public void offsetChange (ApplicationListener listener, float xOffset, float yOffset, float xOffsetStep, float yOffsetStep,
                int xPixelOffset, int yPixelOffset) {
                Gdx.app.log("LiveWallpaper", "offset changed: " + xOffset + ", " + yOffset);
        }
}

Методы createListener() и createConfig() будут вызываться, когда живые обои показываются в просмотре или при их создании на главном экране.

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

В дополнение к Starter классу, вы также должны создать XML файл, описывающий ваши обои. Назовите файл, к примеру, livewallpaper.xml. Создайте директорию с именем XML в res директории Android проекта и поместите в нее файл (res/xml/livewallpaper.xml). Вот пример содержимого файла:

<?xml version="1.0" encoding="utf-8"?>
<wallpaper
       xmlns:android="http://schemas.android.com/apk/res/android"  
       android:thumbnail="@drawable/ic_launcher"
       android:description="@string/description"
       android:settingsActivity="com.mypackage.LivewallpaperSettings"/>

Здесь определяется миниатюра для окна выбора живых обоев, описание и Activity, которая будет показана, когда пользователь вызовет меню настроек в окне выбора обоев. Эта должна быть обычная Activity, которая имеет несколько виджетов для изменения настроек, таких как цвет заднего фона и подобные вещи. Вы можете сохранить эти настройки в SharedPreferences и загрузить их позже в ApplicationListener живых обоях через Gdx.app.getPreferences() метод.

Наконец, вы должны будете добавлять некоторые вещи в AndroidManifest.xml файл. Вот пример для приложения живых обоев с Activity простых настроек:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.mypackage"
      android:versionCode="1"
      android:versionName="1.0"
      android:installLocation="preferExternal">
        <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14"/>     
        <uses-feature android:name="android.software.live_wallpaper" />
                
        <application android:icon="@drawable/icon" android:label="@string/app_name">
                <activity android:name=".LivewallpaperSettings" 
                                  android:label="Livewallpaper Settings"/>
                
                <service android:name=".LiveWallpaper"
            android:label="@string/app_name"
            android:icon="@drawable/icon"
            android:permission="android.permission.BIND_WALLPAPER">
            <intent-filter>
                <action android:name="android.service.wallpaper.WallpaperService" />
            </intent-filter>
            <meta-data android:name="android.service.wallpaper"
                android:resource="@xml/livewallpaper" />
        </service>                                      
        </application>
</manifest> 

Манифест определяет:

  • Использование особенности живых обоев, смотрите uses-feature.
  • Разрешения для привязки обоев, смотрите android:permission
  • Activity для настроек.
  • Сервис живых обоев, смотрите meta-data.
Живые обои в Android

Живые обои поддерживаются, только начиная с версии Android 2.1 (SDK версия 7).

Живые обои имеют некоторые ограничения относительно сенсорного ввода. В целом только tap и drop будут сообщаться. Установите AndroidApplicationConfiguration # getTouchEventsForLiveWallpaper флаг на true, для получения всех событий от сенсорного экрана.

Daydream

Начиная с Android 4.2 пользователи могут установить Daydream, который показывается когда устройство находится в режиме ожидания. Daydream схож с заставкой на экране и может отображать такие вещи как фото альбомы и так далее. libGDX позволяет с легкостью создать подобные Daydream.

Starter класс для Daydream называется AndroidDaydream. Вот пример:

package com.badlogic.gdx.tests.android;

import android.annotation.TargetApi;
import android.util.Log;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.badlogic.gdx.backends.android.AndroidDaydream;
import com.badlogic.gdx.tests.MeshShaderTest;

@TargetApi(17)
public class Daydream extends AndroidDaydream {
   @Override
   public void onAttachedToWindow() {
      super.onAttachedToWindow();      
          setInteractive(false);

      AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
      cfg.useGL20 = true;
      ApplicationListener app = new MeshShaderTest();
      initialize(app, cfg);
   }
}

Просто наследуйтесь от AndroidDaydream класса, переопределите onAttachedToWindow метод, установите конфигурацию и ApplicationListener и затем инициализируйте Daydream.

В дополнение к Daydream, вы можете предоставить Activity для настроек, которая позволяет пользователю настроить ваш Daydream. Это может быть обычная Activity или libGDX AndroidApplication. Пример пустой Activity:

package com.badlogic.gdx.tests.android;

import android.app.Activity;

public class DaydreamSettings extends Activity {

}

Activity настроек должна быть указана в metadata сервиса Daydream. Создайте xml файл в res/xml директории Android проекта и укажите Activity:

<dream xmlns:android="http://schemas.android.com/apk/res/android"
   android:settingsActivity="com.badlogic.gdx.tests.android/.DaydreamSettings" />
}

В конце добавьте раздел для Activity настроек в AndroidManifest.xml и описание сервиса Daydream:

<service android:name=".Daydream"
   android:label="@string/app_name"
   android:icon="@drawable/icon"
   android:exported="true">
   <intent-filter>
           <action android:name="android.service.dreams.DreamService" />
           <category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
   <meta-data android:name="android.service.dream"
           android:resource="@xml/daydream" />
</service>

iOS

iOS back-end опирается на использовании Xamarin's MonoDevelop среды разработки и Monotouch для развертывания. Точкой входа Monotouch приложения является AppDelegate, который можно найти в Main.cs классе проекта. Ниже приведен пример:

using System;
using System.Collections.Generic;
using System.Linq;

using MonoTouch.Foundation;
using MonoTouch.UIKit;
using com.badlogic.gdx.backends.ios;
using com.me.mygdxgame;

namespace com.me.mygdxgame
{               
    public class Application
    {
        [Register ("AppDelegate")]
        public partial class AppDelegate : IOSApplication {
            public AppDelegate(): base(new MyGdxGame(), getConfig()) {

            }

            internal static IOSApplicationConfiguration getConfig() {
                IOSApplicationConfiguration config = new IOSApplicationConfiguration();
                config.orientationLandscape = true;
                config.orientationPortrait = false;
                config.useAccelerometer = true;
                config.useMonotouchOpenTK = true;
                config.useObjectAL = true;
                return config;
            }
        }

        static void Main (string[] args)
        {
            UIApplication.Main (args, null, "AppDelegate");
        }
    }
}

Файл Info.plist

Файл Info.plist содержит информацию о конфигурации приложения: ориентация экрана, минимальная версия операционной системы, дополнительные параметры, скриншоты и так далее. Ниже приведет пример:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDisplayName</key>
    <string>my-gdx-game</string>
    <key>MinimumOSVersion</key>
    <string>3.2</string>
    <key>UIDeviceFamily</key>
    <array>
        <integer>2</integer>
        <integer>1</integer>
    </array>
    <key>UIStatusBarHidden</key>
    <true/>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
</dict>
</plist>

Файл convert.properties

Процесс преобразования необходим для создания сборки Monotouch iOS платформы. Эта обработка выполнена как часть шага перед сборкой, когда выполняется операция сборки MonoDevelop. Если вы используете сторонние библиотеки или не стандартные расположение некоторых исходников, то нужно обновить convert.properties файл. Пример файла ниже:

SRC =       ../my-gdx-game/src/
CLASSPATH = ../my-gdx-game/libs/gdx.jar
EXCLUDE   =
IN        = -r:libs/ios/gdx.dll -recurse:target/*.class
OUT       = target/my-gdx-game.dll

Этот файл определяет входные файлы для сборки my-gdx-game.dll.

HTML5 GWT

Главной точкой входа HTML5/GWT приложения является GwtApplication. Откройте GwtLauncher.java в my-gdx-game-html5 проекте:

package com.me.mygdxgame.client;

import com.me.mygdxgame.MyGdxGame;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.backends.gwt.GwtApplication;
import com.badlogic.gdx.backends.gwt.GwtApplicationConfiguration;

public class GwtLauncher extends GwtApplication {
   @Override
   public GwtApplicationConfiguration getConfig () {
      GwtApplicationConfiguration cfg = new GwtApplicationConfiguration(480, 320);
      return cfg;
   }

   @Override
   public ApplicationListener getApplicationListener () {
      return new MyGdxGame();
   }
}

GwtApplication состоит из двух методов: getConfig() и getApplicationListener(). Первый метод должен вернуть экземпляр GwtApplicationConfiguration класса, который определяет различные параметры конфигурации HTML5 приложения. Метод getApplicatonListener для запуска приложения возвращает ApplicationListener.

Файлы модулей

GWT нужен реальный Java код каждого jar файла jar/projtect директории. Кроме того, каждый из файлов должен иметь один модуль определения с gwt.xml суффиксом.

Файл модуля HTML5 проекта может выглядеть следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<module>
   <inherits name='com.badlogic.gdx.backends.gdx_backends_gwt' />
   <inherits name='MyGdxGame' />
   <entry-point class='com.me.mygdxgame.client.GwtLauncher' />
   <set-configuration-property name="gdx.assetpath" value="../my-gdx-game-android/assets" />
</module>

Этим самым описываются наследование от двух модулей (gdx-backends-gwt и основного проекта), а также точка входа (класс GwtLauncher) и пути относительно HTML5 корневой директории, указывающие на assets директорию.

Оба gdx-backend-gwt.jar и основной проект имеют аналогичный файл модуля, описывающий другие зависимости. Вы не можете использовать файлы в jars/projects, которые не содержат файла модуля и исходников.

Дополнительную информацию о модулях и зависимостях смотрите в руководстве GWT разработчика.

Поддержка Reflection

GWT не поддерживает Java reflection по различным причинам. libGDX имеет внутренний слой эмуляции, который генерирует reflection информацию для некоторых внутренних классов. Это означает, что если вы используете JSON сериализацию libGDX, то вы столкнетесь с проблемами. Вы можете решить их, указав для каких пакетов и классов необходимо сгенерировать reflection информацию. Чтобы сделать это, поместите свойства конфигурации в gwt.xml файл GWT проекта следующим образом:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<module>
    ... other elements ...
    <extend-configuration-property name="gdx.reflect.include" value="org.softmotion.explorers.model" />
    <extend-configuration-property name="gdx.reflect.exclude" value="org.softmotion.explorers.model.HexMap" />
</module>

Можно указать несколько пакетов и классов добавив еще extend-configuration-property элементов.

Это экспериментальная возможность, используйте ее на свой страх и риск.

Экран загрузки

libGDX HTML5 приложение загружает все asset находящиеся в gdx.assetpath. Во время процесса загрузки отображается экран загрузки, реализованный с помощью GWT виджета. Если нужно изменить экран загрузки, то просто переопределите метод getPreloaderCallback() в GwtApplication классе. В следующем примере рисуется очень простой экран загрузки, используя Canvas:

long loadStart = TimeUtils.nanoTime();
public PreloaderCallback getPreloaderCallback () {
  final Canvas canvas = Canvas.createIfSupported();
  canvas.setWidth("" + (int)(config.width * 0.7f) + "px");
  canvas.setHeight("70px");
  getRootPanel().add(canvas);
  final Context2d context = canvas.getContext2d();
  context.setTextAlign(TextAlign.CENTER);
  context.setTextBaseline(TextBaseline.MIDDLE);
  context.setFont("18pt Calibri");

  return new PreloaderCallback() {
     @Override
     public void done () {
            context.fillRect(0, 0, 300, 40);
     }

     @Override
     public void loaded (String file, int loaded, int total) {
            System.out.println("loaded " + file + "," + loaded + "/" + total);
            String color = Pixmap.make(30, 30, 30, 1);
            context.setFillStyle(color);
            context.setStrokeStyle(color);
            context.fillRect(0, 0, 300, 70);
            color = Pixmap.make(200, 200, 200, (((TimeUtils.nanoTime() - loadStart) % 1000000000) / 1000000000f));
            context.setFillStyle(color);
            context.setStrokeStyle(color);
            context.fillRect(0, 0, 300 * (loaded / (float)total) * 0.97f, 70);

            context.setFillStyle(Pixmap.make(50, 50, 50, 1));
            context.fillText("loading", 300 / 2, 70 / 2);
     }

     @Override
     public void error (String file) {
            System.out.println("error: " + file);
     }
  };
}

Обратите внимание, что для отображения экрана загрузки можно использовать только чистые GWT объекты, использование libGDX API будет доступно после того, как закончится загрузка.