手把手教你写蛇蛇大作战

206 阅读17分钟
原文链接: www.zhangman523.cn

手把手教你写蛇蛇大作战(六)

这篇我们实现的功能主要是菜单界面的编写,还有设置控制。

效果图

首先还是老样子,先看图。

效果图

先准备好素材图

素材

这个是我打包后的图片,具体小图片可以去我的GITHUB 下载

代码优化

这次我把代码整理了一下,分了下包。看起来结构明显。

代码结构如下图

结构

由于需要多个界面,我们抽象出DirectedGame.javaAbstractGameScreen.java

  • DirectedGame.java 主要管理界面的切换,页面间的切换动画。
  • AbstractGameScreen.java 抽象的界面,所有的界面的夫类。

DirectedGame.java

public class DirectedGame implements ApplicationListener {
    private boolean init;
    private AbstractGameScreen currScreen;
    private AbstractGameScreen nextScreen;
    private FrameBuffer currFbo;
    private FrameBuffer nextFbo;
    private SpriteBatch batch;
    private float t;
 
    private ScreenTransition screenTransition;
 
    public void setScreen(AbstractGameScreen screen) {
        setScreen(screen, null);
    }
 
    public void setScreen(AbstractGameScreen screen, ScreenTransition screenTransition) {
        int w = Gdx.graphics.getWidth();
        int h = Gdx.graphics.getHeight();
        if (!init) {
            currFbo = new FrameBuffer(Pixmap.Format.RGB888, w, h, false);
            nextFbo = new FrameBuffer(Pixmap.Format.RGB888, w, h, false);
            batch = new SpriteBatch();
            init = true;
        }
        // start  new transition;
        nextScreen = screen;
        nextScreen.show(); //active next screen
        nextScreen.resize(w, h);
        nextScreen.render(0);// let screen update() once
        if (currScreen != null) currScreen.pause();
        nextScreen.pause();
        Gdx.input.setInputProcessor(null);//disable input
        this.screenTransition = screenTransition;
        t = 0;
    }
 
    @Override
    public void create() {
 
    }
 
    @Override
    public void resize(int width, int height) {
        if (currScreen != null) currScreen.resize(width, height);
        if (nextScreen != null) nextScreen.resize(width, height);
    }
 
    @Override
    public void render() {
        // get delta time and ensure an upper limit of one 60th second
        float deltaTime = Math.min(Gdx.graphics.getDeltaTime(), 1.0f / 60.0f);
        if (nextScreen == null) {
            // no ongoing transition
            if (currScreen != null) currScreen.render(deltaTime);
        } else {
            // ongoing transition
            float duration = 0;
            if (screenTransition != null) {
                duration = screenTransition.getDuration();
            }
            //update progress of ongoing transition
            t = Math.min(t + deltaTime, duration);
            if (screenTransition == null || t >= duration) {
                // no transition effect set or transition has just finished
                if (currScreen != null) currScreen.hide();
                nextScreen.resume();
                // enable input for next screen
                Gdx.input.setInputProcessor(nextScreen.getInputProcessor());
                // switch screens
                currScreen = nextScreen;
                nextScreen = null;
                screenTransition = null;
            } else {
                // render screens to FBOs
                currFbo.begin();
                if (currScreen != null) currScreen.render(deltaTime);
                currFbo.end();
                nextFbo.begin();
                nextScreen.render(deltaTime);
                nextFbo.end();
                //render transition effect to screen
                float alpha = t / duration;
                screenTransition.render(batch, currFbo.getColorBufferTexture(), nextFbo.getColorBufferTexture(), alpha);
            }
        }
    }
 
    @Override
    public void pause() {
        if (currScreen != null) currScreen.pause();
    }
 
    @Override
    public void resume() {
        if (currScreen != null) currScreen.resume();
    }
 
    @Override
    public void dispose() {
        if (currScreen != null) currScreen.hide();
        if (nextScreen != null) nextScreen.hide();
        if (init) {
            currFbo.dispose();
            currScreen = null;
            nextFbo.dispose();
            nextScreen = null;
            batch.dispose();
            init = false;
        }
    }
}

AbstractGameScreen.java

public abstract class AbstractGameScreen implements Screen {
    protected DirectedGame game;
 
