前言
上回我们正式完成了初始方块的正确生成以及组合的消除,同时我们这个系列也接近尾声。
这篇文章和大家一起实现组合消除之后,方块的下落以及新方块的生成。
项目预览
消消消
正文
整体思路
1. 方块的下落:我们从左下角第一个方块开始,向上遍历检查,发现有空位后,向上查找直到有可用的方块,将可用方块交换(做下落动画)到刚刚的空位,每一列都重复以上步骤,即可让下面的空位被填满。
2. 生成新方块:下落之后,再遍历每一个位置,发现有空的就随机生成一个新的方块就好啦。
3. 消除并检测可玩性:生成新的方块之后又可以继续查找可消除的组合了,如果没有可以消除的组合,还要检查有没有可以一步消除的组合,如果没有,那就要重新生成全部方块了。
代码实现
1. 在 TileManager 组件中实现方块下落函数 fallDown :
/**
* 方块下落
*/
private async falldown() {
let promises: Promise<void>[] = [];
for (let c = 0; c < GameConfig.col; c++) {
for (let r = 0; r < GameConfig.row; r++) {
// 找到空位
if (!this.getType(c, r)) {
// 往上找方块
for (let nr = r + 1; nr < GameConfig.row; nr++) {
// 找到可以用的方块
if (this.getType(c, nr)) {
// 转移数据
this.setType(c, r, this.getType(c, nr));
this.setTile(c, r, this.getTile(c, nr));
this.getTile(c, r).setCoord(c, r);
// 置空
this.setTile(c, nr, null);
this.setType(c, nr, null);
// 下落
let fallPos = MapManager.getPos(c, r);
let fallTime = (nr - r) * 0.1;
promises.push(new Promise(res => {
cc.tween(this.getTile(c, r).node)
.to(fallTime, { position: cc.v2(fallPos.x, fallPos.y - 10) })
.to(0.05, { position: fallPos })
.call(() => res())
.start();
}));
break;
}
}
}
}
}
// 等待所有方块完成下落动画
await Promise.all(promises);
}
2. 在 TileManager 组件中实现生成新方块的函数 fillEmpty :
/**
* 填充空位
*/
private async fillEmpty() {
for (let c = 0; c < GameConfig.col; c++) {
for (let r = 0; r < GameConfig.row; r++) {
// 找到空位
if (!this.getType(c, r)) {
let type = GameUtil.getRandomType();
let tile = this.getNewTile(c, r, type);
this.setTile(c, r, tile)
this.setType(c, r, type);
}
}
}
await new Promise(res => setTimeout(res, 100));
}
3. 在 TileManager 组件实现函数 keepCheckingUntilNoMoreCombiantion ,用于持续检查组合然后消除下落并填充,没有可一步操作的组合时重新生成全部方块:
/**
* 检查可消除组合直到没有可以消除的组合
*/
private async keepCheckingUntilNoMoreCombiantion() {
this.combinations = GameUtil.getCombinations(this.typeMap); // 获取可消除的组合
// 有可消除的组合吗
while (this.combinations.length > 0) {
this.eliminateCombinations(); // 消除
await new Promise(res => setTimeout(res, 250));
await this.falldown(); // 下落
await new Promise(res => setTimeout(res, 250));
await this.fillEmpty(); // 填充
await new Promise(res => setTimeout(res, 250));
this.combinations = GameUtil.getCombinations(this.typeMap); // 获取可消除的组合
await new Promise(res => setTimeout(res, 250));
}
// 存在一步可消除情况吗
if (!GameUtil.hasValidCombo(this.typeMap)) {
this.removeAllTiles(); // 移除所有方块
this.generateInitTypeMap(); // 生成可用 typeMap
this.generateTiles(); // 生成方块
}
}
4. 改造 tryExchange 函数,实现交换消除后下落并填充,然后持续检测:
/**
* 尝试交换方块
* @param coord1 1
* @param coord2 2
*/
private async tryExchange(coord1: Coordinate, coord2: Coordinate) {
// 交换方块
await this.exchangeTiles(coord1, coord2);
// 获取可消除组合
this.combinations = GameUtil.getCombinations(this.typeMap);
if (this.combinations.length > 0) {
await new Promise(res => setTimeout(res, 250));
this.eliminateCombinations(); // 消除
await new Promise(res => setTimeout(res, 250));
await this.falldown(); // 下落
await new Promise(res => setTimeout(res, 250));
await this.fillEmpty(); // 填充
await new Promise(res => setTimeout(res, 250));
this.keepCheckingUntilNoMoreCombiantion(); // 持续检测
} else {
// 不能消除,换回来吧
await this.exchangeTiles(coord1, coord2);
}
}
★ 到这里,我们完成了简版消消乐的制作啦,但是我们只有核心基础玩法,如果想要让游戏更好玩,可以在这基础上实现特殊方块、道具系统之类的功能,这些就需要你自己去探索啦~