使用OGEngine开发别踩白块儿
www.ogengine.com
别踩白块儿
“别踩白块儿”是目前非常火的一款游戏,游戏非常简单刺激。关于具体怎么火法怎么玩我就不多说了,相信看到本文的朋友们都非常地清楚。
什么游戏火,我们都想知道自己能不能也弄一个玩玩,我也花了点时间弄了一个,游戏代码将会开源,利人利己,大家一起提高,希望各位多多支持。
下面介绍如何用OGEngine游戏引擎完成“别踩白块儿”游戏的经典模式。 一、最终实现的部分效果截图
1、刚开始时,最下面有一栏为黄色,紧接着上面每一行都是有一个黑色块,其余为白色块,准备冲啊.....
www.ogengine.com
2、游戏中,在游戏过程中都有一个计时器,
当前所进行的时间。冲啊.....注意别点到了白块儿。
3、玩家如果误点了白色块,会出现游戏失败界面,如果之前有创立过最佳成绩,则会显示出来,记录的是完成任务所用的时间,时间越短说明你越厉害。点击“重来”即会重新开始。
www.ogengine.com
4、游戏进行到最后会出现绿色栏,游戏胜利界面,会显示本次所用时间,如果刷新了记录则标记“新纪录”字样,如果没有刷新记录则还会显示历史最佳记录。大伙努力挑战新记录吧~
www.ogengine.com
二、创建Block类 游戏中要用到很多方块,所以我们单独创建一个方块类Block。Block是一个个白块或者
黑块,单个块元素,这里我们的Block继承矩形Rectangle,每个Block用矩形来展示。
1、声明一些所需要的成员变量
public class Block extends Rectangle {
// 游戏场景
private GameScene mGameScene;
// 此block的颜色类型,白色还是黑色,
private int colorType;
// block 所在的行
private int row;
// block 所在的列
private int column;
// ======================get&set========================
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getColumn() {
return column;
}
public int getColorType() {
return colorType;
}
// =====================================================
www.ogengine.com
2、构造器1,初始化blocks时用到,当为最底部一行时设为黄色
/**
* 构造器1,初始化blocks时用到
* @param pGameScene 游戏场景
* @param row block所在的行
* @param column block所在的列
* @param pWidth block的宽
* @param pHeight block的高
* @param blackIndex 用来确定是否是黑块,如果blackIndex == column时设为黑块
* @param pVertexBufferObjectManager
*/
public Block(GameScene pGameScene, int row, int column, float pWidth,
float pHeight, int blackIndex,
VertexBufferObjectManager pVertexBufferObjectManager) {
super(column * pWidth, (3 - row) * pHeight, pWidth - 1, pHeight - 1,
pVertexBufferObjectManager);
this.mGameScene = pGameScene;
this.row = row;
this.column = column;
if (row == 0) {
// 第一行设置为黄块
this.setColor(Color.YELLOW);
} else {
// 初始化block的颜色数据,是白块还是黑块?
initBlockData(column, blackIndex);
}
// 设置可以相应触碰事件
this.setIgnoreTouch(false);
}
www.ogengine.com
3、构造器2,新增blocks时用到
/**
* 构造器2,新增blocks时用到
* @param pGameScene 游戏场景
* @param column block所在的列
* @param pWidth block的宽
* @param pHeight block的高
* @param blackIndex 来确定是否是黑块,如果blackIndex == column时设为黑块
* @param pVertexBufferObjectManager
*/
public Block(GameScene pGameScene, int column, float pWidth, float pHeight,
int blackIndex, VertexBufferObjectManager pVertexBufferObjectManager) {
super(column * pWidth, 0, pWidth - 1, pHeight - 1,
pVertexBufferObjectManager);
this.mGameScene = pGameScene;
this.column = column;
// 初始化block的颜色数据,是白块还是黑块?
initBlockData(column, blackIndex);
// 设置可以相应触碰事件
this.setIgnoreTouch(false);
}
4、实现函数initBlockData初始化block的颜色数据,是白块还是黑块
/**
* 初始化block的颜色数据,是白块还是黑块?
* @param column
* @param blackIndex
*/
private void initBlockData(int column, int blackIndex) {
if (blackIndex == column) {
// 设置为黑块
this.setColor(Color.BLACK);
this.colorType = ConstantUtil.COLOR_BLACK;
} else {
// 设置为白块
this.setColor(Color.WHITE);
this.colorType = ConstantUtil.COLOR_WHITE;
}
}
www.ogengine.com
5、每个block 实现触碰监听,当按下时,调起在GameScene中实现的touchBlock方法。
记得设置可以相应触碰事件setIgnoreTouch(false);
@Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
float pTouchAreaLocalX, float pTouchAreaLocalY) {
// 触碰事件监听
if (pSceneTouchEvent.isActionDown()) {
// 点击到Block时进行的逻辑处理
mGameScene.touchBlock(this);
}
return true;
}
三、创建GameScene类
GameScene类是游戏场景类,是本游戏的主要部分,主要实现游戏界面及相关逻辑。
1、先看看其成员变量
public class GameScene extends Scene { /** 块的宽 **/ private float blockWidth = 0; /** 块的高 **/ private float blockHight = 0; /** 用于装当前所有生成但未被删除的block **/ private List
blockList; /** 当前生成的块的行数 **/ private int linesLength = 5; /** 游戏状态 **/ private int gameStatus = ConstantUtil.GAME_START; /** 移动步数 **/ private int moveNum = 0; /** 成功界面 **/ private SuccGroup mSuccGroup; /** 失败界面 **/ private FailGroup mFailGroup; /** 计时管理类 **/ private TimerTool mTimerTool; /** 显示计时的Text **/ private Text timerText;
/**游戏提示语**/
private AnimatedSprite game_tip;
// 用于Z索引排序
www.ogengine.com
2、计算每个block 块的宽高
// 镜头里显示的是4*4的块,所以用镜头宽的四分之一作为块宽
blockWidth = this.getCameraWidth() / 4;
blockHight = this.getCameraHeight() / 4;
3、 初始blocks,先创建4*5表格,使用时候再一行行增加
/**
* 初始化blocks
*/
private void initBlocks() {
Random mRandom = new Random();
int blackIndex = 0;
// 初始blocks,先创建4*5表格,使用时候再一行行增加
for (int row = 0; row < linesLength; row++) {// 行
// 一行blocks
Block[] rowBolcks = new Block[4];
// 随机一个黑块所在位置
blackIndex = mRandom.nextInt(4);
for (int column = 0; column < 4; column++) {// 列
rowBolcks[column] = new Block(this, row, column, blockWidth,
blockHight, blackIndex, getVertexBufferObjectManager());
this.attachChild(rowBolcks[column]);
}
blockList.add(rowBolcks);
}
}
www.ogengine.com
四、游戏交互实现
1、前面已经介绍在 Block 类实现了每个block的触碰监听,block 实现触碰监听,当按下时,
调起在GameScene中实现的touchBlock方法。下面来看改方法的实现。
/**
* 点击到Block时进行的逻辑处理
*
* @param pBlock
* 所点击的block
*/
public void touchBlock(Block pBlock) {
if (gameStatus == ConstantUtil.GAME_START) {
if (pBlock.getRow() == moveNum + 2) {// 表示是在底部往上数的倒数第三行
// 判断是不是点击了该点击的黑块的上一格,如果是,我们也判定这是正确点击了,
做出相应移动
upBlockTouch(pBlock);
} else if (pBlock.getRow() == moveNum + 1) {// 表示是在底部往上数的倒数第二行
if (pBlock.getColorType() == ConstantUtil.COLOR_BLACK) {
if (linesLength == moveNum + 2) {
// 游戏胜利
gameSucc(pBlock);
} else {
// 整体blocks下移
moveDown(pBlock, 1);
}
} else if (pBlock.getColorType() == ConstantUtil.COLOR_WHITE) {
// 误点了白块,游戏失败
gameFail();
// 失败时pBlock的一个闪红效果
LoopEntityModifier loop = failAction();
// 播放效果
pBlock.registerEntityModifier(loop);
}
}
}
}
www.ogengine.com
上面的代码已经有所注释,这里再简单解析一下。
?理论上可以点击的一行为倒数第二行,如果点击到该行的黑块,则所有block向下移动一格,如果点击到该行的白块,则游戏失败,弹出失败界面。
?为了体验上的优化,如果点击到倒数第二行黑块所在列正对上的一个block(不管是黑块还是白块)也视为正确点击了黑块。因为在快速点击黑块的同时整体的block也迅速向下移动,很容易点击到上一格,此时我们认为这也是正确的点击,这样才能保证快速点击的连贯性。
2、判断是不是点击了该点击的黑块的上一格,如果是,我们也判定这是正确点击了,做出相应移动
/**
* 判断是不是点击了该点击的黑块的上一格,如果是,我们也判定这是正确点击了,
做出相应移动
*
* @param pBlock
* 所被点击的块
*/
private void upBlockTouch(Block pBlock) {
int touchColumn = pBlock.getColumn();
for (Block[] blocks : blockList) {
for (Block block : blocks) {
if (block.getRow() == moveNum + 1
&& block.getColorType() == ConstantUtil.COLOR_BLACK) {
if (block.getColumn() == touchColumn) {
// 整体blocks下移
moveDown(block, 1);
}
return;
}
}
}
}
www.ogengine.com
3、方块下移等相关逻辑moveDown方法的实现。
// 正确点击该点击的黑块,或者上一行的块,整体向下移动、创建新的一样块,改变黑块 private void moveDown(Block pBlock, int stepNum) { if(moveNum == 0){ // 开始计时 mTimerTool.start(); // 隐藏提示文字 game_tip.setVisible(false); } if (moveNum < ConstantUtil.LINES_LEN) { // 创建添加新的一行 createNewRowBolcks(); } else if (moveNum == ConstantUtil.LINES_LEN) { // 开始显示绿色胜利界面,即将胜利,但还没有胜利 showSuccGroup(); } // 被点击的黑块变灰 pBlock.setColor(0.63f, 0.63f, 0.63f); pBlock.registerEntityModifier(new ScaleModifier(0.1f, 0.5f, 1.0f)); // 移动步数加1 moveNum++; // 需要移动的距离 float moveDistance = this.getCameraHeight() - blockHight * stepNum - pBlock.getY(); for (Block[] rowBlocks : blockList) { for (Block block : rowBlocks) { // 遍历,所有block向下移动指定距离 moveToY(block, moveDistance); } } // 快到胜利出现绿色胜利界面时,胜利界面跟着移动 if (mSuccGroup.isVisible()) { moveToY(mSuccGroup, moveDistance); } }
?moveNum用来记录移动了多少步,第一次开始下移时,我们开始计时以及隐藏下边的提示文字。
?如果还没有达到预设的总行数就创建添加新的一行blocks,如果到了则开始显示绿色胜利界面。
www.ogengine.com
?被点击的黑块变灰色,加点小小的动作效果。
?计算出需要向下移动的距离,遍历所有blocks,向下移动指定距离。 ?快到胜利出现绿色胜利界面时,胜利界面跟着移动。
4、创建添加新的一行createNewRowBolcks()方法的实现。
//创建添加新的一行 private void createNewRowBolcks() { // 超出屏幕没用的部分移除掉 if (blockList.size() > 5) { Block[] rowBolcks = blockList.get(0); for (Block block : rowBolcks) { block.detachSelf(); } blockList.remove(0); } // 目前顶部的一行块的Y坐标 float topBlocksY = blockList.get(blockList.size() - 1)[0].getY(); // 一行块 Block[] rowBolcks = new Block[4]; int blackIndex = new Random().nextInt(4); for (int column = 0; column < 4; column++) { // 新创建 Block rowBolcks[column] = new Block(this, column, blockWidth, blockHight, blackIndex, getVertexBufferObjectManager()); // 设置Y坐标 rowBolcks[column].setBottomPositionY(topBlocksY - 1); // 设置已经是第几行 rowBolcks[column].setRow(linesLength); this.attachChild(rowBolcks[column]); } blockList.add(rowBolcks); // 按Z索引排序一下上下层顺序 this.sortChildren(); // 当前生成的块的行数增加 linesLength++; }
? 先把超出屏幕没用的部分移除掉,把新增的一行blocks的位置设在最顶端,设置已经是
第几行。
www.ogengine.com
5、游戏成功或者游戏失败
/** * 最后一个移动,游戏胜利 * * @param pBlock */ private void gameSucc(Block pBlock) { gameStatus = ConstantUtil.GAME_OVER; // 最后一个移动,游戏胜利 moveDown(pBlock, 0); // 停止计时 mTimerTool.stop(); // 显示游戏胜利画面 mSuccGroup.showItems(true); // 隐藏显示时间的Text timerText.setVisible(false); } /** * 点击错误后弹出红色的失败界面,游戏失败 */ private void gameFail() { gameStatus = ConstantUtil.GAME_OVER; // 游戏失败,停止计时 mTimerTool.stop(); // 隐藏显示时间的Text timerText.setVisible(false); }
www.ogengine.com
五、计时控制类TimerTool 的实现。
我们知道游戏一开始就会有时间的计时,完成指定任务后,时间越短越厉害,玩家通过
不断刷新记录来提高对游戏的成就感,所以计时、显示历史最佳记录必不可少。下面我们就
来看看如何实现计时。
我们使用OGEngine引擎中的TimerHandler来实现计时,我们先来了解一下
TimerHandler。从源码我们发现它实现IUpdateHandler接口。
public class TimerHandler implements IUpdateHandler{
再来看一下它的构造器
/** * @param pTimerSeconds 经过多少秒后执行 pTimerCallback 中的回调 * @param pAutoReset 是否循环执行 * @param pTimerCallback 回调函数 */ public TimerHandler(final float pTimerSeconds, final boolean pAutoReset, final ITimerCallback pTimerCallback) { if(pTimerSeconds <= 0){ throw new IllegalStateException("pTimerSeconds must be > 0!"); } this.mTimerSeconds = pTimerSeconds; this.mAutoReset = pAutoReset; this.mTimerCallback = pTimerCallback; }
下面来看TimerTool 的具体实现。
?所定义的成员变量,构造器实现。
public class TimerTool {
private GameScene mGameScene;
private TimerHandler mTimerHandler;
/**当前经过的总毫秒数**/
private long millisPass = 0;
/**是否计时中**/
private boolean isCountDowning = false;
/**多少毫秒累加一次计时**/
private static long stepMillis = 83;
public TimerTool(GameScene pGameScene) {
this.mGameScene = pGameScene;
initTimerHandler();
}
www.ogengine.com
?初始化TimerHandler
// 初始化TimerHandler,设置为每隔stepMillis * 0.001f秒循环回调onTimePassed方法 private void initTimerHandler() { mTimerHandler = new TimerHandler(stepMillis * 0.001f, true, new ITimerCallback() { @Override public void onTimePassed(TimerHandler pTimerHandler) { // 累加所经过的时间,并在界面上更新显示当前时间 addMillisPass(); } }); } ?相关时间的逻辑实现
// 重置时间
public void resetTimer() {
millisPass = 0;
isCountDowning = false;
}
// 累加所经过的时间,并在界面上更新显示当前时间
private void addMillisPass() {
millisPass += stepMillis;
mGameScene.getTimerText().setText(millisToTimer(millisPass));
}
// 获取当前经过的总毫秒数
public long getMillisPass() {
return millisPass;
}
// 把毫秒转化为一种分、秒、毫秒的文本显示模式字符串
public String millisToTimer(long pMillisPass) {
String timer = "";
long min = pMillisPass / 60000;
long sec = (pMillisPass - min * 60000) / 1000;
String secStr = sec < 10 ? "0" + sec : String.valueOf(sec);
long millisec = pMillisPass % 1000;
if (min > 0) {
timer += min + ":";
}
timer += secStr + "." + millisec + "\"";
return timer;
}
www.ogengine.com
?开始计时和结束计时
// 注册mTimerHandler开始计时
public void start() {
if (!isCountDowning) {
isCountDowning = true;
mGameScene.registerUpdateHandler(mTimerHandler);
}
}
// 反注册mTimerHandler停止计时
public void stop() {
mGameScene.unregisterUpdateHandler(mTimerHandler);
}
在正确点击第一块黑块的时候开始计时,游戏失败或者游戏完成时停止计时,保存好记
录的时间,在胜利界面或失败界面根据实际情况实现所用时间跟最佳记录的显示。
本游戏就介绍到这里,更多细节请看游戏源码。。。。。。