前言
在上一篇文章我们实现了消消乐的消除算法,但是同时出现了一个问题,游戏开始时就存在一些可以消除的组合,这是我们不想要看到的情况。
正文
整体思路
1. 为了不生成可以直接消除的组合,那我们就要在生成类型表的时候动些手脚。我们是逐行生成类型表的,那我们就在生成类型的时候检测当前坐标左边两个相同类型是否相同,下面两个类型是否相同,我们在获取随机类型的时候排除这两种类型就可以了。
左边有两颗草莓了!!!
2. 然后同时要有可一步消除的情况,那我们可以对已生成的类型表进行检测,不合格就再重新生成类型表再次检测。由于情况比较多,我这里只举一个例子,其他的大家看代码即可。比如下面图中这种情况,我们把左下角葡萄的位置设为 (1, 1) ,那我们可以检测 (1, 1) 的类型是否和 (2, 2) 、 (2, 3) 的类型相同,相同的话那就说明他们可以一步消除。
想吃葡萄
代码实现
1. 在 GameUtil 中添加 getInitTypeMap 函数:
/**
* 获取初始类型表
*/
public static getInitTypeMap(): TileType[][] {
let typeMap: TileType[][] = [];
for (let c = 0; c < GameConfig.col; c++) {
let colSet: TileType[] = [];
for (let r = 0; r < GameConfig.row; r++) {
let excludeTypes = [];
// 水平检测左边 2 个相同类型
let rowType: TileType = null;
if (c > 1 && typeMap[c - 1][r] === typeMap[c - 2][r]) rowType = typeMap[c - 1][r];
if (rowType) excludeTypes.push(rowType);
// 垂直检测下面 2 个相同类型
let colType: TileType = null;
if (r > 1 && colSet[r - 1] === colSet[r - 2]) colType = colSet[r - 1];
if (colType) excludeTypes.push(colType);
// 获取可用的随机类型
colSet.push(GameUtil.getRandomType(excludeTypes));
}
typeMap.push(colSet);
}
return typeMap;
}
2. 在 GameUtil 中添加 hasValidCombo 函数;你别看下面代码密密麻麻好像很复杂,其实原理很直接很简单(另外我们还可以返回结果来制作提示功能,可以自行尝试):
/**
* 是否有可一步消除的组合
*/
public static hasValidCombo(map: TileType[][]) {
for (let r = 0; r < GameConfig.row; r++) {
for (let c = 0; c < GameConfig.col; c++) {
if (c + 3 <= GameConfig.col - 1) {
if (map[c][r] === map[c + 1][r] && map[c][r] === map[c + 3][r]) { // 1 1 X 1
return true;
}
if (map[c][r] === map[c + 2][r] && map[c][r] === map[c + 3][r]) { // 1 X 1 1
return true;
}
}
if (map[c][r] === map[c + 1][r]) {
if (r - 1 >= 0 && map[c][r] === map[c + 2][r - 1]) { // 1 1 X
return true; // X X 1
}
if (r + 1 <= GameConfig.row - 1 && map[c][r] === map[c + 2][r + 1]) { // X X 1
return true; // 1 1 X
}
}
if (map[c][r] === map[c + 2][r]) {
if (r - 1 >= 0 && map[c][r] === map[c + 1][r - 1]) { // 1 X 1
return true; // X 1 X
}
if (r + 1 <= GameConfig.row - 1 && map[c][r] === map[c + 1][r + 1]) { // X 1 X
return true; // 1 X 1
}
}
if (r - 1 >= 0 &&
map[c][r] === map[c + 1][r - 1] && map[c + 1][r - 1] === map[c + 2][r - 1]) { // 1 X X
return true; // X 1 1
}
if (r + 1 <= GameConfig.row - 1 &&
map[c][r] === map[c + 1][r + 1] && map[c + 1][r + 1] === map[c + 2][r + 1]) { // X 1 1
return true; // 1 X X
}
if (r + 3 <= GameConfig.row - 1) {
if (map[c][r] === map[c][r + 1] && map[c][r] === map[c][r + 3]) {
return true;
}
if (map[c][r] === map[c][r + 2] && map[c][r] === map[c][r + 3]) {
return true;
}
}
if (map[c][r] === map[c][r + 1]) {
if (c - 1 >= 0 && map[c][r] === map[c - 1][r + 2]) {
return true;
}
if (c + 1 <= GameConfig.col - 1 && map[c][r] === map[c + 1][r + 2]) {
return true;
}
}
if (map[c][r] === map[c][r + 2]) {
if (c - 1 >= 0 && map[c][r] === map[c - 1][r + 1]) {
return true;
}
if (c + 1 <= GameConfig.col - 1 && map[c][r] === map[c + 1][r + 1]) {
return true;
}
}
if (c - 1 >= 0 &&
map[c][r] === map[c - 1][r + 1] && map[c - 1][r + 1] === map[c - 1][r + 2]) {
return true;
}
if (c + 1 <= GameConfig.col - 1 &&
map[c][r] === map[c + 1][r + 1] && map[c + 1][r + 1] === map[c + 1][r + 2]) {
return true;
}
}
}
return false;
}
3. 我们再对 TileManager 中的 generateInitType 函数进行改造,让他可以生成让我们满意的类型表:
/**
* 生成初始的类型表
*/
private generateInitTypeMap() {
this.typeMap = GameUtil.getInitTypeMap();
if (!GameUtil.hasValidCombo(this.typeMap)) {
this.typeMap = GameUtil.getInitTypeMap();
}
}
4. 打开游戏预览,可以看到我们已经成功了,没有可以直接消除的类型,而且存在可以一步消除的情况!
消它!!!
★ 所以到这里这篇文章就结束啦,下篇文章我们就来实现消除之后方块的下落和新生成!