设计一个 game class。负责了游戏的核心控制逻辑,包括游戏状态管理、方块和道具的生成与效果处理,以及游戏的重新开始和复活流程。通过这些方法,脚本实现了游戏的基本玩法和用户交互。
主要游戏控制方法
-
gameStart()
:开始游戏,恢复所有方块状态,初始化分数管理器,设置地图。 -
mapSet(num)
:初始化地图,生成随机的方块布局。 -
checkNeedFall()
:检查是否需要下落的防抖方法。 -
onFall()
:方块下落的逻辑。 -
gameOver()
:游戏结束的逻辑,添加复活页面。 -
askRevive()
:处理复活请求,显示复活页面。 -
onReviveButton()
:复活按钮的点击事件。 -
showReviveSuccess()
:显示复活成功的页面。 -
onReviveCertainBtn()
:处理确定复活的按钮点击事件。 -
restart()
:重新开始游戏。
道具相关方法
-
onUserTouched(iid, jid, itemType, color, warning, pos)
:存储用户点击的方块信息,用于生成道具。 -
generatePropItem(type)
:根据类型生成道具。 -
checkGenerateProp(chain)
:检查并生成连锁道具效果。 -
onItem(type, color, pos)
:处理道具效果,如分数翻倍、炸弹消除等。
预制体实例化方法
-
generatePool()
:生成对象池,用于方块的实例化和回收。 -
instantiateBlock(self, data, parent, itemType, pos)
:实例化单个方块,初始化方块组件。 -
recoveryAllBlocks()
:回收所有方块节点,用于游戏重新开始或结束。
onUserTouched方法实现了处理用户触摸方块或方块被其他方块触发时的逻辑,包括状态检查、动画播放和连锁反应处理。
// onUserTouched 方法用于处理用户触摸方块时的逻辑
onUserTouched(color, isChain, isBomb, time) {
// 如果提供了延迟时间参数,则使用 setTimeout 延迟执行 onTouched
if (time) {
setTimeout(() => {
this.onUserTouched(color, false, isBomb); // 延迟调用时默认不连锁
}, time);
return;
}
// 确保 isChain 和 isBomb 参数被正确设置,如果是 null 或 undefined,则给予默认值
isChain = JSON.stringify(isChain) == 'null' ? true : isChain;
isBomb = isBomb ? true : false;
// 保存当前对象的引用
let self = this;
// 如果方块状态为可触发,并且是炸弹触发,播放死亡动画然后处理消除逻辑
if (this._status == 1 && isBomb == true) {
this._status = 2; // 更新方块状态为已消失
this.playDieAction().then(() => {
this.onBlockPop(color, isChain, isBomb); // 处理方块消除后的连锁反应
});
return;
}
// 如果 color 对象包含 type 属性,表示是用户主动触发的触摸事件
if (color && color.type) {
// 如果是用户主动触发,并且方块状态为可触发,游戏状态为可玩,颜色匹配
if (this._status == 1 && this._game._status == 1 && this.color == color) {
// 调用游戏的 onUserTouched 方法处理用户触摸事件
this._game.onUserTouched(this.iid, this.jid, this._itemType, this.color, this.warningType, {
x: this.node.x,
y: this.node.y
});
// 异步处理分数变化,如果成功则播放死亡动画并处理消除逻辑
this._game._score.onStep(-1).then((res) => {
if (res) {
this.playDieAction().then(() => {
this.onBlockPop(color, null, null); // 处理消除后的连锁反应
});
}
});
}
} else {
// 如果 color 对象不包含 type 属性,表示是其他方块触发的事件
// 检查方块状态和游戏状态,如果颜色匹配则播放死亡动画并处理消除逻辑
if (this._status == 1 && this._game._status == 5 && this.color == color) {
this.playDieAction().then(() => {
this.onBlockPop(color, null, null); // 处理消除后的连锁反应
});
}
}
},
详细步骤:
-
延迟处理:如果提供了
time
参数,使用setTimeout
延迟执行onUserTouched
方法。 -
参数校验:确保
isChain
和isBomb
参数被正确设置,如果不是有效的布尔值,则给予默认值。 -
保存当前对象引用:使用
let self = this;
保存当前对象的引用,以便在回调中使用。 -
炸弹触发处理:如果方块状态为可触发,并且
isBomb
为true
,更新方块状态并播放死亡动画,然后处理消除逻辑。 -
用户主动触发处理:
-
如果
color
对象包含type
属性,表示是用户主动触发的触摸事件。 -
检查方块状态、游戏状态和颜色是否匹配,如果匹配,调用游戏逻辑处理用户触摸事件。
-
-
分数变化处理:异步处理分数变化,如果成功,播放死亡动画并处理消除逻辑。
-
其他方块触发处理:
-
如果
color
对象不包含type
属性,表示是其他方块触发的事件。 -
检查方块状态和游戏状态,如果颜色匹配,播放死亡动画并处理消除逻辑。
-
-
处理连锁反应:在
onBlockPop
方法中处理消除后的连锁反应,这可能包括更新分数、检查消除情况等。
generatePropItem
方法实现了异步生成道具方块的逻辑,确保了游戏在生成道具时的流畅性和动画效果。
/**
* generatePropItem 方法用于生成道具方块
* @param {number} type - 道具类型
* @return {Promise} - 返回一个Promise对象,用于异步处理道具生成
*/
generatePropItem(type) {
return new Promise((resolve, reject) => {
// 获取当前对象的引用
let self = this;
// 检查方块是否已经有道具类型,如果没有则设置默认值
type = type ? type : 0;
// 获取方块的实例,如果对象池中有可用的实例则使用,否则创建新的实例
let block = null;
if (self.blockPool && self.blockPool.size() > 0) {
block = self.blockPool.get();
} else {
block = cc.instantiate(this.blockPrefab);
}
// 设置方块的父节点为blocksContainer,即方块容器
block.parent = this.blocksContainer;
// 重置方块的位置和缩放
block.scale = 1;
block.x = 0;
block.y = 0;
// 初始化方块组件,传入方块数据和道具类型
block.getComponent('cell').init(self, {
x: this.target.j, // 列位置
y: this.target.i, // 行位置
color: this.target.color, // 方块颜色
width: this.blockWidth, // 方块宽度
startTime: null // 动画开始时间
}, type);
// 设置方块的动画开始时间,可能基于方块在地图上的位置
block.getComponent('cell').startTime = (this.target.i + this.target.j + 1) * self._controller.config.json.startAnimationTime / this.rowNum;
// 将新生成的道具方块放置到目标位置
block.setPosition(this.target.x, this.target.y);
// 延迟一定时间后解析Promise,表示道具生成完成
setTimeout(() => {
resolve();
}, 300);
});
},
详细步骤:
-
定义方法和参数:
generatePropItem
方法接受一个参数type
,表示生成的道具类型。 -
返回 Promise:返回一个新的
Promise
对象,用于异步处理道具生成。 -
获取当前对象引用:使用
let self = this;
获取当前对象的引用,以便在回调函数中使用。 -
检查道具类型:如果
type
参数未提供或无效,则设置默认值为0。 -
获取方块实例:检查对象池
blockPool
是否存在且有可用的方块实例,如果有则从对象池中获取,否则实例化一个新的方块。 -
设置方块父节点:将方块的父节点设置为
blocksContainer
,即方块容器节点。 -
重置方块位置和缩放:重置方块的位置和缩放,确保方块在生成时从正确的位置和大小开始。
-
初始化方块组件:使用
init
方法初始化方块组件,传入方块数据和道具类型。 -
设置动画开始时间:根据方块在地图上的位置,设置方块的动画开始时间。
-
设置方块位置:将方块的
setPosition
设置为this.target.x
和this.target.y
,即目标位置。 -
延迟解析 Promise:使用
setTimeout
在300毫秒后解析Promise
,表示道具生成完成。
onItem 函数是处理用户与道具交互的逻辑部分当玩家使用不同类型的道具时,游戏能够正确地响应并执行相应的效果。
// 当用户使用道具时调用此函数
// type 表示道具的类型
// color 表示道具的颜色
// pos 表示道具使用的位置
onItem(type, color, pos) {
switch (type) {
case 1: // 分数翻倍道具
// 初始化分数翻倍提示框
this._score.tipBox.init(this._score, 1);
// 为指定颜色的方块添加分数翻倍效果
this._score.addMult(color, pos);
// 播放双倍分数的音效
this._controller.musicMgr.onDouble();
// 对所有处于活动状态的方块执行表面效果动作
this.executeSurfaceActionForActiveBlocks(pos);
break;
case 2: // 炸弹道具,消除同种颜色的方块
// 初始化炸弹提示框
this._score.tipBox.init(this._score, 2);
// 游戏界面震动效果
this.node.runAction(AC.shackAction(0.1, 10));
// 如果社交模块处于激活状态,则触发手机震动
if (this._controller.social.node.active) {
this._controller.social.onShakePhone();
}
// 设置道具连锁标志为真
this.isPropChain = true;
// 播放炸弹爆炸的音效
this._controller.musicMgr.onBoom();
// 对所有同色且非锁定状态的方块执行触摸效果,并消除
this.executeTouchEffectAndEliminateSameColorBlocks(color);
break;
case 3: // 加步数道具
// 初始化步数增加提示框
this._score.tipBox.init(this._score, 4);
// 播放双倍分数的音效
this._controller.musicMgr.onDouble();
// 对所有活动状态的方块执行表面效果动作
this.executeSurfaceActionForActiveBlocks(pos);
// 增加步数
this._score.onStep(3).then();
break;
case 4: // 消除全部单身的方块
// 初始化消除单身方块提示框
this._score.tipBox.init(this._score, 5);
// 设置道具连锁标志为真
this.isPropChain = true;
// 播放魔法效果的音效
this._controller.musicMgr.onMagic();
// 对所有单身且非锁定状态的方块执行触摸效果,并消除
this.executeTouchEffectAndEliminateSingleBlocks(pos);
break;
}
},
// 对所有处于活动状态的方块执行表面效果动作
executeSurfaceActionForActiveBlocks(pos) {
for (let i = 0; i < this.rowNum; i++) {
for (let j = 0; j < this.rowNum; j++) {
if (this.map[i][j] && this.map[i][j].getComponent('cell')._status == 1) {
let distance = Math.sqrt(Math.pow(pos.x - this.map[i][j].x, 2) + Math.pow(pos.y - this.map[i][j].y, 2));
if (distance != 0) {
this.map[i][j].getComponent('cell').surfaceAction(distance);
}
}
}
}
},
// 对所有同色且非锁定状态的方块执行触摸效果,并消除
executeTouchEffectAndEliminateSameColorBlocks(color) {
for (let i = 0; i < this.rowNum; i++) {
for (let j = 0; j < this.rowNum; j++) {
if (this.map[i][j] && this.map[i][j].getComponent('cell').color == color && this.map[i][j].getComponent('cell')._status != 2) {
this.map[i][j].getComponent('cell').onTouched(color, false, true);
} else {
this.map[i][j].runAction(AC.rockAction(0.2, 10));
}
}
}
},
// 对所有单身且非锁定状态的方块执行触摸效果,并消除
executeTouchEffectAndEliminateSingleBlocks(pos) {
for (let i = 0; i < this.rowNum; i++) {
for (let j = 0; j < this.rowNum; j++) {
if (this.map[i][j] && this.map[i][j].getComponent('cell').isSingle && this.map[i][j].getComponent('cell')._status != 2) {
let distance = Math.sqrt(Math.pow(pos.x - this.map[i][j].x, 2) + Math.pow(pos.y - this.map[i][j].y, 2));
this.map[i][j].getComponent('cell').onTouched(color, false, true, distance);
}
}
}
},
详细步骤:
-
case 1:当用户使用分数翻倍道具时,初始化提示框,为指定颜色的方块添加分数翻倍效果,并播放音效。然后对所有活动状态的方块执行表面效果动作。
-
case 2:当用户使用炸弹道具时,初始化提示框,执行界面震动效果,如果社交模块激活则触发手机震动。设置道具连锁标志,播放音效,并消除所有同色且非锁定状态的方块。
-
case 3:当用户使用加步数道具时,初始化提示框,播放音效,对所有活动状态的方块执行表面效果动作,并增加步数。
-
case 4:当用户使用消除单身方块道具时,初始化提示框,设置道具连锁标志,播放音效,并消除所有单身且非锁定状态的方块。
-
辅助函数:
executeSurfaceActionForActiveBlocks
对所有活动状态的方块执行表面效果动作;executeTouchEffectAndEliminateSameColorBlocks
对所有同色且非锁定状态的方块执行触摸效果并消除;executeTouchEffectAndEliminateSingleBlocks
对所有单身且非锁定状态的方块执行触摸效果并消除。
更多详细分析 看以下链接