LibGDX_4.7: 场景(Screen)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xietansheng/article/details/50187415

本文链接: 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 之间的关系如下图所示:

sy_framework.png

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 接口的所有方法。<br/>
 * 这个场景使用 LibGDX 的官方 logo 居中显示 3 秒钟当做是游戏的欢迎界面。 <br/><br/>
 * 
 * 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 类 <br/>
 * 这里就展示一张图片代表游戏主界面
 */
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);
    }
}

运行结果:

sy_result.png


展开阅读全文

没有更多推荐了,返回首页