5、使用Libgdx设计一个简单的游戏------雨滴

时间:2023-12-05 09:42:08

(原文:http://www.libgdx.cn/topic/49/5-%E4%BD%BF%E7%94%A8libgdx%E8%AE%BE%E8%AE%A1%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E6%B8%B8%E6%88%8F-%E9%9B%A8%E6%BB%B4

在深入研究Libgdx提供的API之前,我们先来创建一个简单的游戏来感受一下libgdx各个功能。这里将简单的对一些功能做介绍。

使用的技术:

  • 文件访问

  • 清除屏幕

  • 渲染图片

  • 使用相机

  • 基本的输入

  • 播放音效

项目设置

首先创建一个libgdx项目,移步到这里

  • 应用名称(Application name):drop

  • 包名(Package name):cn.libgdx.drop

  • 游戏类(Game class):Drop

生成项目之后,将其导入到eclipse中(需要记住,是Gradle项目)。

游戏设计思路

游戏思路很简单:

  • 使用一个桶雨滴

  • 桶在屏幕底部

  • 雨滴随机从屏幕顶部下载,加速下落

  • 玩家可以水平拖动桶

  • 游戏没有结束

Assets文件

点击下载资源文件

我们需要一些图片和音效来让游戏变得更好。对于图像来说我们需要设置分辨率为800*480像素(在Android中要设置为横向(landscape ))。如果设备没有这个分辨率,我们需要设置适用屏幕。

为了让资源文件在游戏中可用,我们必须将资源文件放到Android项目的assets文件夹下。一共四个文件:drop.wav,rain.mp3,droplet.png和bucket.png。把他们放到drop-android/assets/文件夹下。

配置启动类

准备完之前的配置后,我们需要修改desktop项目的启动类。打开drop-desktop项目下的DesktopLauncher.java文件。我们需要设置窗口为800*480,设置窗口标题为“雨滴”。代码如下:

package cn.libgdx.drop.desktop;

import com.badlogic.gdx.backends.lwjgl.LwjglApplication;

import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;

import cn.libgdx.drop.Drop;

public class DesktopLauncher {

public static void main (String[] arg) {

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();

config.title="雨滴";

config.width=800;

config.height=480;

new LwjglApplication(new Drop(), config);

}

}

找到Android项目(即drop-android),需要设置应用的屏幕方向,所以需要修改项目根目录AndroidManifest.xml文件,设置android:screenOrientation="landscape"。代码如下:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;

package="cn.libgdx.drop.android"

android:versionCode="1"

android:versionName=&quot;1.0&quot; >

<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" />

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/GdxTheme" >

<activity

android:name=&quot;cn.libgdx.drop.android.AndroidLauncher&quot;

android:label="@string/app_name"

android:screenOrientation="landscape"

android:configChanges="keyboard|keyboardHidden|orientation|screenSize">

<intent-filter>

<action android:name=&quot;android.intent.action.MAIN&quot; />

<category android:name=&quot;android.intent.category.LAUNCHER&quot; />

</intent-filter>

</activity>

</application>

</manifest>

其实是用GDX-setup生成的项目android:screenOrientation默认值为landscape,所以无需改动。

接下来需要禁用加速度计和罗盘,这需要改动Android项目AndroidLauncher.java文件,代码如下:

package cn.libgdx.drop.android;

import android.os.Bundle;

import com.badlogic.gdx.Gdx;

import com.badlogic.gdx.backends.android.AndroidApplication;

import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;

import cn.libgdx.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的分辨率,这个是由Android操作系统来定。就像我们之前定义的,设置所有平台分辨率为800*480。

开始编写代码

现在将代码分成几个部分来分析,为了保持项目的可移植性,我们需要将代码写到core项目下。

载入Assets

我们第一个任务就是载入assets文件和保存参数。Assets通常在ApplicationListener.create()方法中载入,代码如下:

package cn.libgdx.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.GL20http://com.badlogic.gdx.graphics.GL200;

import com.badlogic.gdx.graphics.Texturehttp://com.badlogic.gdx.graphics.Texturee;

import com.badlogic.gdx.graphics.g2d.SpriteBatchhttp://com.badlogic.gdx.graphics.g2d.SpriteBatchh;

public class Drop extends ApplicationAdapter {

private Texture dropImage;

private Texture bucketImage;

private Sound dropSound;

private Music rainMusic;

@Override

public void create () {

//将资源载入到GPU中。

dropImage=new Texture(Gdx.files.internal("droplet.png"));

bucketImage=new Texture(Gdx.files.internal("bucket.png"));

//载入雨滴音效和下雨背景音乐

dropSound=Gdx.audio.newSound(Gdx.files.internal("drop.wav"));

rainMusic=Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));

//开始播放背景音乐

rainMusic.setLooping(true);

rainMusic.play();

}

