我正在编写一个简单的游戏,其中玩家拥有不同颜色的瓷砖区域。当拾取类似块的块时,将其删除。剩余的图块向下滑动到删除的图块留下的空白处。
字段本身是2d数组,而tile是tileSpr [x] [y]
删除图块时,其值为null:
tileSpr[x][y] = null;
现在我有一个非常简单的函数,该函数返回需要向下移动的瓷砖阵列以及它们下面有多少个“孔”:
whichTilesNeedMove() {
const tilesToMove = [];
for (let i = 1; i < CONFIG.maxRows; i++) {
for (let j = 0; j < CONFIG.maxCols; j++) {
if (this.tilesSpr[i][j] !== null) {
let emptySpace = 0;
for (let m = i - 1; m >= 0; m--) {
if (this.tilesSpr[m][j] === null) {
emptySpace++ ;
}
}
if (emptySpace > 0) {
tilesToMove.push({ tile: this.tilesSpr[i][j], emptySpace });
this.tilesSpr[i - emptySpace][j] = this.tilesSpr[i][j];
this.tilesSpr[i - emptySpace][j].rowIndex = i - emptySpace;
this.tilesSpr[i][j] = null;
}
}
}
}
return tilesToMove;
}
感觉该函数中使用的算法不是最佳算法,因为它必须遍历整个2d数组。不幸的是,因为我是编程方面的新手,所以没有比我更好的事情了。
您能建议一种更理想的方法吗?
答案 0 :(得分:4)
我将2D数组组织为列(而不是行)的数组,其中列数组的第一个条目是底部的正方形。然后,当磁贴必须消失时,不要分配null
,而是在相应的列上执行拼接。这将自动向下移动上部砖块(在模型中)。如果确实需要,则可以将null推送到该列以恢复其中的条目总数,但实际上没有理由。
这是一个小演示。它使用CSS transition
来为水滴设置动画,但这仅仅是装饰。它不包含查找相同颜色的图块的逻辑,而只是删除了您单击的图块,并相应地更新了游戏区域。
我要说的核心是简化push
和splice
调用以保持模型(game.columns
)更新的目的。
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const transition = (elem, marginTop) => new Promise(resolve => {
elem.style.marginTop = marginTop + "px";
elem.ontransitionend = resolve
});
const game = {
grid: document.querySelector("#grid"),
columns: Array.from({length: 10}, () => []),
async drop(x, color) {
if (this.columns[x].length >= 10) return;
const y = 10-this.columns[x].length;
const tile = document.createElement("span");
this.grid.append(tile);
Object.assign(tile.style, { left: x * 20 + "px", backgroundColor: color });
this.columns[x].push(tile);
await delay(1);
return transition(tile, y*20);
},
async remove(x, y) {
if (y >= this.columns[x].length) return;
this.columns[x].splice(y, 1)[0].remove();
let p = Promise.resolve();
for (let tile of this.columns[x].slice(y)) {
p = transition(tile, (parseInt(tile.style.marginTop)+20));
}
return p;
}
};
game.grid.onclick = function (e) {
const x = Math.floor((e.clientX - this.offsetLeft) / 20);
const y = 9 - Math.floor((e.clientY - this.offsetTop) / 20);
game.remove(x, y)
}
async function initGame() {
const randInt = (len) => Math.floor(Math.random() * len);
const colors = ["red", "blue", "green", "yellow", "grey", "cyan"];
for (let i = 0; i < 70; i++) {
game.drop(randInt(10), colors[randInt(colors.length)]);
await delay(5);
}
}
initGame();
body { margin: 0 }
#grid>span {
position: absolute;
border: 1px solid;
display: inline-block;
width: 19px;
height: 19px;
top: -20px;
transition: margin-top 100ms linear;
}
#grid {
background-size: 20px 20px;
background-image: linear-gradient(to right, lightgrey 1px, transparent 1px),
linear-gradient(to bottom, lightgrey 1px, transparent 1px);
display: inline-block;
width: 201px;
height: 201px;
position: relative;
}
<div id="grid"></div>
注意:drop
和remove
方法返回的承诺会在相应的动画完成时解析,但是我在这段代码中并未真正使用该返回值。
答案 1 :(得分:1)
您可以从greates索引中进行迭代,查看一列中是否有更多的null(此处仅用于格式化为零)值,然后将最大的上限值替换为零,将较低的下标替换为该值。
var array = [
[1, 1, 1, 1, 1],
[2, 2, 0, 0, 2],
[3, 0, 0, 0, 3],
[4, 4, 4, 0, 0]
],
i = array.length,
j,
l;
array.forEach(a => console.log(...a));
console.log('to');
while (--i) {
for (j = 0; j < array[0].length; j++) {
l = i;
while (l > 0 && array[l][j] === 0) l--;
if (l !== i) {
array[i][j] = array[l][j];
array[l][j] = 0;
}
}
}
array.forEach(a => console.log(...a));
.as-console-wrapper { max-height: 100% !important; top: 0; }