四、游戏交互实现
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);
}
}
}
}
复制代码
上面的代码已经有所注释,这里再简单解析一下。
①理论上可以点击的一行为倒数第二行,如果点击到该行的黑块,则所有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;
}
}
}
}
复制代码
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,如果到了则开始显示绿色胜利界面。
③被点击的黑块变灰色,加点小小的动作效果。
④计算出需要向下移动的距离,遍历所有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的位置设在最顶端,设置已经是第几行。
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);
}
复制代码
五、计时控制类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();
}
复制代码
②初始化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;
}
复制代码
④开始计时和结束计时
// 注册mTimerHandler开始计时
public void start() {
if (!isCountDowning) {
isCountDowning = true;
mGameScene.registerUpdateHandler(mTimerHandler);
}
}
// 反注册mTimerHandler停止计时
public void stop() {
mGameScene.unregisterUpdateHandler(mTimerHandler);
}
复制代码
在正确点击第一块黑块的时候开始计时,游戏失败或者游戏完成时停止计时,保存好记录的时间,在胜利界面或失败界面根据实际情况实现所用时间跟最佳记录的显示。
本游戏就介绍到这里,更多细节请看游戏源码。。。。。。
源码下载地址
http://www.eoeandroid.com/forum-863-1.html
www.ogengine.com