@Override

public void render () {

}

}

在create()方法中前两行载入雨滴和桶。Texture是指将图像载入并保存到缓存。Texture通过一个文件句柄(FileHandle)。FileHandle通过Gdx.files获取。这里有不同的文件,如果是调用Android下assets文件夹下的资源,使用internal。

接下来是载入音效和背景音乐。libgdx分音效和音乐,音效是保存到内存中,音乐是从保存的地方直接载入的音乐。音乐文件一般比较大不能全部载入内存。如果声音低于10秒,最好使用音效;如果超过10秒,就使用音乐。

载入音效通过Gdx.audio.newSound()方法,载入音乐使用Gdx.audio.newMusic()方法。两种方法都需要传递一个文件句柄(FileHandle),就行上面的Texture。

接下来就是播放音乐并且设置循环。如果你这是运行程序,会听到下雨声。

Camera和SpriteBatch

接下来需要创建相机(camera)和SpriteBatch。我们使用相机的目的是在所有的平台都使用800*480分辨率,无论它的真实屏幕的大小。SpriteBatch是绘制2D图像的特殊类,跟texture差不多。

我们添加两个变量到类中,如下:

private OrthographicCamera camera;

private SpriteBatch batch;

在create()方法中添加如下语句,创建camera:

camera = new OrthographicCamera();

camera.setToOrtho(false, 800, 480);

这将确保camera总是显示800*480的范围。可以视作一个虚拟窗口。这样可以让应用可移植性增强。

接下来,在create()方法中创建SpriteBatch:

batch=new SpriteBatch();

添加桶

最后添加桶和雨滴,它们需要以下的条件:

  • 桶和雨滴在800*480中需要一个位置信息。

  • 桶和雨滴需要宽和高。

  • Texture

为了展现桶和雨滴,我们需要保存它们的位置和大小。在Libgdx中可以通过Rectangle实现。代码如下:

private Rectangle bucket;

在create()方法中我们需要实例化并且设置他的只。这里设置在屏幕底部,并且水平居中。代码如下:

bucket = new Rectangle();

bucket.x = 800 / 2 - 64 / 2;

bucket.y = 20;

bucket.width = 64;

bucket.height = 64;

需要注意的是,在libgdx中,坐标系原点在左下角。

渲染桶

接下来需要渲染我们的桶,首先需要设置屏幕的背景色,在render()方法中添加如下代码:

@Override

public void render () {

Gdx.gl.glClearColor(0, 0, 0.2f, 1)http://Gdx.gl.glClearColor(0.2f, 1);

Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)http://Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)

}

为了使用顶层的类,像Texture和SpriteBatch。首先需要调用清除屏幕并设置为蓝色。第一句的意思是设置清屏颜色,第二句才执行清屏操作。

接下来我们需要告诉camera确保它更新。接下来我们设置camera在每一帧中都更新。

接下来我们需要渲染桶。

batch.setProjectionMatrix(camera.combined);

batch.begin();

batch.draw(bucketImage, bucket.x, bucket.y);

batch.end();

第一句设置batch的坐标系统为相机,batch.end();会立即提交我们的绘制请求。

