本文链接: http://blog.csdn.net/xietansheng/article/details/50187415
LibGDX 基础教程(总目录)
场景(Screen)是游戏框架中舞台(Stage)的父节点,一个场景可以理解为一个游戏界面,类似 Android 中的 Activity,一个游戏由多个场景(界面)组成,通常包括开始界面、游戏/关卡界面、帮助界面等。一个场景中可以包含一个或多个舞台。
LibGDX API 中提供的 Screen 仅仅是一个接口,我们需要自定义场景类实现 Screen 接口,并结合 com.badlogic.gdx.Game 类进行使用,Game 是一个抽象类,直接继承自 Object,并实现了 ApplicationListener 接口(即属于游戏主程序的入口类),Game 可以看做是 Screen 的父节点或游戏框架的最顶层节点,可以将 Screen 添加到 Game 中,Game 负责管理 Screen 和其生命周期方法的调用。
Game,Screen,Stage,Actor 之间的关系如下图所示:
Screen 的生命周期方法和 ApplicationListener 生命周期方法的调用相似,实际是将 ApplicationListener 委派给了 Screen。查看 Game 类的源码可以很容易看出 Game 对 Screen 的处理。
Screen 接口中共有下面 7 个方法:
/**
* 当该场景被设置到 Game 中成为 Game 的当前场景时被调用
*/
public void show();
public void resize(int width, int height);
/**
* 当该场景需要被渲染时被调用
*/
public void render(float delta);
public void pause();
public void resume();
/**
* 当有另一个场景被设置为 Game 的当前场景时(即该场景被覆盖/移出当前场景)被调用
*/
public void hide();
/**
* 当场景需要被释放所有资源时调用,
* 注意: 该方法不会自动被调用, 在需要释放场景的所有资源时手动进行调用
*/
public void dispose();
Game 类的实现其实非常简单,为了更好的理解 Game 对 Screen 的处理,下面展示一下 com.badlogic.gdx.Game 类中的源码:
package com.badlogic.gdx;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
/**
* Game 实现了 ApplicationListener 接口, 是游戏主程序的启动入口类
*/
public abstract class Game implements ApplicationListener {
// Game 中的当前需要被渲染的场景
protected Screen screen;
@Override
public void dispose () {
if (screen != null) {
// 当应用被销毁时调用一次当前 Screen 的 hide() 方法
screen.hide();
// 注意: 这里没有调用 Screen 的 dispose() 方法, 所以需要在适当时机自己手动调用
}
}
@Override
public void pause () {
if (screen != null) {
screen.pause();
}
}
@Override
public void resume () {
if (screen != null) {
screen.resume();
}
}
@Override
public void render () {
if (screen != null) {
// 游戏被渲染时调用当前 Screen 的 render() 方法, 并将渲染时间步(delta)传递给 screen
screen.render(Gdx.graphics.getDeltaTime());
}
}
@Override
public void resize (int width, int height) {
if (screen != null) {
screen.resize(width, height);
}
}
/**
* 设置 Game 的当前需要渲染的场景时;
* 先调用之前 Game 中旧的当前场景的 hide() 方法;
* 然后调用新设置到 Game 中的当前场景的 show() 方法, 并接着调用一次 resize() 方法;
*/
public void setScreen (Screen screen) {
if (this.screen != null) {
this.screen.hide();
}
this.screen = screen;
if (this.screen != null) {
this.screen.show();
this.screen.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
}
public Screen getScreen () {
return screen;
}
}
这个示例中除了使用到 badlogic.jpg 图片外,还需要使用到下面这张图片(LibGDX 的官方 logo),保存图片到本地 -> 重命名为 “logo.png” -> 然后复制到 “assets” 资源文件夹中。
文件名:logo.png (300*50):
先引用前面章节自定义的演员类:
package com.libgdx.test;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
/**
* 自定义演员
*/
public class MyActor extends Actor {
private TextureRegion region;
public MyActor(TextureRegion region) {
super();
this.region = region;
setSize(this.region.getRegionWidth(), this.region.getRegionHeight());
}
public TextureRegion getRegion() {
return region;
}
public void setRegion(TextureRegion region) {
this.region = region;
setSize(this.region.getRegionWidth(), this.region.getRegionHeight());
}
@Override
public void act(float delta) {
super.act(delta);
}
@Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
if (region == null || !isVisible()) {
return;
}
batch.draw(
region,
getX(), getY(),
getOriginX(), getOriginY(),
getWidth(), getHeight(),
getScaleX(), getScaleY(),
getRotation()
);
}
}
开始场景(欢迎界面):
package com.libgdx.test;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.StretchViewport;
/**
* 开始场景(欢迎界面), 实现 Screen 接口 或者 继承 ScreenAdapter 类, ScreenAdapter 类空实现了 Screen 接口的所有方法。
* 这个场景使用 LibGDX 的官方 logo 居中显示 3 秒钟当做是游戏的欢迎界面。
*
* PS: 类似 Screen 这样的有许多方法的接口, 更多时候只需要实现其中一两个方法, 往往会有一个对应的便捷的空实现所有接口方法的 XXAdapter 类,
* 例如 ApplicationListener >> ApplicationAdapter, InputProcessor >> InputAdapter
*/
public class StartScreen implements Screen {
// 为了方便与 MainGame 进行交互, 创建 Screen 时将 MainGame 作为参数传进来
private MainGame mainGame;
private Texture logoTexture;
private Stage stage;
private MyActor logoActor;
// 渲染时间步累计变量(当前场景被展示的时间总和)
private float deltaSum;
public StartScreen(MainGame mainGame) {
this.mainGame = mainGame;
// 在 Screen 中没有 create() 方法, show() 方法有可能被调用多次, 所有一般在构造方法中做一些初始化操作较好
// 创建 logo 的纹理, 图片 logo.png 的宽高为 300 * 50
logoTexture = new Texture(Gdx.files.internal("logo.png"));
// 使用伸展视口创建舞台
stage = new Stage(new StretchViewport(MainGame.WORLD_WIDTH, MainGame.WORLD_HEIGHT));
// 创建 logo 演员
logoActor = new MyActor(new TextureRegion(logoTexture));
// 将演员设置到舞台中心
logoActor.setPosition(
stage.getWidth() / 2 - logoActor.getWidth() / 2,
stage.getHeight() / 2 - logoActor.getHeight() / 2
);
// 添加演员到舞台
stage.addActor(logoActor);
}
@Override
public void show() {
deltaSum = 0;
}
@Override
public void render(float delta) {
// 累计渲染时间步
deltaSum += delta;
if (deltaSum >= 3.0F) {
// 开始场景展示时间超过 3 秒, 通知 MainGame 切换场景(启动主游戏界面)
if (mainGame != null) {
mainGame.showGameScreen();
return;
}
}
// 使用淡蓝色清屏
Gdx.gl.glClearColor(0.75F, 1, 0.98F, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// 更新舞台逻辑
stage.act();
// 绘制舞台
stage.draw();
}
@Override
public void resize(int width, int height) {
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void hide() {
}
@Override
public void dispose() {
// 场景被销毁时释放资源
if (stage != null) {
stage.dispose();
}
if (logoTexture != null) {
logoTexture.dispose();
}
}
}
主游戏场景(游戏主界面):
package com.libgdx.test;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.StretchViewport;
/**
* 主游戏场景(游戏主界面), 实现 Screen 接口 或者 继承 ScreenAdapter 类
* 这里就展示一张图片代表游戏主界面
*/
public class GameScreen extends ScreenAdapter {
private Texture manTexture;
private Stage stage;
private MyActor manActor;
public GameScreen() {
// 创游戏人物的纹理, 图片 badlogic.jpg 的宽高为 256 * 256
manTexture = new Texture(Gdx.files.internal("badlogic.jpg"));
// 使用伸展视口创建舞台
stage = new Stage(new StretchViewport(MainGame.WORLD_WIDTH, MainGame.WORLD_HEIGHT));
// 创建游戏人物演员
manActor = new MyActor(new TextureRegion(manTexture));
// 添加演员到舞台
stage.addActor(manActor);
}
@Override
public void render(float delta) {
// 红色清屏
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// 更新舞台逻辑
stage.act();
// 绘制舞台
stage.draw();
}
@Override
public void dispose() {
super.dispose();
// 场景被销毁时释放资源
if (stage != null) {
stage.dispose();
}
if (manTexture != null) {
manTexture.dispose();
}
}
}
游戏主程序的启动入口类:
package com.libgdx.test;
import com.badlogic.gdx.Game;
/**
* 游戏主程序的启动入口类, 要使用场景需要将 MainGame 改为继承 Game 抽象类
*/
public class MainGame extends Game {
// 视口世界的宽高统使用 480 * 800, 并统一使用伸展视口(StretchViewport)
public static final float WORLD_WIDTH = 480;
public static final float WORLD_HEIGHT = 800;
// 开始场景
private StartScreen startScreen;
// 主游戏场景
private GameScreen gameScreen;
@Override
public void create() {
// 创建开始场景
startScreen = new StartScreen(this);
// 创建主游戏场景
gameScreen = new GameScreen();
// 设置当前场景为开始场景
setScreen(startScreen);
}
/**
* 开始场景展示完毕后调用该方法切换到主游戏场景
*/
public void showGameScreen() {
// 设置当前场景为主游戏场景
setScreen(gameScreen);
if (startScreen != null) {
// 由于 StartScreen 只有在游戏启动时展示一下, 之后都不需要展示,
// 所以启动完 GameScreen 后手动调用 StartScreen 的 dispose() 方法销毁开始场景。
startScreen.dispose();
// 场景销毁后, 场景变量值空, 防止二次调用 dispose() 方法
startScreen = null;
}
}
@Override
public void dispose() {
super.dispose(); // super.dispose() 不能删除, 在父类中还有其他操作(调用当前场景的 hide 方法)
// 游戏程序退出时, 手动销毁还没有被销毁的场景
if (startScreen != null) {
startScreen.dispose();
startScreen = null;
}
if (gameScreen != null) {
gameScreen.dispose();
gameScreen = null;
}
}
}
温馨提示:对于 Desktop 平台,窗口(屏幕)的大小可以自己手动设置,而程序中使用的是伸展视口(StretchViewport),为了最终显示的内容不会被压扁或拉长,可以将窗口的宽高比设置成和视口世界的宽高比一致。Desktop 平台启动器配置参考如下:
package com.libgdx.test;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.libgdx.test.MainGame;
/**
* Desktop 平台启动器
*/
public class DesktopLauncher {
public static void main(String[] args) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
float scale = 0.6F; // 适当改变窗口缩放比以适应自己的电脑屏幕
/*
* 窗口(实际屏幕)宽高比设置为 480:800, 与视口世界的宽高比相同, 所以最终显示到屏幕上的内容将不会被压扁或拉长
*/
config.width = (int) (480 * scale); // 窗口宽度
config.height = (int) (800 * scale); // 窗口高度
config.resizable = false; // 窗口设置为大小不可改变
new LwjglApplication(new MainGame(), config);
}
}
运行结果: