1. 介绍
libGDX 是一个基于 OpenGL (ES) 的跨平台 Java 游戏开发框架,适用于 Windows、Linux、macOS、Android、浏览器和 iOS。
更详细的介绍可点击该链接浏览官网: libgdx.com
笔者通过官方的入门教程实现了入门游戏-接雨滴,期间也遇到不少未知的问题,好在都一一解决并收获了新的知识。写这篇博客的目的是方便以后回顾复习,并能帮助到有需要的同学。
以下链接是官方的入门教程:libgdx.com/wiki/start/…
2. 准备开发环境
开始使用 libGDX 之前首先要准备好必须得开发环境,确保电脑上安装有JDK、IDE。
2.1. JDK
官方要求 JDK 11 以及以上版本,我这里按照官方的推荐,使用的 Adoptium JDK 11。
如果网速可以的话直接点击官方的链接去 Adoptium 官网下载 JDK 即可。 adoptium.net/zh-CN/temur…
我这里下载太慢,是在清华开源软件镜像站下载的:mirrors.tuna.tsinghua.edu.cn/Adoptium/11…
2.2. IDE
IDE 我一直是用的 IntelliJ IDEA,没有的同学可以去 JetBrains 官网下载社区版,或者自行下载使用其它 libGDX 推荐的开发工具。 www.jetbrains.com/idea/downlo…
3. 生成项目
3.1. 下载项目生成工具
点击下面链接下载 libGDX 项目生成工具,或者在官方教程页面点击下载链接下载
libgdx-nightlies.s3.amazonaws.com/libgdx-runn…
3.2. Gradle
libGDX 项目是一个 Gradle 项目,所以在生成项目前要确保电脑上装有合适的版本的Gradle,才能构建成功。可以在官方教程页面点击 Gradle 链接到 Gradle 官网下载。
我这里由于官网下载速度太慢,是在腾讯镜像库下载的,我使用的 8.5 版本。mirrors.cloud.tencent.com/gradle/
3.3. 生成项目
运行生成工具(如果打不开的话自行配置 java 环境变量),打开后如下图:
基本的填入信息、可选项这里不做解释,官方教程写的很详细。
需要注意的是,由于生成的项目默认使用的 Maven 中央仓库,国内网络下载 jar 包的话特别慢,我们可以点击 Advanced 按钮在 Maven Mirror URL 这一栏填入阿里的 Maven 库,免得生成项目后再去 build.gradle 文件中修改。
阿里 Maven 库 URL:maven.aliyun.com/repository/…
如果勾选了支持 Android 平台,是要求有安装 Android SDK 的。我这里只勾选了支持桌面。
都填好之后,点击 Generate 按钮生成项目。
4. 导入和运行
4.1. 导入项目
打开 IntelliJ IDEA,点击 Open 按钮,选择刚才通过工具生成的项目。
4.2. 构建项目
修改 Gradle 的设置,使用自己本地安装的 8.5 版本,JVM 使用 Adoptium JDK 11。
Project Settings 中设置 SDK 为 Adoptium JDK 11, Language level 为 11。
设置完成后,用 Gradle 重新构建项目。
4.3. gdx-websockets 版本问题解决
如果直接构建成功,这一节可以跳过。
我这里报了下图错误:
- Could not find com.github.MrStahlfelge.gdx-websockets:common:1.1.0.
- Could not find com.github.MrStahlfelge.gdx-websockets:core:1.1.0.
报错原因是工具生成的 build.gradle 中 com.github.MrStahlfelge.gdx-websockets 的 common 和 core 两个包版本都是 1.1.0,如下图所示。
需要去 github 找到这个项目最新可用的版本,手动修改版本号。
当前最新版本为 1.9.10.3
4.4. 运行项目
项目构建成功之后,运行 desktop 模块下的启动类 DesktopLauncher,出现下图窗口,证明运行成功。
5. 接雨滴
5.1. 资产
在开始写代码之前,先把运行游戏所需要的图片、音效准备好,也就是所谓的资产,放在 assets 模块下。
我这里并没有用官方文档给的链接中的资产,图片是自己画的,音效是在素材网站找的。
我用的素材网站:v.docin.com
我的建议是自己动手,准备好桶、雨滴的图片,游戏背景音乐还有雨滴音效,虽然麻烦了一点,但是过程是很有趣的。
5.2. 编程
程序的话我建议跟着官方文档的步骤来,一边理解代码的含义,一边手写,不要直接把完整代码粘过来直接用。
最后写完之后,对照着官方给出的完整代码比较一下,有没有写错的、遗漏的地方。
下面贴一下我的代码,增加了展示得分功能,优化了接雨滴判定。
package com.yfgy.drop;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
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.ScreenUtils;
import com.badlogic.gdx.utils.TimeUtils;
import java.util.Iterator;
public class Drop extends ApplicationAdapter {
private Texture dropImg;
private Texture bucketImg;
private Sound dropSound;
private Music rainMusic;
private OrthographicCamera camera;
private SpriteBatch batch;
private Rectangle bucket;
private Array<Rectangle> raindrops;
private long lastDropTime;
private BitmapFont font;
private static final String SCORE_STR = "score:";
private int score = 0;
@Override
public void create () {
//加载雨滴图片
dropImg = new Texture(Gdx.files.internal("droplet.png"));
//加载桶图片
bucketImg = new Texture(Gdx.files.internal("bucket.png"));
font = new BitmapFont();
//加载雨滴音效
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
//加载主题音乐
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
//设置音乐循环播放
rainMusic.setLooping(true);
//播放音乐
rainMusic.play();
//创建相机
camera = new OrthographicCamera();
//设置相机显示区域
camera.setToOrtho(false, 800, 480);
//循环批
batch = new SpriteBatch();
//创建桶模型
bucket = new Rectangle();
bucket.x = 800 / 2 - 64 / 2;
bucket.y = 20;
bucket.width = 64;
bucket.height = 64;
//创建雨滴模型集合
raindrops = new Array<>();
//产生一个雨滴
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 () {
//清除深蓝色屏幕
ScreenUtils.clear(0, 0, 0.2f, 1);
//更新相机
camera.update();
//使用相机指定的坐标系
batch.setProjectionMatrix(camera.combined);
//开始一个批次
batch.begin();
//渲染桶
batch.draw(bucketImg, bucket.x, bucket.y);
//渲染雨滴
for (Rectangle raindrop : raindrops) {
batch.draw(dropImg, raindrop.x, raindrop.y);
}
font.draw(batch, SCORE_STR + score, 700, 450);
batch.end();
//如果有鼠标、触屏输入
if (Gdx.input.isTouched()) {
//创建3维向量
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(Input.Keys.LEFT))
//桶横坐标左移 每秒 200 像素/单位
bucket.x -= 200 * Gdx.graphics.getDeltaTime();
//如果有键盘右方向键输入,桶横坐标右移 每秒 200 像素
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT))
bucket.x += 200 * Gdx.graphics.getDeltaTime();
//限制桶不超出相机范围
if (bucket.x < 0)
bucket.x = 0;
if (bucket.x > 800 - 64)
bucket.x = 800 - 64;
//如果上次生成雨滴时间超过1秒,新生成1个雨滴
if (TimeUtils.nanoTime() - lastDropTime > 1000000000)
spawnRaindrop();
//雨滴每秒 200 像素单位向下移动
for (Iterator<Rectangle> iterator = raindrops.iterator(); iterator.hasNext(); ) {
Rectangle raindrop = iterator.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
//超出x轴范围就删除
if (raindrop.y + 64 < 0)
iterator.remove();
if (raindrop.overlaps(bucket)) {
iterator.remove();
if (overlaps(raindrop, bucket)) {
dropSound.play();
score++;
}
}
}
}
/**
* 自定义 桶接住雨滴的 判定
* @param raindrop
* @param bucket
* @return
*/
private boolean overlaps(Rectangle raindrop, Rectangle bucket) {
return raindrop.x + raindrop.width / 2 > bucket.x
&& raindrop.x + raindrop.width / 2 < bucket.x + bucket.width
&& raindrop.y > bucket.y + 0.8 * bucket.height
&& raindrop.y < bucket.y + bucket.height;
}
@Override
public void dispose () {
dropImg.dispose();
bucketImg.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
}
}
5.3. 成品
游戏运行的截图