目录
前言
在前面三章中(libGDX跨平台游戏开发框架入门:搭建开发环境和生成libGDX项目_汤姆猫不是猫的博客-CSDN博客_libgdx框架),(libGDX跨平台游戏开发框架入门:运行libGDX项目_汤姆猫不是猫的博客-CSDN博客),(libGDX跨平台游戏开发框架入门:开发一个小游戏之加载资源_汤姆猫不是猫的博客-CSDN博客)我们简单介绍了如何搭建libGDX开发环境和如何运行项目,以及项目中的模块作用等情况,对libGDX有了基本认识,以及通过加载资源篇,了解了libGDX资源管理相关的api内容,本章,我们将通过代码了解libGDX的图形渲染api。
为了更加清楚的描述每个api作用,我们将使用大量的代码和描述,来阐述每个api的作用和用途,所以我们将开发小游戏分为四个部分讲解,由于篇幅限制,我们分为三章讲解各个部分主要内容,最后一篇进行汇总: 1. 加载资源篇 2. 图形渲染、清屏和音效播放篇 3. 鼠标键盘输入和移动篇 4. 小游戏完整版汇总篇
本篇为渲染和清屏篇
渲染桶
是时候渲染我们的桶了。我们要做的第一件事是用深蓝色清除屏幕。只需将render()方法更改为如下所示: ```java
@Override
public void render() {
ScreenUtils.clear(0, 0, 0.2f, 1);
... more to come here ...
} ``` ScreenUtils.clear(r, g, b, a)中参数分别表示的是颜色中的红色、绿色、蓝色和 alpha 分量,每个分量都在 [0, 1] 范围内。
接下来,我们需要告诉我们的相机确保它已更新。相机使用称为矩阵的数学实体,负责设置用于渲染的坐标系。每次我们更改camera的属性(例如:位置)时,都需要重新计算这些矩阵。在我们的简单示例中,我们不这样做,但通常每帧更新一次相机是一个好习惯:
```java
camera.update();
``` 现在我们可以渲染我们的桶:
```java
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(bucketImage, bucket.x, bucket.y);
batch.end();
``` 第一行告诉SpriteBatch使用camera指定的坐标系。如前所述,这是通过称为投影矩阵的东西完成的,camera.combined字段就是用来指定这种矩阵。从这里开始SpriteBatch将渲染坐标系中的所有内容。
接下来我们告诉SpriteBatch开始一个新的批量任务。为什么我们需要这个批量任务?这涉及到OpenGL渲染原理,理论上来说,如果每个图像都要让GPU去渲染一次,那样一个屏幕上这么多图像,每个都要渲染一次,显然效率太低。所以我们一次性把很多图像渲染任务一起交给opengl让它希望帮我们一次渲染出来,这样可以有效增加游戏帧率,降低gpu占用。
使用SpriteBatch有助于优化OpenGL渲染效率。它将记录SpriteBatch.begin()和之间的所有绘图命令SpriteBatch.end()。一旦我们调用SpriteBatch.end()后,它将立即提交我们提出的所有绘图渲染请求,从而大大加快渲染速度。这一切在开始时可能看起来很麻烦,但这正是以每秒 60 帧的速度渲染 500 个精灵和以每秒 20 帧的速度渲染 100 个精灵之间的区别。
添加雨滴和渲染雨滴
对于雨滴,我们保存一个Rectangle列表,每个Rectangle实例都保存雨滴的位置和大小。 ```java
private Array raindrops; ```
该Array数组是用来取代标准的Java集合像libGDX实用工具类ArrayList。后者的问题在于它们以各种方式产生垃圾。本Array类尝试尽量减少垃圾。libGDX 还提供其他垃圾收集器感知集合,例如hash-maps 或者sets 。
我们还需要跟踪上次生成雨滴的时间,因此我们添加另一个字段: ```java
private long lastDropTime; ```
我们将以纳秒为单位存储时间,这就是我们使用 long 的原因。
为了方便创建雨滴,我们将编写一个名为spawnRaindrop()的方法,该方法实例化一个Rectangle对象,并将其设置到屏幕顶部边缘的随机位置且添加到raindrops数组中。 ```java
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是一个libGDX类提供各种数学计算相关的静态方法。在这种情况下,它将返回一个介于 0 和 800 - 64 之间的随机值。TimeUtils是另一个 libGDX 类,它提供了一些非常基本的日期时间相关的静态方法。在这种情况下,我们以纳秒为单位记录当前时间,稍后我们将根据该时间决定是否产生新的水滴。
在该create()方法中,我们现在实例化雨滴数组并生成我们的第一个雨滴:
我们需要在create()方法中实例化该数组: ```
raindrops = new Array(); spawnRaindrop();
```
接下来,我们向该render()方法添加几行代码,用于检查自我们产生新雨滴以来已经过去了多长时间,并在必要时创建一个新雨滴: ```java
if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop(); ```
我们还需要让雨滴移动,让雨滴以每秒 200 像素/单位的恒定速度移动。如果雨滴在屏幕底部边缘之下,则将其从数组中移除。 ```java
for (Iterator iter = raindrops.iterator(); iter.hasNext(); ) {
Rectangle raindrop = iter.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
if(raindrop.y + 64 < 0)
iter.remove();
}
```
我们还需要渲染雨滴。把雨滴添加到SpriteBatch渲染代码中: ```java
batch.begin();
batch.draw(bucketImage, bucket.x, bucket.y);
for(Rectangle raindrop: raindrops) {
batch.draw(dropImage, raindrop.x, raindrop.y);
}
batch.end();
```
最后一个判断逻辑:如果雨滴击中了桶,我们想要播放落下的声音并从数组中移除雨滴。只需将以下几行添加到雨滴的更新循环中: ```java
if(raindrop.overlaps(bucket)) {
dropSound.play();
iter.remove();
}
```
Rectangle.overlaps()方法检查矩形是否与另一个矩形重叠。在这个例子中,我们让水滴音效自行播放并从数组中移除雨滴。
清屏和回收对象实例
用户可以随时关闭应用程序。对于我们这个简单的小游戏demo,没有什么特别需要做的事情。但一般来说,稍微清理我们之前创建的几个对象实例有利于降低内存占用,提高游戏运行性能。
任何实现Disposable接口并因此具有dispose()方法的libGDX类在不再使用后都需要手动清理。在我们的示例中,对于纹理、声音和音乐以及SpriteBatch. 作为优秀的程序猿,我们重写该ApplicationAdapter.dispose()方法如下: ```java
@Override public void dispose() {
dropImage.dispose();
bucketImage.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
}
```
一旦回收了资源,就不应以任何方式访问它。
Disposables 通常是 Java 垃圾收集器不处理的资源。这就是我们需要手动处理它们的原因。libGDX 提供了多种方式来帮助进行资源管理。
到这里,我们已经了解了libGDX资源管理和图形渲染的api、音效播放以及如何清理对象实例,回收内存的内容。下一章我们将讲解如何使用鼠标和键盘等输入设备,然后通过鼠标或者键盘移动游戏中的精灵物体。
如果觉得博主写得还不错,欢迎“关注、点赞、收藏” 一键三连。