在深入了解LibGDX詳細(xì)的API之前,讓我們先創(chuàng)建一個(gè)簡(jiǎn)單的游戲,它會(huì)對(duì)每個(gè)模塊都有接觸,我們將介紹一些概念,但是又會(huì)適可而止。
我們將來了解:
- 基本文件訪問
- 清除屏幕
- 繪制圖像
- 使用相機(jī)
- 基本輸入處理
- 播放聲音效果
這些概念的示例也可以在LibGDX.info中找到
項(xiàng)目搭建
按照“Project Setup Gradle”中的步驟進(jìn)行操作。我使用以下配置:
Application name: drop
Package name: com.badlogic.drop
Game class: Drop
我還設(shè)置了項(xiàng)目的目標(biāo)存儲(chǔ)路徑及SDK,我還取消選中OS子項(xiàng)目(這個(gè)可以根據(jù)您的需要自行選擇),并取消了所有的擴(kuò)展包(以為該項(xiàng)目不會(huì)用到這些擴(kuò)展包,并且也是為了讓示例項(xiàng)目足夠簡(jiǎn)單)。
一旦導(dǎo)入到您的IDE中,您應(yīng)該有5個(gè)項(xiàng)目或模塊:main,以及子項(xiàng)目android(或Eclipse下的drop-android),核心/ drop-core,桌面/ drop-desktop和html /HTML。
要啟動(dòng)或調(diào)試游戲,請(qǐng)參閱Project Setup Gradle中專用于IDE的頁(yè)面。
如果我們只是運(yùn)行它,你會(huì)得到一個(gè)錯(cuò)誤:無法加載文件:badlogic.jpg。 您必須首先編輯您的運(yùn)行配置:選擇工作目錄PATH_TO_YOUR_PROJECT \ drop \ android \ assets,
如果我們現(xiàn)在運(yùn)行,我們將獲得由啟動(dòng)應(yīng)用程序生成的默認(rèn)“游戲”:在紅色背景上的BadLogic Games圖像.
游戲設(shè)計(jì)
游戲理念很簡(jiǎn)單:
- 用水桶捕捉雨滴。
- 桶位于屏幕的下部
- 雨滴每秒鐘隨機(jī)產(chǎn)生屏幕頂部,并向下加速。
- 玩家可以通過鼠標(biāo)/觸摸水平拖動(dòng)水桶,或者通過左右光標(biāo)鍵移動(dòng)水桶。
- 當(dāng)然我們并沒有給游戲設(shè)置結(jié)束條件
游戲資源文件
我們需要幾張圖像和聲音效果,使游戲看起來更加漂亮。對(duì)于圖形,我們需要定義800x480像素的目標(biāo)分辨率(Android上的橫屏模式)。如果游戲運(yùn)行的設(shè)備沒有該分辨率,我們只需將所有內(nèi)容縮放到屏幕上即可。注意:對(duì)于更高級(jí)的游戲項(xiàng)目,您可能需要考慮為不同的屏幕密度提供不同的資源。但這個(gè)一個(gè)獨(dú)立的大論題,這里不再贅述。
雨滴和水桶應(yīng)垂直占據(jù)屏幕的小部分,所以我們讓它們的尺寸為64x64像素。
我從以下來源取得游戲資源:
water drop sound by junggle, see http://www.freesound.org/people/junggle/sounds/30341/
rain by acclivity, see http://www.freesound.org/people/acclivity/sounds/28283/
droplet sprite by mvdv, see https://www.box.com/s/peqrdkwjl6guhpm48nit
bucket sprite by mvdv, see https://www.box.com/s/605bvdlwuqubtutbyf4x
要使游戲資源可用于游戲,我們必須將它們放在Android資產(chǎn)文件夾中。 我命名了4個(gè)文件:drop.wav,rain.mp3,drops.png和bucket.png,并將它們放在android / assets /中。 我們只需要存儲(chǔ)游戲資源一次,因?yàn)樽烂婧虷TML5項(xiàng)目都配置為通過不同的方式“查看”此文件夾。 之后,根據(jù)您的IDE,您可能需要刷新項(xiàng)目樹以使新文件已知(在Eclipse中,右鍵單擊 - >刷新),否則可能會(huì)收到“未找到文件”的運(yùn)行時(shí)異常。
配置啟動(dòng)類
根據(jù)我們的要求,我們現(xiàn)在可以配置不同的啟動(dòng)類。 我們將從桌面項(xiàng)目開始。 在desktop/ src / ...(或Eclipse下的drop-desktop)中打開DesktopLauncher.java類。 我們想要一個(gè)800x480的窗口,并將標(biāo)題設(shè)置為“Drop”。 代碼應(yīng)該如下所示:
package com.badlogic.drop.desktop;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.drop.Drop;
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = "Drop";
config.width = 800;
config.height = 480;
new LwjglApplication(new Drop(), config);
}
}
轉(zhuǎn)到Android項(xiàng)目,我們希望應(yīng)用程序以橫屏模式運(yùn)行。 為此,我們需要修改android(或drop-android)根目錄中的AndroidManifest.xml,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.badlogic.drop.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="20" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/GdxTheme" >
<activity
android:name="com.badlogic.drop.android.AndroidLauncher"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
IDE已經(jīng)為我們填寫了正確的值,android:screenOrientation設(shè)置為“l(fā)andscape”。 如果我們想以縱向模式運(yùn)行游戲,我們將把該屬性設(shè)置為“portrait”。
我們也想節(jié)約電池,禁用加速度計(jì)和指南針。 我們?cè)赼ndroid/src/...(或drop-android)中的AndroidLauncher.java文件中執(zhí)行此操作,應(yīng)該如下所示:
package com.badlogic.drop.android;
import android.os.Bundle;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.badlogic.drop.Drop;
public class AndroidLauncher extends AndroidApplication {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
config.useAccelerometer = false;
config.useCompass = false;
initialize(new Drop(), config);
}
}
我們無法定義Activity的分辨率,因?yàn)樗怯葾ndroid操作系統(tǒng)設(shè)置的。 如前所述,我們將簡(jiǎn)單地將800x480目標(biāo)分辨率縮放到設(shè)備的分辨率。
關(guān)于代碼
我們想將我們的代碼分成幾個(gè)部分。 為了簡(jiǎn)單起見,我們將所有內(nèi)容保存在Core項(xiàng)目的Drop.java文件中,該文件位于core / src / ...(或Eclipse中的drop-core)中。
加載資源文件
我們的第一個(gè)任務(wù)是加載資產(chǎn)并存儲(chǔ)對(duì)它們的引用。 資源通常在ApplicationAdapter.create()方法中加載,所以我們來做:
package com.badlogic.drop;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Texture;
public class Drop extends ApplicationAdapter {
private Texture dropImage;
private Texture bucketImage;
private Sound dropSound;
private Music rainMusic;
@Override
public void create() {
// load the images for the droplet and the bucket, 64x64 pixels each
dropImage = new Texture(Gdx.files.internal("droplet.png"));
bucketImage = new Texture(Gdx.files.internal("bucket.png"));
// load the drop sound effect and the rain background "music"
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
// start the playback of the background music immediately
rainMusic.setLooping(true);
rainMusic.play();
... more to come ...
}
// rest of class omitted for clarity
對(duì)于每個(gè)資源文件,在Drop.java中都有對(duì)應(yīng)的變量指向它,這樣我們就可以在后期隨時(shí)隨地使用它。
create()方法中的前兩行加載雨滴和桶的圖像。紋理表示存儲(chǔ)在Video Ram中的圖像,通常傳遞一個(gè)來自Assets的資源文件的FileHandle句柄到Texture 來創(chuàng)建一個(gè)紋理,F(xiàn)ileHandle 實(shí)例是通過Gdx.files提供的方法之一獲得的。我們使用【internal】來獲得Assets文件夾下的資源文件,internal 文件位于Android項(xiàng)目的 assets 目錄中。 如前所述,桌面和HTML5項(xiàng)目引用同一目錄。
接下來我們加載聲音效果和背景音樂。Music 通常比較大。根據(jù)經(jīng)驗(yàn),如果您的聲音資源文件短于10秒,您應(yīng)該使用Sound實(shí)例,而更長(zhǎng)音頻片段則使用Music實(shí)例。
聲音或音樂實(shí)例的加載是通過Gdx.audio.newSound()和Gdx.audio.newMusic()完成的。 這兩種方法都采用FileHandle,就像Texture構(gòu)造函數(shù)一樣。
相機(jī)和SpriteBatch
接下來,我們要?jiǎng)?chuàng)建一個(gè)攝像頭和一個(gè)SpriteBatch。 我們將使用前者(Camera)來確保我們可以使用800x480像素的目標(biāo)分辨率渲染,無論實(shí)際的屏幕分辨率如何。 SpriteBatch是一個(gè)特殊的類,用于繪制2D圖像,像加載我們的紋理。
我們?cè)陬惿咸砑觾蓚€(gè)新的字段,我們稱之為相機(jī)和batch:
private OrthographicCamera camera;
private SpriteBatch batch;
在create()方法中,我們這樣初始化相機(jī):
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
這將確保相機(jī)總是向我們展示我們的游戲世界的800x480單位寬的區(qū)域。 把它看作是我們世界的虛擬窗戶。 我們目前將單位默認(rèn) 為像素。 沒有什么可以阻止我們使用其他單位, 米或任何你可以想得到的單位。 相機(jī)非常強(qiáng)大,您可以在本基礎(chǔ)教程中做很多事情。查看開發(fā)者指南的其余部分了解更多信息。
接下來我們?nèi)栽赾reate()方法中創(chuàng)建SpriteBatch實(shí)例:
batch = new SpriteBatch();
至此我們差不多已經(jīng)完成了創(chuàng)建我們需要運(yùn)行這個(gè)簡(jiǎn)單的游戲的所有事情。
添加桶
接下來我們來添加我們的桶還有雨滴:
- 一個(gè)桶/雨滴在我們的800x480單位世界的x / y位置。
- 一個(gè)桶/雨滴的寬度和高度以我們世界的單位表示。
- 桶/雨滴具有圖形表示,我們已經(jīng)具有我們加載的Texture實(shí)例的形式。
所以,要描述桶和雨滴,我們需要存儲(chǔ)他們的位置和大小。 Libgdx提供了一個(gè)可以用于此目的的Rectangle類。 我們首先創(chuàng)建一個(gè)代表我們的存儲(chǔ)桶的Rectangle類。 我們添加一個(gè)新字段:
private Rectangle bucket;
在create() 方法中我們實(shí)例化了一個(gè)Rectangle對(duì)象并為它設(shè)置了初始值,我們希望桶在離屏幕底部邊緣20像素的上方,并且水平居中:
bucket = new Rectangle();
bucket.x = 800 / 2 - 64 / 2;
bucket.y = 20;
bucket.width = 64;
bucket.height = 64;
我們將水桶放在水平方向上,并將其放置在屏幕底部的20個(gè)像素上方。 等等,為什么bucket.y設(shè)置為20,不應(yīng)該是480 - 20?這是因?yàn)長(zhǎng)ibGDX采用的是笛卡爾坐標(biāo)系(以左下角為原點(diǎn))。
注意:可以更改此設(shè)置相關(guān)討論,以便y軸指向下,原點(diǎn)位于屏幕的左上角。 OpenGL和相機(jī)類非常靈活,您可以在2D和3D中擁有您想要的任何種類的視角。 我們將繼續(xù)使用上述設(shè)置。
渲染桶
是時(shí)候來繪制我們的桶了。我們要做的第一件事是用深藍(lán)色來清除屏幕。 只需將render()方法更改為如下所示:
@Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
... more to come here ...
}
如果您使用高級(jí)類(如Texture或SpriteBatch),這兩行就是您需要了解OpenGL的唯一內(nèi)容。 第一個(gè)調(diào)用會(huì)將清屏的顏色設(shè)置為藍(lán)色。 參數(shù)是該顏色的紅色,綠色,藍(lán)色和alpha值,分別在[0,1]范圍內(nèi)。 下一個(gè)調(diào)用指示OpenGL實(shí)際清除屏幕。
接下來,我們需要告訴我們的相機(jī),以確保實(shí)時(shí)更新。 相機(jī)使用稱為矩陣的數(shù)學(xué)實(shí)體,負(fù)責(zé)設(shè)置渲染坐標(biāo)系。 每當(dāng)我們更改相機(jī)的屬性(如位置)時(shí),相機(jī)都需要重新計(jì)算這些矩陣。 我們不會(huì)在簡(jiǎn)單的例子中做到這一點(diǎn),但是通常每幀更新一次攝像機(jī)一般是一個(gè)很好的做法:
camera.update();
現(xiàn)在我們可以渲染我們的桶了:
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(bucketImage, bucket.x, bucket.y);
batch.end();
第一行告訴SpriteBatch使用相機(jī)指定的坐標(biāo)系。 如前所述,這是通過稱為矩陣的東西來完成的,更具體地說,是一個(gè)投影矩陣。 camera.combined字段是這樣一個(gè)矩陣。 從SpriteBatch上將會(huì)在前面描述的坐標(biāo)系中呈現(xiàn)所有內(nèi)容。
接下來我們告訴SpriteBatch開始一個(gè)新批量渲染。 為什么我們需要這個(gè),什么是批量渲染? OpenGL討厭不高效的繪制渲染。 它希望被告知有關(guān)盡可能多的圖像需要渲染,并一次性渲染完畢。
SpriteBatch 使得 OpenGL 在渲染視圖上更加得心應(yīng)手,它將記錄SpriteBatch.begin()和SpriteBatch.end()之間的所有繪圖命令。 一旦我們調(diào)用SpriteBatch.end(),它將一次提交我們所做的所有繪圖請(qǐng)求,加快渲染速度。 這一切可能在一開始就看起來很麻煩,但是它使得渲染500個(gè)精靈之間的差異是每秒60幀,并以每秒20幀的速度渲染100個(gè)精靈。
使水桶移動(dòng)(觸摸/鼠標(biāo))
時(shí)間讓用戶控制桶。 之前我們說過我們?cè)试S用戶拖動(dòng)存儲(chǔ)桶。 讓我們讓事情變得更容易一些。 如果用戶觸摸屏幕(或按下鼠標(biāo)按鈕),我們希望水桶將水平放置在該位置的周圍。 將以下代碼添加到render()方法的底部將這樣做:
if(Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
bucket.x = touchPos.x - 64 / 2;
}
解析:
- 首先,我們通過調(diào)用Gdx.input.isTouched()來訪問輸入模塊當(dāng)前是否觸摸屏幕(或按下鼠標(biāo)按鈕)。 接下來,我們要將觸摸/鼠標(biāo)坐標(biāo)轉(zhuǎn)換為我們的相機(jī)的坐標(biāo)系。 這是必要的,因?yàn)橛|摸/鼠標(biāo)坐標(biāo)的坐標(biāo)系可能與我們用于表示我們世界中的對(duì)象的坐標(biāo)系不同。
Gdx.input.getX()和Gdx.input.getY()返回當(dāng)前的觸摸/鼠標(biāo)位置(libgdx也支持多點(diǎn)觸控,但這是另一篇文章的主題)。 要將這些坐標(biāo)轉(zhuǎn)換到我們的攝像機(jī)坐標(biāo)系,我們需要調(diào)用camera.unproject()方法,它需要一個(gè)三維矢量Vector3作為參數(shù)。 我們創(chuàng)建這樣一個(gè)向量,設(shè)置當(dāng)前的觸摸/鼠標(biāo)坐標(biāo)并調(diào)用方法。 矢量現(xiàn)在將包含我們的桶所在坐標(biāo)系中的觸摸/鼠標(biāo)坐標(biāo)。最后,我們改變桶的位置,以觸摸/鼠標(biāo)坐標(biāo)為中心。
注意:總是實(shí)例化一個(gè)新的對(duì)象,如Vector3實(shí)例這是一個(gè)非常糟糕的做法。 因?yàn)槔占鞅仨氼l繁地收集這些短命的變量。在桌面應(yīng)用上可能還好,但是在Android上,GC可能會(huì)導(dǎo)致停留時(shí)間達(dá)幾百毫秒,從而導(dǎo)致卡頓。 為了在這種特殊情況下解決這個(gè)問題,我們可以簡(jiǎn)單地讓TouchPos成為Drop類的一個(gè)全局變量,而不是一直實(shí)例化它。
touchPos是一個(gè)三維向量。 你可能會(huì)想知道為什么我們只用2D操作但是使用一個(gè)三維向量。 OrthographicCamera實(shí)際上是一個(gè)3D相機(jī),也考慮到z坐標(biāo)。 想想CAD應(yīng)用程序,他們也使用3D攝相機(jī)。 我們只是濫用它來繪制2D圖形。
使桶移動(dòng)(鍵盤)
在桌面和瀏覽器中,我們也可以接收鍵盤輸入。 當(dāng)按下左或右光標(biāo)鍵時(shí),讓桶移動(dòng)。
我們希望桶勻速移動(dòng),以每秒200像素/單位向左或向右移動(dòng)。 為了實(shí)現(xiàn)這種基于時(shí)間的運(yùn)動(dòng),我們需要知道在最后和當(dāng)前渲染幀之間的時(shí)間間隔。 以下是我們?nèi)绾巫龅竭@一點(diǎn):
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime();
Gdx.input.isKeyPressed()告訴我們是否按下了特定的鍵。 鍵枚舉包含libgdx支持的所有鍵碼。 方法Gdx.graphics.getDeltaTime()返回在最后和當(dāng)前幀之間的時(shí)間間隔(以秒為單位)。 我們所需要做的就是通過以秒為單位增加/減去200像素來修改桶的x坐標(biāo)。
我們還需要確保我們的桶保持在屏幕的可視范圍內(nèi):
if(bucket.x < 0) bucket.x = 0;
if(bucket.x > 800 - 64) bucket.x = 800 - 64;
添加雨滴
對(duì)于雨滴,我們?cè)O(shè)計(jì)了一個(gè)Rectangle 列表實(shí)例,每個(gè)實(shí)例都跟蹤雨滴的位置和大小。 我們將該列表添加為一個(gè)字段:
private Array<Rectangle> raindrops;
Array類是一個(gè)libgdx實(shí)用程序類,而不是像ArrayList這樣的標(biāo)準(zhǔn)Java集合。 后者的問題是以各種方式生產(chǎn)垃圾。 Array類嘗試盡量減少垃圾。 Libgdx還提供其他垃圾收集器感知集合,如散列圖或集合。
我們還需要記錄上次我們產(chǎn)生的雨滴的時(shí)間,所以我們添加另一個(gè)字段:
private long lastDropTime;
我們將把時(shí)間精確到納秒,這就是為什么我們使用了long 類型的原因。
為了方便創(chuàng)建雨滴,我們將編寫一個(gè)名為spawnRaindrop()的方法,該方法實(shí)例化一個(gè)新的Rectangle,將其設(shè)置為屏幕頂部邊緣的隨機(jī)位置,并將其添加到雨滴陣列中。
private void spawnRaindrop() {
Rectangle raindrop = new Rectangle();
raindrop.x = MathUtils.random(0, 800-64);
raindrop.y = 480;
raindrop.width = 64;
raindrop.height = 64;
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
該方法的目的和明確。 MathUtils類是一個(gè)提供各種數(shù)學(xué)相關(guān)靜態(tài)方法的libgdx類。 在這種情況下,它將返回0到(800-64)之間的隨機(jī)值.TimeUtils是另一個(gè)libgdx類,提供了一些非?;镜臅r(shí)間相關(guān)的靜態(tài)方法。 在這種情況下,我們以納秒為單位記錄當(dāng)前時(shí)間,以后我們決定是否產(chǎn)生新的雨滴。
在create()方法中,我們實(shí)例化了雨滴數(shù)組,并產(chǎn)生了我們的第一個(gè)雨滴:
我們需要在create()方法中實(shí)例化該數(shù)組:
raindrops = new Array<Rectangle>();
spawnRaindrop();
接下來,我們?cè)趓ender()方法中添加幾行,該方法將檢查自從我們產(chǎn)生一個(gè)新的雨滴以來已經(jīng)過去了多少時(shí)間,并在必要時(shí)創(chuàng)建一個(gè)新的雨滴:
if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop();
我們還需要使我們的雨滴移動(dòng),讓我們采取簡(jiǎn)單的做法,讓他們以每秒200像素/秒的速度移動(dòng)。 如果雨滴在屏幕底部下方,我們將其從陣列中移除。
Iterator<Rectangle> iter = raindrops.iterator();
while(iter.hasNext()) {
Rectangle raindrop = iter.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
if(raindrop.y + 64 < 0) iter.remove();
}
····
雨滴需要渲染。 我們將添加到現(xiàn)在的SpriteBatch:
````java
batch.begin();
batch.draw(bucketImage, bucket.x, bucket.y);
for(Rectangle raindrop: raindrops) {
batch.draw(dropImage, raindrop.x, raindrop.y);
}
batch.end();
一個(gè)最后的調(diào)整:如果一個(gè)雨滴撞到水桶,我們想播放我們的下落聲音,并從陣列中刪除雨滴。 我們只需將以下行添加到雨滴更新循環(huán)中:
if(raindrop.overlaps(bucket)) {
dropSound.play();
iter.remove();
}
Rectangle.overlaps()方法檢查這個(gè)矩形是否與另一個(gè)矩形重疊/碰撞。 在我們的情況下,我們將播放聲音效果,并從陣列中刪除雨滴。
釋放
用戶可以隨時(shí)關(guān)閉應(yīng)用程序。 對(duì)于這個(gè)簡(jiǎn)單的例子,沒有什么需要做的。 然而一般來說,幫助操作系統(tǒng)清理我們創(chuàng)建的垃圾是一個(gè)好主意。
任何實(shí)現(xiàn)Disposable接口的libgdx類,都具有dispose()方法,在不再使用后需要手動(dòng)進(jìn)行清理。 在我們的例子中,紋理,聲音和音樂以及SpriteBatch都是如此。 作為好的程序員,我們復(fù)寫ApplicationAdapter.dispose()方法如下:
@Override
public void dispose() {
dropImage.dispose();
bucketImage.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
}
處理資源后,您不應(yīng)該以任何方式訪問它。
Disposables (本機(jī)資源)通常是無法被Java垃圾收集器處理的。 這就是為什么我們需要手工處理它們的原因。 Libgdx提供了各種幫助資產(chǎn)管理的方式。 閱讀開發(fā)指南的其余部分來發(fā)現(xiàn)它們。
處理暫停/恢復(fù)
每當(dāng)用戶打電話或按住主頁(yè)按鈕時(shí),Android都會(huì)暫停和恢復(fù)您的應(yīng)用程序。 在這種情況下,Libgdx會(huì)為您自動(dòng)執(zhí)行許多操作,例如 重新加載可能已經(jīng)丟失的圖像(OpenGL上下文丟失,一個(gè)可怕的主題),暫停和恢復(fù)音樂流等。
在我們的游戲中,實(shí)際上不需要處理暫停/恢復(fù)。 一旦用戶回到應(yīng)用程序,游戲就會(huì)繼續(xù)下去。 通常會(huì)實(shí)現(xiàn)暫停屏幕,并要求用戶觸摸屏幕以繼續(xù)。 這是為讀者留下的一個(gè)練習(xí) - 查看ApplicationAdapter.pause()和ApplicationAdapter.resume()方法。
完整代碼
這是我們這個(gè)示例游戲的完整代碼:
package com.badlogic.drop;
import java.util.Iterator;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;
public class Drop extends ApplicationAdapter {
private Texture dropImage;
private Texture bucketImage;
private Sound dropSound;
private Music rainMusic;
private SpriteBatch batch;
private OrthographicCamera camera;
private Rectangle bucket;
private Array<Rectangle> raindrops;
private long lastDropTime;
@Override
public void create() {
// load the images for the droplet and the bucket, 64x64 pixels each
dropImage = new Texture(Gdx.files.internal("droplet.png"));
bucketImage = new Texture(Gdx.files.internal("bucket.png"));
// load the drop sound effect and the rain background "music"
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
// start the playback of the background music immediately
rainMusic.setLooping(true);
rainMusic.play();
// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
batch = new SpriteBatch();
// create a Rectangle to logically represent the bucket
bucket = new Rectangle();
bucket.x = 800 / 2 - 64 / 2; // center the bucket horizontally
bucket.y = 20; // bottom left corner of the bucket is 20 pixels above the bottom screen edge
bucket.width = 64;
bucket.height = 64;
// create the raindrops array and spawn the first raindrop
raindrops = new Array<Rectangle>();
spawnRaindrop();
}
private void spawnRaindrop() {
Rectangle raindrop = new Rectangle();
raindrop.x = MathUtils.random(0, 800-64);
raindrop.y = 480;
raindrop.width = 64;
raindrop.height = 64;
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
@Override
public void render() {
// clear the screen with a dark blue color. The
// arguments to glClearColor are the red, green
// blue and alpha component in the range [0,1]
// of the color to be used to clear the screen.
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
batch.setProjectionMatrix(camera.combined);
// begin a new batch and draw the bucket and
// all drops
batch.begin();
batch.draw(bucketImage, bucket.x, bucket.y);
for(Rectangle raindrop: raindrops) {
batch.draw(dropImage, raindrop.x, raindrop.y);
}
batch.end();
// process user input
if(Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
bucket.x = touchPos.x - 64 / 2;
}
if(Gdx.input.isKeyPressed(Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime();
// make sure the bucket stays within the screen bounds
if(bucket.x < 0) bucket.x = 0;
if(bucket.x > 800 - 64) bucket.x = 800 - 64;
// check if we need to create a new raindrop
if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop();
// move the raindrops, remove any that are beneath the bottom edge of
// the screen or that hit the bucket. In the later case we play back
// a sound effect as well.
Iterator<Rectangle> iter = raindrops.iterator();
while(iter.hasNext()) {
Rectangle raindrop = iter.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
if(raindrop.y + 64 < 0) iter.remove();
if(raindrop.overlaps(bucket)) {
dropSound.play();
iter.remove();
}
}
}
@Override
public void dispose() {
// dispose of all the native resources
dropImage.dispose();
bucketImage.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
}
}
這是一個(gè)非?;镜睦?,介紹如何使用libgdx創(chuàng)建一個(gè)簡(jiǎn)單的游戲。 有些事情可以改進(jìn),像使用內(nèi)存管理類來回收我們每次刪除雨滴時(shí)垃圾收集器清理的所有矩形。 如果我們?cè)谂恐袀鬟f太多不同的圖像,OpenGL也不太喜歡(在我們這種情況下,我們只有兩個(gè)圖像情有可原)。 通常會(huì)把所有這些圖像都放在一個(gè)單一的紋理中,也稱為TextureAtlas。 屏幕和游戲也可以用于增加交互; 要了解更多,請(qǐng)看進(jìn)階教程。
我強(qiáng)烈建議您閱讀開發(fā)人員指南的其余部分,并查看Git存儲(chǔ)庫(kù)中的演示和測(cè)試。