Libgdx Developer's Guide(Libgdx开发者手册)-6(启动类与配置)

对于每个平台,必须写一个启动类。该类实例化一个特定后台的Application实现和实现了应用逻辑的ApplicationListener。该启动类依赖于平台,让我们看看怎样为每个后台实例化和配置一个启动类。

该篇假定你已经完成了Project Setup中的说明并将生成的核心,桌面,Android 和 HTML5工程导入到了Eclipse。

桌面应用(LWJGL)

打开my-gdx-game中的类 Main.java,显示如下:

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等。请参考该类的Javadocs获取更多信息。

一旦设置好了配置对象,一个LwjglApplication 就被实例化了。MyGdxGame() 类是实现了游戏逻辑的 ApplicationListener 。

由此,就创建了一个窗口并且 ApplicationListener 如 The Life-Cycle  篇描述的那样被调用。

Android

Android 应用不使用 main() 方法作为入口点,取而代之的是一个 Activity。打开my-gdx-game-android项目中的类 MainActivity.java :

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);
    }
}

主入口方法是 Activity 的 onCreate() 方法. 注意, MainActivity 继承自 AndroidApplication,而AndroidApplication继承自Activity。像桌面应用中的启动类一样,创建一个配置实例 (AndroidApplicationConfiguration)。配置好后,调用 AndroidApplication.initialize()方法,传入ApplicationListener(MyGdxGame) 和配置。请参考 AndroidApplicationConfiguration Javadocs 以获取更多关于可配置项的信息。

Android  应用可以拥有多个activities。 Libgdx 游戏通常应该只由单个的 activity 组成。在libgdx内部实现游戏的不同屏幕,而不是单独的activities。这样做的原因是创建一个新的 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 版本

如果你的应用是打算运行在Android1.5及更高版本,那么关键的一点是要把 targetSdkVersion的值设置为 >= 6。如果没有设置这个属性,高版本的Android会以传统模式运行应用程序。在不好的情况下,绘制区域的分辨率会比实际的分辨率低很多。

屏幕方向 & 配置更改

除了 targetSdkVersion,activity 元素中的  screenOrientation 和 configChanges 也应该设置。

screenOrientation 属性为应用指定一个固定的方向。一旦省略,应用程序就可以同时在横屏和竖屏情况下运行。

configChanges 属性很 关键 且上面应该始终有值。省略该属性意味着每次当物理键盘滑出/滑入或者设备方向改变时应用程序都会重启。 如果省略了screenOrientation ,libgdx应用会收到ApplicationListener.resize()的调用以指示屏幕方向的改变。接着API客户端就可能据此重新布局。

权限

如果应用需要能够在设备外存储器中写入(如:SD-card),需要访问网络,使用振动器,想避免屏幕进入睡眠状态,或者想记录音频,那么需要在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。

如果一个应用不需要访问加速计或指南针,建议通过设置 AndroidApplicationConfiguration 的 useAccelerometer 和useCompass 域为 false 以取消它们。

请参考 Android Developer's Guide 查看怎样设置其他属性,如设置应用程序的图标。

动态壁纸

Libgdx 有一个简单的功能可以为Android创建动态壁纸。动态壁纸的启动类为 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() 方法在用户滑动主屏时调用,指出与屏幕与中心位置有多大偏移。该方法在渲染线程进而调用,因此不需要同步任何东西。

除了启动类,还必须创建一个XML文件以描述壁纸。我们将它命名为 livewallpaper.xml。在Android工程 res/ 文件夹下创建一个文件夹叫 xml/ 并把该文件放在那(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"/>

这里定义了你的LWP在选择器中显示的缩略图,描述和一个Activity,这个Activity会在用户点击LWP选择器的 "Settings" 的时候显示。这里应该是一个标准的Activity,包含一些组件用来更改设置,如:背景色等类似的设置。你可以在SharedPreferences中保存这些设置并稍后在LWP 的 ApplicationListener 里通过Gdx.app.getPreferences()加载它们。

最后,需要在AndroidManifest.xml 文件中添加一些东西。这里是一个LWP以及一个简单设置的:

<?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
  • 动态壁纸服务,指向 livewallpaper.xml 文件,查看 meta-data

注意,动态壁纸功能仅从Android 2.1 (SDK level 7)版本开始支持。

LWPs 的触摸输入有一定的局限。通常只记录点击/删除。如果你需要全触摸,可以查看设置 AndroidApplicationConfiguration#getTouchEventsForLiveWallpaper 标志为 true 以接收全部多点触摸事件。

Daydreams

从Android 4.2起, 用户可能设置 Daydreams,它会在设备闲置或锁定时显示。daydreams 类似于屏保可以显示相册等。Libgdx 使你能够很容易地编写daydreams:

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,设置 configuration 和 ApplicationListener 然后初始化 daydream。

除了daydream本身,还可以设置一个activity以便用户配置他的dayream。这只需要一个普通activity,或是一个libgdx AndroidApplication。一个空的activity如下所示: 

package com.badlogic.gdx.tests.android;

import android.app.Activity;

public class DaydreamSettings extends Activity {

}

这个配置activity必须被用作Dayream的元数据。在res/xml下创建一个xml文件,并像如下所示指定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 后台依赖使用 Xamarin 的 MonoDevelop IDE作开发,并使用一个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 文件包含以下应用配置信息:屏幕方向,最低OS版本,可选, 屏幕截图等。下面是一个通过MonoDevelop编辑过的XML文件:

<?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文件。下面是一个示例:

A conversion process is required in order to create the assemblies needed by Monotouch for the iOS platform. This processing is done as part of the pre-build step when you issue the build operation in MonoDevelop. If you are using third party libraries or have non-standard locations defined for some of your source, you will need to update the convert.properties file accordingly. An example file is below:

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。打开my-gdx-game-html5工程里的 GwtLauncher.java :

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() 和 GwtApplication.getApplicationListener() 。前者需要返回一个GwtApplicationConfiguration 实例,该实例指定了多数HTML5应用的配置。GwtApplication.getApplicatonListener() 方法返回 ApplicationListener以运行。

模块文件

GWT 需要为每一个引用的 jar/项目 编写Java代码。此外,每个jar/项目需要一个以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 Developer Guide 获取更多关于模块和依赖的信息。

反射支持

因各种原因,GWT不支持Java反射。 Libgdx 有一个内部模拟层来生成一小部分内部类的反射信息。就是说如果使用libgdx 的 Json serialization 功能,就会出问题。你可以通过指定生成哪个包和类的反射信息来修复这个问题。要这样做,需要在GWT工程的gwt.xml文件中配置如下属性:

<?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元素来添加多个包和类。 

该功能还处在实验阶段,使用请注意风险。

Loading 画面

libgdx HTML5 应用会预加载gdx.assetpath下找到的所有资源。在此加载过程中,会显示一个通过GWT组件实现的加载画面。如果要自定义加载画面,可以简单地重写下GwtApplication.getPreloaderCallback()方法(上面例子中的GwtLauncher)。下面的例子通过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才可用。

你可能感兴趣的:(guide,中文手册,Libdgx)