    public AbstractGameScreen(DirectedGame game) {
        this.game = game;
    }
 
    @Override
    public abstract void show();
 
    @Override
    public abstract void render(float delta);
 
    @Override
    public abstract void resize(int width, int height);
 
    @Override
    public abstract void hide();
 
    @Override
    public abstract void pause();
 
    public abstract InputProcessor getInputProcessor();
 
    @Override
    public void resume() {
        Assets.instance.init(new AssetManager());
    }
 
    @Override
    public void dispose() {
        Assets.instance.dispose();
    }
}

添加完几个类,我们把SnakeIo也改为继承DirectedGame 然后新建GameScreen.java

这个类的主要职责就是原来SnakeIo一样的。大致搬过来。

看看主要实现

public class GameScreen extends AbstractGameScreen {
    private WorldController worldController;
    private WorldRenderer worldRenderer;
 
    private boolean paused;//游戏暂停
 
    public GameScreen(DirectedGame game) {
        super(game);
    }
 
    @Override
    public void show() {
        worldController = new WorldController(game);
        worldRenderer = new WorldRenderer(worldController);
        Gdx.input.setCatchBackKey(true);//获取Android返回键
    }
 
    @Override
    public void render(float delta) {
        if (!paused) {//如果游戏进入后台 不更新游戏
            worldController.update(delta);
        }
        Gdx.gl.glClearColor(0, 0, 0, 0);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        worldRenderer.render();
    }
 
    @Override
    public void resize(int width, int height) {
        worldRenderer.resize(width, height);
    }
 
    @Override
    public void hide() {
        worldController.dispose();
        worldRenderer.dispose();
        Gdx.input.setCatchBackKey(false);
    }
 
    @Override
    public void pause() {
        paused = true;
        AudioManager.instance.stopMusic();//暂停
    }
 
    @Override
    public void resume() {
        super.resume();
        paused = false;
        AudioManager.instance.play(Assets.instance.sounds.bm);//播放游戏背景音乐
    }
 
    @Override
    public InputProcessor getInputProcessor() {
        return worldController;
    }
}

菜单类

首先介绍下绘制菜单界面的主要逻辑。

绘制菜单我们需要一个舞台Stage

然后把游戏背景先添加add(),然后添加设置按钮,游戏logo,无尽模式,团战模
式,挑战模式,赏金模式 Button。

我们看下具体实现

MenuScreen.java

public class MenuScreen extends AbstractGameScreen {
 
    private Stage stage;//舞台
    private Skin snakeSkin;//皮肤,就是上面那个图片
    private Skin defaultSkin;//这个是LibGdx 的默认皮肤,由于没找到合适的素材,所以设置Window 和CheckBox 的样式都是用到默认的。
 
    private Image imgBackground;//背景
    private Image imgLogo;//logo
    private Button endlessModel;//无尽模式
    private Button teamModel;//团战模式
    private Button challengeModel;//挑战模式
    private Button robcoinModel;//赏金模式
    private Button setting;//设置
 
    private Window winSetting;//设置windown
    private CheckBox chkSound;//声音CheckBox
    private CheckBox chkMusic;//音乐CheckBox
    private CheckBox leftOp;//TouchPad 在左CheckBox
    private CheckBox rightOp;//TouchPad 在有CheckBox
    private TextButton btnWinSave;//保存按钮
    private TextButton btnWinCancel;//取消按钮
 
    private boolean debugEnable = false;//调试控制
 
    public MenuScreen(DirectedGame game) {
        super(game);
    }
 
    @Override
    public void show() {
        GamePreferences.instance.load();
        stage = new Stage(new StretchViewport(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT));
        buildStage();//构建舞台
    }
    /**
     * 构建舞台
     */
    private void buildStage() {
        snakeSkin = new Skin(Gdx.files.internal(Constants.SKIN_TOUCHPAD_UI),
                new TextureAtlas(Gdx.files.internal(Constants.TEXTURE_ATLAS_TOUCHPAD_UI)));
        defaultSkin = new Skin(Gdx.files.internal(Constants.SKIN_LIBGDX_UI),
                new TextureAtlas(Constants.TEXTURE_ATLAS_LIBGDX_UI));
        Table layerBackground = buildBackgroundLayer();
        Table layerLogo = buildLogoLayer();
        Table layerGame = buildGameModelLayer();
        Table layerSetting = buildSettingLayer();
        Table layerSettingWin = buildSettingWindowLayer();
 
        stage.clear();
        Stack stack = new Stack();//这里的堆栈是Libgdx 实现的一个视图的容器,可以分层的摆放个个容器
        stage.addActor(stack);//将堆栈添加进舞台
        stack.setSize(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT);
        stack.add(layerBackground);
        stack.add(layerLogo);
        stack.add(layerGame);
        stack.add(layerSetting);
        stage.addActor(layerSettingWin);
    }
 
