三、闪屏

在这一章中,我们将清理一些当你在游戏启动画面上工作时可能出现的常见问题。闪屏,有时称为标题卡,是玩家开始游戏时看到的第一个东西。

闪屏可以由一个或多个不同的图像组成。这些图像通常在一些后台设置过程运行时显示,可以代表从游戏开发公司到发行公司或代理的任何内容。

除非你正在创建一个不需要玩家设置的游戏,否则你可以在游戏线程开始之前,使用 Android 在主活动线程中加载这些闪屏。原因很简单。大多数游戏会在游戏开始前用菜单屏幕提示玩家。菜单屏幕可以有从开始游戏、查看分数到登录网络服务的选项。如果你的游戏将包括这种菜单系统,你将希望在主活动线程中启动菜单。当玩家选择开始游戏时,你可以让菜单产生游戏线程。

本章介绍的解决方案假设您将在主活动线程而不是游戏线程中启动游戏的闪屏。此外,如第 2 章中所述,该闪屏和菜单屏幕示例将采用横向模式。为什么要对此进行区分呢?如果你在主游戏线程中启动闪屏,你可以使用 OpenGL ES 来显示屏幕,然后使用你的游戏代码来跟踪玩家在菜单中做什么。虽然完全可以接受,但这个解决方案有点大材小用。在主活动线程中加载和处理闪屏的解决方案更容易编码和跟踪。

3.1 创建闪屏

问题

当游戏在后台加载时,您无法显示游戏名称。

解决办法

当您在后台执行其他游戏相关功能时,使用闪屏显示游戏信息。闪屏通常是当你的主要 Android 活动被加载时显示的图像。这意味着您将在主活动线程中加载图像,并在第二个线程中启动游戏。

它是如何工作的

这个解决方案通过三个简单的步骤实现。您需要创建一个布局来显示您想要用作闪屏的图像。然后你需要在你的应用中创建第二个Activity来代表你的游戏。最后,您需要创建一个postDelayed() Handler() ,它将执行您的后台代码,然后在完成后启动您的游戏线程。

最终的结果是一个遵循这个路径的游戏流:当玩家启动你的游戏时,主活动开始,然后当你的游戏在后台做一些内务处理时,出现一个闪屏,最后,当这个内务处理完成时,活动直接启动到游戏中。

创建布局

首先创建一个显示初始屏幕图像的布局。创建该布局的说明在第 2 章的中解释。activity_main.xml的代码如清单 3-1 所示。关于代码含义的进一步解释,请参见第 2 章。

清单 3-1activity_main.xml

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:contentDescription="@string/splash_screen_description"
android:layout_alignParentTop="true"
android:scaleType="fitXY"
android:src="@drawable/titlescreen" />

</RelativeLayout>

activity_main.xml文件中显示的图像显示在图 3-1 中。

9781430257646_Fig03-01.jpg

图 3-1 。游戏的启动画面

创建新的Activity

现在你的布局已经创建好了,你需要在你的应用中创建一个新的Activity来代表你的游戏的主要活动。一个Activity的基本代码如清单 3-2 所示。

清单 3-2 。基本Activity代码

public class SBGGame extends Activity{

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//Place your game code here

}
}

现在你有主Activity(一个布局代表你的闪屏),你有Activity代表你游戏的主启动点。你是如何从主Activity进入游戏Activity的?

你将在主Activity中使用一个Handler()来延迟游戏Activity的推出。

创建一个postDelayed() Handler()

Handler()有一个名为postDelayed()的方法,可以用来延迟另一个Activity意图的开始。您需要执行的所有日常工作都可以在Handler()内完成。清单 3-33-6 会告诉你怎么做。

在您的主Activity中,创建一个名为 GAME_THREAD_DELAY 的常量,并将其设置为值 999000,如清单 3-3 所示。这意味着在你的游戏Activity启动之前会有 999 秒的延迟。

清单 3-3Activity一延迟

public class MainActivity extends Activity {
static int GAME_THREAD_DELAY = 999000;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
}

}

现在创建一个新的Handler()实例。在GAME_THREAD_DELAY到期后,使用postDelayed()方法延迟一个新线程的启动,如清单 3-4 所示。

清单 3-4 。使用postDelayed

public class MainActivity extends Activity {
static int GAME_THREAD_DELAY = 999000;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
}
}, GAME_THREAD_DELAY);
}
}

现在将所有的内务代码、启动游戏的代码Activity和杀死主程序的代码Activity放到新的 runnable 对象的run()方法中(见清单 3-5 )。

清单 3-5 。启动新的Activity

