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

  1. 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;
    }
}
  1. 代码示例
    这个示例中除了使用到 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 接口的所有方法。<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);
    }
}

运行结果:


作者:xietansheng
来源:CSDN
原文:https://blog.csdn.net/xietansheng/article/details/50187415
版权声明:本文为博主原创文章,转载请附上博文链接!

发表评论

邮箱地址不会被公开。 必填项已用*标注