    /**
     * 构建背景
     */
    private Table buildBackgroundLayer() {
        Table layer = new Table();
        imgBackground = new Image(snakeSkin, "home_bg");
        layer.add(imgBackground);
        return layer;
    }
    /**
     * 构建Logo
     */
    private Table buildLogoLayer() {
        Table layer = new Table();
        layer.center().top();
        imgLogo = new Image(snakeSkin, "snake_name_icon");
        imgLogo.setOrigin(imgLogo.getWidth() / 2, imgLogo.getHeight() / 2);
        imgLogo.setScale(0.5f);//由于找到的素材太大了,我们把它缩放为原来的一半
        layer.add(imgLogo);
        if (debugEnable) layer.debug();
        return layer;
    }
    /**
     * 构建 无尽模式,团战模式,挑战模式,赏金模式 
     */
    private Table buildGameModelLayer() {
        Table layer = new Table();//表格布局
        layer.center();//布局位置
        endlessModel = new Button(snakeSkin, "endless");
        layer.add(endlessModel).width(100).height(140).padLeft(5);//原图片素材比较大所以就给它一个高度
        teamModel = new Button(snakeSkin, "team");
        layer.add(teamModel).width(100).height(140).padLeft(5);
        Table childTable = new Table();//里面嵌套了个表格布局
        challengeModel = new Button(snakeSkin, "challenge");
        childTable.add(challengeModel).width(120).height(70);
        childTable.row();//换行
        robcoinModel = new Button(snakeSkin, "robcoin");
        childTable.add(robcoinModel).width(120).height(70);
        layer.add(childTable).padLeft(5);
        if (debugEnable) layer.debug();
 
        //监听时间
        endlessModel.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                playGame();
            }
        });
        teamModel.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                playGame();
            }
        });
        challengeModel.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                playGame();
            }
        });
        robcoinModel.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                playGame();
            }
        });
        return layer;
    }
     /**
     * 开始游戏,跳转进入游戏界面
     */
    private void playGame() {
        ScreenTransition transition = ScreenTransitionFade.init(0.75f);
        game.setScreen(new GameScreen(game), transition);
    }
    /**
     * 构建 设置按钮
     */
    private Table buildSettingLayer() {
        Table layer = new Table();
        layer.top().right();
        setting = new Button(snakeSkin, "setting");
        layer.add(setting).padRight(20).padTop(20);
        setting.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                showOptionsWindow(true, true);// 监听事件
            }
        });
        return layer;
    }
    /**
     * 构建 设置Window
     */
    private Table buildSettingWindowLayer() {
        winSetting = new Window("Setting", defaultSkin);
        winSetting.center();
        winSetting.add(buildSetWinAudioSettings()).row();
        winSetting.add(buildSetWinOpreateSettings()).row();
        winSetting.add(buildWinBtn());
        showOptionsWindow(false, false);
        winSetting.pack();
        winSetting.setPosition((Constants.VIEWPORT_GUI_WIDTH - winSetting.getWidth()) / 2, (Constants.VIEWPORT_GUI_HEIGHT - winSetting.getHeight()) / 2);
        return winSetting;
    }
    /**
     * 构建 游戏音效控制
     */
    private Table buildSetWinAudioSettings() {
        Table table = new Table();
        table.pad(10, 10, 0, 10);
        table.add(new Label("Audio", snakeSkin, "default-font", Color.RED)).colspan(3);
        table.row();
        chkSound = new CheckBox("", defaultSkin);
        table.add(chkSound).padLeft(10);
        table.add(new Label("Sound", defaultSkin)).padLeft(5);
        chkMusic = new CheckBox("", defaultSkin);
        table.add(chkMusic).padLeft(10);
        table.add(new Label("Music", defaultSkin)).padLeft(5);
        chkSound.setChecked(GamePreferences.instance.sound);
        chkMusic.setChecked(GamePreferences.instance.music);
        return table;
    }
    /**
     * 构建 控制TouchPad 在左或者在右
     */
    private Table buildSetWinOpreateSettings() {
        Table table = new Table();
        table.pad(10, 10, 0, 10);
        table.add(new Label("Operation", snakeSkin, "default-font", Color.RED)).colspan(3);
        table.row();
        leftOp = new CheckBox("", defaultSkin);
        table.add(leftOp).padLeft(10);
        table.add(new Label("Left", defaultSkin)).padLeft(5);
        rightOp = new CheckBox("", defaultSkin);
        table.add(rightOp).padLeft(10);
        table.add(new Label("Right", defaultSkin)).padLeft(5);
        ButtonGroup<CheckBox> buttonGroup = new ButtonGroup<CheckBox>();//由于CheckBox 是多选 需要加个ButtonGroup 变为单选
        buttonGroup.add(leftOp, rightOp);
        //读取配置
        leftOp.setChecked(GamePreferences.instance.touchPadLeft);
        rightOp.setChecked(!GamePreferences.instance.touchPadLeft);
        return table;
    }
    /**
     * 构建 保存和取消按钮
     */
    private Table buildWinBtn() {
        Table tbl = new Table();
        Label lbl = null;
        lbl = new Label("", defaultSkin);
        lbl.setColor(0.75f, 0.75f, 0.75f, 1);
        lbl.setStyle(new Label.LabelStyle(lbl.getStyle()));
        lbl.getStyle().background = defaultSkin.newDrawable("white");
        tbl.add(lbl).colspan(2).height(1).width(220).pad(0, 0, 0, 1);
        tbl.row();
        lbl = new Label("", defaultSkin);
        lbl.setColor(0.5f, 0.5f, 0.5f, 1);
        lbl.setStyle(new Label.LabelStyle(lbl.getStyle()));
        lbl.getStyle().background = defaultSkin.newDrawable("white");
        tbl.add(lbl).colspan(2).height(1).width(220).pad(0, 1, 5, 0);
        tbl.row();
        btnWinSave = new TextButton("Save", defaultSkin);
        tbl.add(btnWinSave).padRight(30);
        btnWinSave.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                onSaveClicked();
            }
        });
        btnWinCancel = new TextButton("Cancel", defaultSkin);
        tbl.add(btnWinCancel);
        btnWinCancel.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                showOptionsWindow(false, false);
            }
        });
        return tbl;
    }
 
    private void onSaveClicked() {
        showOptionsWindow(false, false);
        saveSettings();//
    }
 
    private void saveSettings() {
        GamePreferences prefs = GamePreferences.instance;
        prefs.sound = chkSound.isChecked();
        prefs.music = chkMusic.isChecked();
        prefs.touchPadLeft = leftOp.isChecked();
        prefs.save();
    }
 
    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        stage.act(delta);
        stage.draw();
    }
 
    @Override
    public void resize(int width, int height) {
        stage.getViewport().update(width, height, true);
    }
 
    @Override
    public void hide() {
        stage.dispose();
        snakeSkin.dispose();
        defaultSkin.dispose();
    }
 
    @Override
    public void pause() {
 
    }
 
    @Override
    public InputProcessor getInputProcessor() {
        return stage;
    }
    /**
     * 控制设置Window 是否显示,是否需要动画
     */ 
    public void showOptionsWindow(boolean visible, boolean animated) {
        float alphaTo = visible ? 0.8f : 0.0f;
        float duration = animated ? 1.0f : 0.0f;
        final Touchable touchEnable = visible ? Touchable.enabled : Touchable.disabled;
        winSetting.addAction(sequence(touchable(touchEnable), alpha(alphaTo, duration)));
    }
}

结语

菜单界面的功能基本实现了。可以去GITHUB 看完整代码。

转载请注明:zhangman523 » 手把手教你写蛇蛇大作战(六)

喜欢 (1)