让桶可以移动

接下来我们控制桶。在设计游戏时,我们的设想是桶可以拖动。如果触摸屏幕或者鼠标按钮。我们想让桶进行水平移动。

if(Gdx.input.isTouched()) {

touchPos = new Vector3();

touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);

camera.unproject(touchPos);

bucket.x = touchPos.x - 64 / 2;

}

首先我们询问input模块通过Gdx.input.isTouched()来判断是否被触摸。接下来我们将触摸或者鼠标点击的卫士传递到camera的坐标系统。Gdx.input.getX()和Gdx.input.getY()方法用于返回当前触摸和鼠标的位置。为了转换当前坐标系统为camera的坐标系统我们需要使用camera.uproject()方法,调用这个方法需要一个Vector3,一个三维向量。我们创建这个向量,设置当前触摸或者鼠标的当前坐标,调用方法。

需要注意的是touchPos需要在类中声明,如果在if语句中,每次执行这个语句就会创建一个变量,这就会导致Android的垃圾处理产生异常。

touchPos是一个三维向量,你可能想知道为什么我们用2D界面需要一个三维向量。事实上,OrthographicCamera是一个三维相机(camera)。

让桶移动(键盘)

在桌面和浏览器环境我们同样需要获取键盘输入。让我们通过设置可以通过键盘来操作桶。

if(Gdx.input.isKeyPressed(Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime()http://Gdx.graphics.getDeltaTime()FT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();

if(Gdx.input.isKeyPressed(Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime()http://Gdx.graphics.getDeltaTime()GHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime();

当键盘按下时,Gdx.input.isKeyPressed()方法用来判断是否为指定按键。Gdx.graphics.getDeltaTime()返回一帧的时间http://Gdx.graphics.getDeltaTime()用来判断是否为指定按键。Gdx.graphics.getDeltaTime()返回一帧的时间。

我们同样需要设置桶不要越界:

if(bucket.x < 0) bucket.x = 0;

if(bucket.x > 800 - 64) bucket.x = 800 - 64;

添加雨滴

对于雨滴来说我们需要使用一个Rectangle数组,每个保存雨滴的位置和大小。让我们添加一个变量:

private Array<Rectangle> raindrops;

这个Array类是一个libgdx的工具类,用来代替java的ArrayList。后者将在很多情况下产生垃圾。Array尽量减少垃圾的产生。

我们同样需要记录上一次掉落的雨滴的时间,所以我们添加:

private long lastDropTime;

我们将存储纳秒,这就是我们为什么使用long。

下面方法用来随机产生雨点:

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

}

我们需要在create()方法中进行实例化:

raindrops = new Array<Rectangle>();

spawnRaindrop();

接下来我们需要在render()方法中检测雨点的时间间隔,并生成新雨点:

if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop();

我们同样需要雨点移动,一下是代码:

Iterator<Rectangle> iter = raindrops.iterator();

while(iter.hasNext()) {

Rectangle raindrop = iter.next();

raindrop.y -= 200 * Gdx.graphics.getDeltaTime()http://Gdx.graphics.getDeltaTime()getDeltaTime();

if(raindrop.y + 64 < 0) iter.remove();

}

雨点需要被渲染,所以需要SpriteBatch来进行渲染:

batch.begin();

batch.draw(bucketImage, bucket.x, bucket.y);

for(Rectangle raindrop: raindrops) {

batch.draw(dropImage, raindrop.x, raindrop.y);

}

batch.end();

最后需要判断雨滴和桶是否重叠,如果重叠,就删除雨滴:

if(raindrop.overlaps(bucket)) {

dropSound.play();

iter.remove();

}

退出清理

最后需要清理。代码如下:

@Override

public void dispose() {

dropImage.dispose();

bucketImage.dispose();

dropSound.dispose();

rainMusic.dispose();

batch.dispose();

}

点击下载源码

www.libgdx.cn版权所有,如需转载,注明出处)