手把手教你写蛇蛇大作战(六)
这篇我们实现的功能主要是菜单界面的编写,还有设置控制。
效果图
首先还是老样子,先看图。
先准备好素材图
这个是我打包后的图片,具体小图片可以去我的GITHUB 下载
代码优化
这次我把代码整理了一下,分了下包。看起来结构明显。
代码结构如下图
由于需要多个界面,我们抽象出DirectedGame.java
和 AbstractGameScreen.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)