LibGDX簡(jiǎn)單的示例程序

在深入了解LibGDX詳細(xì)的API之前,讓我們先創(chuàng)建一個(gè)簡(jiǎn)單的游戲,它會(huì)對(duì)每個(gè)模塊都有接觸,我們將介紹一些概念,但是又會(huì)適可而止。

我們將來了解:

  1. 基本文件訪問
  2. 清除屏幕
  3. 繪制圖像
  4. 使用相機(jī)
  5. 基本輸入處理
  6. 播放聲音效果

這些概念的示例也可以在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)單:

  1. 用水桶捕捉雨滴。
  2. 桶位于屏幕的下部
  3. 雨滴每秒鐘隨機(jī)產(chǎn)生屏幕頂部,并向下加速。
  4. 玩家可以通過鼠標(biāo)/觸摸水平拖動(dòng)水桶,或者通過左右光標(biāo)鍵移動(dòng)水桶。
  5. 當(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)單的游戲的所有事情。

添加桶

接下來我們來添加我們的桶還有雨滴:

  1. 一個(gè)桶/雨滴在我們的800x480單位世界的x / y位置。
  2. 一個(gè)桶/雨滴的寬度和高度以我們世界的單位表示。
  3. 桶/雨滴具有圖形表示,我們已經(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;
   }

解析:

  1. 首先,我們通過調(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è)試。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容