LibGDX_4.7: 场景(Screen)

本文链接: http://blog.csdn.net/xietansheng/article/details/50187415

LibGDX 基础教程(总目录)

1. 概述

场景(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 之间的关系如下图所示:

LibGDX_4.7: 场景(Screen)_第1张图片

2. Screen 的生命周期方法

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;
	}
}

3. 代码示例

这个示例中除了使用到 badlogic.jpg 图片外,还需要使用到下面这张图片(LibGDX 的官方 logo),保存图片到本地 -> 重命名为 “logo.png” -> 然后复制到 “assets” 资源文件夹中。

文件名:logo.png (300*50):

logo.png

先引用前面章节自定义的演员类:

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

运行结果:

LibGDX_4.7: 场景(Screen)_第2张图片


你可能感兴趣的:(LibGDX)