第17節 - LibGDX: Splash Screen
這一節我會介紹如何製作LibGDX遊戲的開埸畫面(Splash Screen)。
在第13節 - UML(Unified Modeling Language): Class Diagram介紹過開埸畫面的類別圖(Class Diagram),如下圖:

- Game是abstract Class,ApplicationListener是interface,所以它們的關係是Realization。
- MyDemo13是Class,Game是abstract Class,所以它們的關係是Generalization。
- MyDemo13是Class,SplashScreen是Class(傳入MyDemo13內用),所以它們的關係是Association。
- SplashScreen是Class,MenuScreen是Class(傳入SplashScreen內用),所以它們的關係是Association。
- SplashScreen和MenuScreen是Class,Screen是interface,所以它們的關係是Realization。
以下是LibGDX-Splash Screen程式執行的結果:
1 - DesktopLauncher

DesktopLauncher是PC Desktop的Starter Class,我們在DesktopLauncher內設定顯示的大小為459x600px。
2 - MyDemo17 extends Game
在第15節 - @Override的用法和意思介紹過在MyDemo15內必須把create()方法定義內容。

- ApplicationListener是一個interface,所有方法必須是抽象方法,則是內容沒有{......}的,所以create()尚未定義。
在Game abstract class內create()也未定義內容,所以在MyDemo15 class內必須定義內容。 - 在Game abstract class內resize(), render(), pause(), resume()和dispose()已經定義了內容,所以在MyDemo15 class內可以覆寫或不覆寫,以上例子就覆寫所有resize(), render(), pause(), resume()和dispose()方法。
- 最後,我們用MyDemo15 class 繼承Game abstract class是因為Game內有兩個新方法:
setScreen(Screen screen);
getScreen(Screen screen);
setScreen()方法可以用來轉換畫面,例如在第15節 - @Override的用法和意思我們介紹過埸畫面(Splash Screen)的做法。
注意,我們把第15節的MyDemo15改為MyDemo17,MyDemo17.java的程式如下:

- 把MyDemo17 extends Game。
在Game abstract class內create()也未定義內容,所以在MyDemo17 class內必須定義內容。 - 定義create()方法,在create()方法內用setScreen()方法把畫面跳到splashScreen。
3 - SplashScreen implements Screen
進入splashScreen後,我們須要一個Game物件,因為Game物件有我們須要的setScreen()方法,把畫面由splashScreen跳到menuScreen。
以下會介紹4個方法,方法1和2是可行的,方法3和4是不可行的,它們只是用作解釋。
方法1 - setScreen(Screen screen)內傳入Game物件(this)
方法1是在setScreen(Screen screen)方法內傳入SplashScreen物件(splashScreen),然後在splashScreen Constructor內傳入Game物件(this):
setScreen(splashScreen(this));
Game類別內的setScreen(Screen screen)方法預設須要傳入一個Screen物件(例如:splashScreen)。
因為在splashScreen畫面內我們要再把畫面跳到menuScreen畫面,所以我們須要傳入一個Game的物件(例如:game)。

- MyDemo17是繼承Game類別,所以MyDemo17也是Game類別,它有setScreen()方法。
每一個類別內,Java預設一個"this"keyword,this相等於這個類別的物件。
MyDemo17內的"this"=MyDemo17類別的物件=Game類別的物件(例如:game)。 - 把this傳入splashScreen Constructor內。
- 在SplashScreen內建立一個新的Game的物件(例如:game),它是用來傳取this物件。
this.game=game; - 在SplashScreen內建立好game物件後,SplashScreen內就可以用setScreen()方法。
game.setScreen(menuScreen); - 在SplashScreen內再把畫面跳到menuScreen畫面。
- 在render()方法內把texture物件顯示在屏幕上。
方法2 - 用getApplicationListener()方法
方法2是用LwjglApplicaiton.java內的getApplicationListener()方法,它會回傳一個ApplicationListener物件。
然後我們再把ApplicationListener物件Casting做Game物件(game)。
以下是LibGDX的官方文件-LwjglApplicaiton.java:

- LwjglApplicaiton.java是Desktop PC的backend,則是如果把LibGDX程式在電腦上執行,我們就須要執行DesktopLauncher Starter,DesktopLauncher內就須要用到LwjglApplicaiton backend。
- LwjglApplicaiton.java內有getApplicationListener()方法,它會回傳一個ApplicationListener物件。
以下是用getApplicationListener()的方法:

- 這次不是把Game物件傳入splashScreen Constructor內,而是直接在splashScreen類別內import Gdx Class,再在Gdx>Application(app)>getApplicationListener()方法。
- 把splashScreen傳入setScreen() 內。
注意,這次不須要傳入Game物件(this)。 - 因為Game類別是實作ApplicationListener介面,所以我們可以把ApplicationListener物件Casting做Game物件(game)。
import Gdx類別後,就可用到Gdx.app.getApplicationListener()方法。 - 有了Game物件,我們就可以用setScreen()方法。
- 在render()方法內把texture物件顯示在屏幕上。
方法3(不可能的方法) - 在MyDemo17內import ApplicationListener物件
注意,方法3是不可能的,我會解釋為甚麼不可能。
假設在MyDemo17內import ApplicationListener介面,再用"new"產生一個ApplicationListener 物件(applicationListener),然後把ApplicationListener物件 Casting 做Game物件,最後用game.setScreen(MenuScreen)把畫面跳到menuScreen畫面。

大家可以發現ApplicaitonListener是一個介面(interface),它是沒有Constructor的,所以沒有"New"來產生物件。
ApplicationListener applicationListener = new ApplicationListener();是不可以的。
方法4(不可能的方法) - 在SplashScreen內import Game物件
注意,方法4和方法3相似,方法4也是不可能的,我會解釋為甚麼不可能。
假設在SplashScreen內import Game類別,再用"new"產生一個Game物件(game),然後用game.setScreen(MenuScreen)把畫面跳到menuScreen畫面

大家可以發現Game是一個abstract class,它是沒有Constructor的,所以沒有"New"來產生物件。
Game game = new Game();是不可以的。
4 - MenuScreen implements Screen
當splashScreen畫面跳到menuScreen畫面後,就會停在menuScreen畫面,如下圖:

- 以上例子只簡單顯示一張圖片,所以MenuScreen Constructor不須要傳入一個Game物件。
- 只簡單顯示一張圖片,把圖片(menuscreen.png)的中心對齊World Coordinate的中心位置(0,0)。
我們可以在menuScreen裡建立不同選擇按鈕,例如:Start, Option, Credit, Facebook等等,方法是在MenuScreen類別內再傳入Game物件,再用setScreen()方法跳到其他畫面,我會在其他章節詳細介紹,概念如下圖:

5 - Scene2D-Stage,Group,Actor,Image和Texture的關係
最後,要做到開埸畫面逐漸呈現的效果(Fade in),我們可以用Stage Class, Actor Class, Image Class。
Stage是一個舞台,在舞台內可以有不同的Actor(演員),而Image(圖像)也是Actor的一種。
Stage內還有一個addAction()方法,這個方法就可以用來把開埸畫面做出逐漸呈現的效果。
有關Stage,Group,Actor,Image和Texture的詳細解釋,請閱讀第16節 - LibGDX: Scene2D-Stage, Group, Actor, Image和Texture的關係。

- 用Texture產生一個Texture物件(例如:texture)然後把圖片(splashScreen.png)傳入。
- 用Image產生一個Image物件(例如:image)然後把texture物件傳入。
- 在show()方法內,把image加入Stage舞台內,再用Stage內還有一個addAction()方法把開埸畫面做出逐漸呈現的效果。
1-Actions.alpha(0)-設定alpha=0,則把image設定為全透明。
2-Actions.fadein(0.5f)-把image做出逐漸呈現的效果。
3-Actions.delay(2)-把動作延遲2秒。
4-Actions.run()-把現時splashScreen畫面跳到menuScreen畫面。 - show()方法會設定所有動作,但尚未顯示在屏幕上,我們必須在render()方法內用輪入:
1-stage.act();-更新所有Actor。
2-stage.draw();-把所有Actor顯示在屏幕上。