public class MainActivity extends Activity {
static int GAME_THREAD_DELAY = 999000;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {

Intent gameMain = new Intent(MainActivity.this, SBGGame.class);
MainActivity.this.startActivity(gameMain);

//Perform all of your housekeeping activities here

MainActivity.this.finish();
}
}, GAME_THREAD_DELAY);
}

}

最后,在所有内务操作完成后,将GAME_THREAD_DELAY从 999 秒改为 1 秒,强制其启动游戏Activity,如清单 3-6 所示。这给了你 999 秒来执行所有的游戏预加载。然后,当你完成预加载游戏,你只需设置延迟 1 秒,以强制启动游戏Activity

清单 3-6 。缩短延迟定时器

public class MainActivity extends Activity {
static int GAME_THREAD_DELAY = 999000;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
new Handler().postDelayed(new Thread() {
@Override
public void run() {
Intent gameMain = new Intent(MainActivity.this, SBGGame.class);
MainActivity.this.startActivity(gameMain);

//Perform all of your housekeeping activities here

GAME_THREAD_DELAY = 1000;

MainActivity.this.finish();

}
}, GAME_THREAD_DELAY);
}

}

3.2 在闪屏期间加载多个图像

问题

当游戏在后台加载时,您希望在闪屏中显示多个图像。

解决办法

创建第二个布局,为主页面Activity创建第二个闪屏图像。

它是如何工作的

这个解决方案将基于上一个问题的解决方案。在问题 3.1 中,你在主Activity中创建了一个Handler()Handler()执行了一些后台任务,完成后启动了游戏Activity

您将向该解决方案添加第二个布局,用于显示第二个图像或闪屏。您将在第二个闪屏中显示的图像如图 3-2 所示。

9781430257646_Fig03-02.jpg

图 3-2 。游戏的第二个闪屏

第一步是创建一个名为second_image的新布局来显示图像。您可以从您的第一个布局中复制 XML(清单 3-1 )以使事情变得简单(这里再次展示以供参考)。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:contentDescription="@string/splash_screen_two_description"
android:layout_alignParentTop="true"
android:scaleType="fitXY"
android:src="@drawable/credits" />

</RelativeLayout>

现在,修改你的主Activity来显示使用这个布局,如清单 3-7 中的所示。

清单 3-7 。加载新布局

public class MainActivity extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
SBGVars.context = this;
new Handler().postDelayed(new Thread() {
@Override
public void run() {
setContentView(R.layout.second_image);
}
       }
}

Handler() delay到期时,它将显示第二个闪屏,如图图 3-2 所示。

3.3 淡入和淡出闪屏

问题

游戏的第一个闪屏应该淡入游戏的菜单中,以获得更微妙的开场。

解决办法

使用动画和overridePendingTransition()和从一个闪屏图像渐变到另一个。

它是如何工作的

为使该解决方案正常工作,您需要从第 2 章中的菜单屏幕开始。

在这个解决方案中,你想要做的是创建一个动画,它将从主Activity的闪屏渐变到菜单屏。这不是一个难以完成的任务;它需要使用一种方法和几个布局文件。

首先,在res/layout文件夹中,新建两个布局文件;说出一个fadein.xml和另一个fadeout.xml的名字。第一个表示将图像淡入显示的动画布局,第二个表示将图像淡出屏幕的动画布局。

文件的代码应该如清单 3-8 中的所示。

清单 3-8fadein.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="1000" />

这段代码说的是,使用指定的动画插值器,在一秒钟内从完全透明(android:fromAlpha="0.0")移动到完全不透明(android:toAlpha="1.0")状态(android:duration="1000")。

fadeout.xml文件中,你将做大致相同的转换,只是不是从透明到不透明,而是从不透明到透明,如清单 3-9 所示。

清单 3-9fadeout.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="1000" />

现在,在解决方案 3.1 和 3.2 中解释的Handler() 中,添加一个对overridePendingTransition()的调用,传递一个指向fadein.xmlfadeout.xml的指针(参见清单 3-10 )。

清单 3-10 。使用overridePendingTransition()

public class MainActivity extends Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
new Handler().postDelayed(new Thread() {
@Override
public void run() {
Intent mainMenu = new Intent(MainActivity.this, SBGMenuScreen.class);
MainActivity.this.startActivity(mainMenu);

//Perform background tasks

GAME_THREAD_DELAY = 1000;
MainActivity.this.finish();
overridePendingTransition(R.layout.fadein,R.layout.fadeout);
}
}, GAME_THREAD_DELAY);
}
}

当你开始你的游戏时,你应该会看到第一个启动画面载入,然后是第二个平滑的渐变到菜单画面。