苗条的蛇游戏没有正确删除蛇

Svelte snake game not deleting snakes properly

我一直在尝试在 svelte 中制作贪吃蛇游戏,但我不明白为什么它不能正确记住之前贪吃蛇的位置并删除它们,而是删除当前的。

<script lang="ts">
    import { onMount } from 'svelte';

    // variable declarations

    let gridSize = 20;
    type Cell = 'empty' | 'snake' | 'food';
    let grid : Cell[][] = [...Array(gridSize)].map(() => [...Array(gridSize)].map(() => "empty"));
    let snakePos: Array<[number, number]> = [[12, 13]];
    grid[5][10] = 'food';
    let snakes = [];

    let length = 1;

    let xy = 0;
    let dir = 1;

    // functions and onmount

    function handleKeydown(e : any) {
        switch(e.keyCode){
            case 87:
                xy = 0;
                dir = -1;
                break;
            case 65:
                xy = 1;
                dir = -1;
                break;
            case 83:
                xy = 0;
                dir = 1;
                break;
            case 68:
                xy = 1;
                dir = 1;
                break;
            default:
                break;
        }
    }
    onMount(() => {
        setInterval(() => {
            snakes.unshift(snakePos[0]);
            snakePos[0][xy] += dir;
            
            grid[snakePos[0][0]][snakePos[0][1]] = 'snake';
            
            if(snakes.length > length+1)
            {
                let delSnake = snakes.pop();
                
                grid[delSnake[0]][delSnake[1]] = 'empty';
            }
        }, 500);
    });
</script>

<svelte:window on:keydown={handleKeydown}/>

<main>
    {#each grid as row, i}
        <div class="row">
            {#each row as cell, k}
            <div class={`square ${cell}`} />
            {/each}
        </div>
    {/each}
</main>

我试过将 snakePos 添加到 snakes 数组,但事实证明即使在更改 snakePos 之后:

snakePos[0][xy] += dir;

当我检查时,snakePos 与之前相同(用 if(===) 检查)。

如果我删除将最后一条蛇设置为空的线,蛇会正常移动但长度无限。另外,我还没有添加进食或丢失的功能,等我想通了如何限制蛇的长度(这个bug)再添加。

看来您的位置数组有些混乱。

这是数组的数组:

    let snakePos: Array<[number, number]> = [[12, 13]];

我想你想要一个 X、Y 位置的“元组”,否则就是长度为 2 的数组:

    let snakePos: [number, number] = [12, 13];

进行此更改后,在您的间隔函数中:

//          snakePos[0][xy] += dir;
// =>
            snakePos[xy] += dir;

//          grid[snakePos[0][0]][snakePos[0][1]] = 'snake';
// =>
            grid[snakePos[0]][snakePos[1]] = 'snake';

这会让你的蛇动起来。

我不确定您在接下来的几行中要做什么。 ¯\_(ツ)_/¯

            if (snakes.length > length + 1) {
                let delSnake = snakes.pop();

                grid[delSnake[0]][delSnake[1]] = 'empty';
            }

赋值看起来很正常,但您可能应该确保 delSnake 不是未定义的。

最后一句话,你应该使用onMount的清理功能来清除你的间隔:

onMount(() => {
  const interval = setInterval(() => {
    ...
  }, 500)

  return () => {
    clearInterval(interval)
  }
})

除此之外,组件被破坏后,间隔会继续发射,一般会造成灾难性的后果。

如果您认为您现在不关心销毁组件(但您应该关心),请为 HMR 这样做。 HMR 在更改时销毁并重新创建组件...如果每次都泄漏一个永远的 运行 间隔,它将变得一团糟!


正在回复您的评论...

What I tried to do with that if statement is, keep previous snake positions and the length of the snake, and remove the snake from the board if the array length(snakes) is more than how long the snake is supposed to be.

哦,我明白了。然后我认为你对整条蛇的位置排列是正确的。混淆的根源似乎是您不需要的 snakePos 变量,因为所有这些信息都在整条蛇中。

因此,我建议:

而不是我的第一个建议
// get rid of this
// let snakePos: Array<[number, number]> = [[12, 13]];

// keep only this one -- whole snake
let snakes: Array<[number, number]> = [[12, 13]];

剩下的可以用更新函数中的局部变量更清楚地完成:

const interval = setInterval(() => {
    const lastPos = snakes[0];

    // clone lastPos somehow, or the line after will mutate lastPos,
    // that is snakes[0]
    const nextPos = [...lastPos] as [number, number];
    nextPos[xy] += dir;

    snakes.unshift(nextPos);

    grid[nextPos[0]][nextPos[1]] = 'snake';

    if (snakes.length > length + 1) {
        let delSnake = snakes.pop();

        grid[delSnake[0]][delSnake[1]] = 'empty';
    }
}, 500);

请注意,Svelte 不会 看到对 snakes 数组(即 snakes.unshift(nextPos)snakes.pop())的更改,因为它们不是作业。

上面的代码有效并且视图得到更新,因为 Svelte 确实跟踪了对 grid 变量(例如 grid[nextPos[0]][nextPos[1]] = 'snake')的实际分配,这就是您当前标记中使用的内容。

如果您需要视图对 snakes 变量的这种变化做出反应,您还必须向 Svelte 发出信号,表明它已通过实际的自我分配发生变化,如下所示:

snakes.pop()
snakes = snakes