Haz un juego de serpientes desde cero, solo conoce vue

Participo en la competencia por equipos del concurso creativo de juegos de la comunidad de los Nuggets. Para obtener más información, consulte: Concurso de contribución creativa del juego

Hoy, compartiré con ustedes un pequeño juego de serpiente codiciosa. Se html + css + jsimplementa en vue + uniCloudforma pura. Dado que la pila de tecnología es muy superficial, espero que los grandes no la pulvericen. Sin más preámbulos, empecemos de frente.

El diagrama de efectos es el siguiente

representaciones.jpg

Inicializar el proyecto

Este proyecto está uni-appbasado en el desarrollo, por lo que es mejor usarlo HBuildercomo su editor preferido.

Nuevo proyecto

Abra HBuilder, cree un nuevo proyecto, nombre el proyecto snake_eat_worm(serpiente come insectos), le di un nombre alto: Grupo de lucha contra serpientes Gusano. La máxima puntuación para el nombre, date un muslo de pollo. Seleccione la plantilla predeterminada, marque Habilitar uniCloud(Alibaba Cloud se usa de forma predeterminada) y haga clic en Crear.

Nuevo proyecto.jpgEsto HBuildergenerará automáticamente el directorio del proyecto:

proyecto directorio.png

Ejecutamos el proyecto directamente:

ejecutar proyecto.png

Ver la siguiente interfaz indica que el proyecto se está ejecutando correctamente

ejecutar correctamente.jpg

Preparación de recursos del juego

poner recursos en un static/imagesdirectorio

recursos del juego.jpg

mapa

Abra la pages/indexpágina, modifiquemos y agreguemos nuestras propias cosas:

<template>
    <view class="content">
        贪吃蛇~
    </view>
</template>
复制代码

Modificar la página de inicio.jpg

ok, la página ya está bajo nuestro control, vamos a iniciarla oficialmente.

dibujar el suelo

¿Configuramos el suelo para que sea una cuadrícula de 10 * 10 y usamos un div para dibujar una cuadrícula de 10 * 10?

Inicializar la grilla.png

¡Joven héroe! Espera, ¡solo un div es suficiente! Si no me crees, mira:

<template>
    <view class="content">
        <view class="game-field">
            <view class="block"></view>
            <view class="block"></view>
            <view class="block"></view>
            <view class="block"></view>
            <!-- 此处省略 100 - 4 个 -->
        </view>
    </view>
</template>
<style>
    .content {
        height: 100vh;
        width: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: space-between;
        padding: 0;
    }
    .game-field {
        display: flex;
        flex-wrap: wrap;
    }
    .block {
        width: 10vw;
        height: 10vw;
        display: flex;
        justify-content: center;
        align-items: center;
        background-color: rgb(232, 235, 178);
        background-repeat: no-repeat;
        background-position: center;
        background-size: cover;
        box-sizing: border-box;
        outline: 2upx solid;
    }
</style>
复制代码

Creo que los amigos que solo conocen divs deberían estar más familiarizados con la representación de los divs repetidos anteriores que yo:

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" v-for="x in 100" :key="x"></view>
        </view>
    </view>
</template>
复制代码

xx.jpg

Bien hecho, ahora hay un trozo de tierra amarilla en nuestra interfaz de juego, veamos cómo poner algunos errores en el suelo.

errores de dibujo

Antes de dibujar, pensemos en cómo usar div para dibujar diferentes patrones (serpientes e insectos o tierra) en diferentes cuadrículas. También podríamos pensar en estas 100 cuadrículas como una matriz. Cada cuadrícula corresponde a los subíndices 0-99 de la matriz. Inicializamos la matriz a 100 ceros y usamos el número 0 para representar la tierra. Usa el número 1 para el insecto y el número 2 para la serpiente. Modificamos ligeramente el código anterior:

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" v-for="(x,i) in blocks" :key="i">{{x}}</view>
        </view>
    </view>
</template>
<script>
    export default {
        data() {
            return {
                blocks: new Array(100).fill(0), // 格子
                worms: [6, 33, 87] // 初始化三只虫子,数字代表它所处的格子的下标
            }
        },
        onLoad() {
            this.worms.forEach(x=> {
                this.blocks[x] = 1;
            })
            this.$forceUpdate();
        }
    }
</script>
复制代码

matriz.jpg

可以看到在黄色土地里有3个格子的数字是1,其他是0。这样就好办了,我们只需把数字是1的格子的背景图变成一只小虫子就好了:

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" 
                :style="{'background-image': bg(x)}" 
                v-for="(x, i) in blocks" :key="i">
                {{x}}
            </view>
        </view>
    </view>
</template>
<script>
    import worm from "../../static/images/worm.png";
    export default {
        data() {
            return {
                blocks: new Array(100).fill(0), // 格子
                worms: [6, 33, 87] // 初始化三只虫子,数字代表它所处的格子的下标
            }
        },
        onLoad() {
            // 将虫子的格子赋值为1
            this.worms.forEach(x=> {
                this.blocks[x] = 1;
            })
        },
        methods: {
            bg(type) {
                let bg = "";
                switch (type) {
                    case 0: // 地板
                        bg = "unset";
                        break;
                    case 1: // 虫子
                        bg = `url(${worm})`;
                        break;
                    case 2: // 蛇
                        // TODO
                }
                return bg;
            }
        },
    }
</script>
复制代码

1649768365(1).jpg

至此,虫子的绘制就完成了,接下来我们开始绘制蛇,蛇的绘制可是有点难度哦,各位看官做好准备了吗,只会div的朋友,恐怕你真的要告辞了,哈哈啊哈

绘制蛇

从游戏资源中,我们可以看到我们的蛇是有头有尾的,咱们先不管,咱们先把蛇的身子怼上去,去头去尾拿去炖汤~

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" 
                :style="{'background-image': bg(x)}" 
                v-for="(x, i) in blocks" :key="i">
                {{x}}
            </view>
        </view>
    </view>
</template>
<script>
    import worm from "../../static/images/worm.png";
    import snakeBody from "../../static/images/snake_body.png";
    export default {
        data() {
            return {
                blocks: new Array(100).fill(0), // 格子
                worms: [6, 33, 87], // 初始化三只虫子,数字代表它所处的格子的下标
                snakes: [0, 1, 2, 3] // 我们的蛇默认在左上角的位置,占据4个格子
            }
        },
        onLoad() {
            // 将虫子的格子赋值为1
            this.worms.forEach(x=> {
                this.blocks[x] = 1;
            })
            // 同理将蛇的格子赋值为2
            this.snakes.forEach((x) => {
                this.blocks[x] = 2;
            });
            this.$forceUpdate();
        },
        methods: {
            bg(type) {
                let bg = "";
                switch (type) {
                    case 0: // 地板
                        bg = "unset";
                        break;
                    case 1: // 虫子
                        bg = `url(${worm})`;
                        break;
                    case 2: // 蛇
                        bg = `url(${snakeBody})`;
                        break;
                }
                return bg;
            }
        },
    }
</script>
复制代码

1649768552(1).jpg

哈哈,现在我们可以看到一个无头无尾蛇,拿去炖汤。其实啊,完整的蛇更美味呢,接下来我们把蛇头蛇尾补上。这里我们需要记录当前格子的下标,因为我们定义蛇的数组是记录了蛇所处格子的下标的:

绘制蛇头蛇尾

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" 
                :style="{'background-image': bg(x, i)}" 
                v-for="(x, i) in blocks" :key="i">
                {{x}}
            </view>
        </view>
    </view>
</template>
<script>
    import worm from "../../static/images/worm.png";
    import snakeBody from "../../static/images/snake_body.png";
    import snakeHead from "../../static/images/snake_head.png";
    import snakeTail from "../../static/images/snake_tail.png";
    export default {
        data() {
            return {
                blocks: new Array(100).fill(0), // 格子
                worms: [6, 33, 87], // 初始化三只虫子,数字代表它所处的格子的下标
                snakes: [0, 1, 2, 3] // 我们的蛇默认在左上角的位置,占据4个格子
            }
        },
        onLoad() {
            // 将虫子的格子赋值为1
            this.worms.forEach(x=> {
                this.blocks[x] = 1;
            })
            // 同理将蛇的格子赋值为2
            this.snakes.forEach((x) => {
                this.blocks[x] = 2;
            });
            this.$forceUpdate();
        },
        methods: {
            bg(type, index) {
                let bg = "";
                switch (type) {
                    case 0: // 地板
                        bg = "unset";
                        break;
                    case 1: // 虫子
                        bg = `url(${worm})`;
                        break;
                    case 2: // 蛇
                        // 蛇的数组最后一个就是蛇头了
                        let head = this.snakes[this.snakes.length - 1];
                        // 同理第一个就是蛇尾
                        let tail = this.snakes[0];
                        if (index === head) {
                            bg = `url(${snakeHead})`;
                        } else if (index === tail) {
                            bg = `url(${snakeTail})`;
                        } else {
                            bg = `url(${snakeBody})`;
                        }
                        break;
                }
                return bg;
            }
        },
    }
</script>
复制代码

1649768716(1).jpg

至此,蛇的绘制完成了,但是看着好难受啊,蛇的头身子尾巴都没对上呢

让它看着正常

将图片顺时针旋转90度:

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" 
                :style="{'background-image': bg(x, i),transform: rotate(90deg)}" 
                v-for="(x, i) in blocks" :key="i">
                {{x}}
            </view>
        </view>
    </view>
</template>
复制代码

1649768861(1).jpg

欧耶,看着正常了!emmmm...貌似哪里不对劲,啊呀,虫子怎么倒了~,加个判断,是蛇才旋转90度:

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" 
                :style="{'background-image': bg(x, i),
                transform: x === 2 ? 'rotate(90deg)': 'rotate(0)'}" 
                v-for="(x, i) in blocks" :key="i">
                {{x}}
            </view>
        </view>
    </view>
</template>
复制代码

1649769186(1).jpg

嗯,正常了!

开始游戏

蛇和虫子都画完了,接下来我们应该考虑的是如何让蛇动起来

让蛇动起来

要让蛇动,肯定要加个定时器了。我们刚开始是在onLoad的时候渲染一次界面,要让蛇动起来,我们需要将绘制蛇的方法封装起来,这样才能在定时器中循环执行:

<script>
    // ...
    export default {
        // ...
        data() {
            return: {
                // ...
                timer: null // 定时器
            }
        },
        onLoad() {
            // ...
            this.paint();
            this.timer = setInterval(() => {
                this.toWards(); // 每秒向前走一格
            }, 1000);
        },
        methods: {
            // ...
            paint() {
                this.worms.forEach((x) => {
                    this.blocks[x] = 1;
                });
                this.snakes.forEach((x) => {
                    this.blocks[x] = 2;
                });
                this.$forceUpdate();
            },
            toWards() {
                // 头部下标
                let head = this.snakes[this.snakes.length - 1];
                // 尾部下标
                let tail = this.snakes[0];
                // 向右走一格
                let next = head + 1; 
                // 那么下一格就应该加入蛇的数组中
                this.snakes.push(next); 
                // 保存下一格是什么类型方便一会判断蛇的数组是否需要弹出尾部元素
                let nextType = this.blocks[next]; 
                // 蛇头经过的位置必然变成蛇的格子,于是赋值为2
                this.blocks[next] = 2;
                // 如果是空白格,则蛇的长度应该保持不变,需要弹出蛇的尾部下标
                if (nextType === 0) {
                    this.snakes.shift();
                } else {
                    // 如果是虫子格,则虫子数组会过滤掉被吃的虫子
                    this.worms = this.worms.filter((x) => x !== next);
                }
                // 蛇尾部经过后一定是土地格子了,好好思考下
                this.blocks[tail] = 0;
                // 绘制
                this.paint();
            }
        },
    }
</script>
复制代码

1649770928(1).jpg

蛇是动起来了,但是好像有点呆,不会拐弯,并且我们发现它会穿过边界,这不是我们想要的效果。我们需要在界面上添加四个按钮,用来操控我们可爱的蛇蛇,绑定上各自的事件:

绘制上下左右键

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" 
                :style="{'background-image': bg(x, i),transform: rotate(90deg)}" 
                v-for="(x, i) in blocks" :key="i">
                {{x}}
            </view>
        </view>
        <view class="action-field">
            <button @click="bindUp">上</button>
            <view class="flex">
                <button @click="bindLeft">左</button>
                <button @click="bindRight">右</button>
            </view>
            <button @click="bindDown">下</button>
        </view>
    </view>
</template>
<script>
    // ...
    export default {
        // ...
        methods: {
            bindUp() {
                console.log('up')
            },
            bindDown() {
                console.log('down')
            },
            bindLeft() {
                console.log('left')
            },
            bindRight() {
                console.log('right')
            },
        },
    }
</script>
<style>
    /* ... */
    .flex {
        display: flex;
        width: 50%;
        justify-content: space-between;
    }
    .action-field {
        display: flex;
        flex-direction: column;
        width: 100%;
        align-items: center;
    }
</style>
复制代码

emmmm 1649748432(1).png

我们的蛇只会往右走,我们在toWards方法里的next赋值加个判断方向的逻辑:

methods: {
    // ...
    toWards() {
        // ...
        let next;
        switch (this.direction) {
            case "up":
                next = head - 10;
                break;
            case "down":
                next = head + 10;
                break;
            case "left":
                next = head - 1;
                break;
            case "right":
                next = head + 1;
                break;
        }
        // ...
        this.paint();
    },
    bindUp() {
        this.direction = 'up';
    },
    bindDown() {
        this.direction = 'down';
    },
    bindLeft() {
        this.direction = 'left';
    },
    bindRight() {
        this.direction = 'right';
    },
}
复制代码

1649772093(1).jpg

妈耶!蛇头方向不对啊。之前粗暴的固定旋转90度是不对的,这个背景图的旋转需要具体问题具体分析呢,新增一个计算旋转的方法,此方法需要好好思考:

<template>
    <view class="content">
        <view class="game-field">
            <view class="block" 
                :style="{'background-image': bg(x, i),
                transform: `rotate(${calcRotate(x, i)}deg)`}" 
                v-for="(x, i) in blocks" :key="i">
                {{x}}
            </view>
        </view>
        <view class="action-field">
            <button @click="bindUp">上</button>
            <view class="flex">
                <button @click="bindLeft">左</button>
                <button @click="bindRight">右</button>
            </view>
            <button @click="bindDown">下</button>
        </view>
    </view>
</template>
<script>
    // ...
    export default {
        // ...
        methods: {
            // ...
            calcRotate(type, index) {
                let rotate = 0;
                switch (type) {
                    case 0: // 地板
                        rotate = 0;
                        break;
                    case 1: // 虫子
                        rotate = 0;
                        break;
                    case 2: // 蛇
                        let length = this.snakes.length;
                        let head = this.snakes[length - 1];
                        let tail = this.snakes[0];
                        // 尾巴的前一个
                        let tailPre = this.snakes[1];
                        // 身子的前一个
                        let bodyPre = this.snakes[this.snakes.indexOf(index) + 1];
                        if (index === head) {
                            if (this.direction === "right") {
                                rotate = 90;
                            } else if (this.direction === "down") {
                                rotate = 180;
                            } else if (this.direction === "left") {
                                rotate = 270;
                            } else {
                                rotate = 0;
                            }
                        } else if (index === tail) {
                            if (tailPre - 1 === tail) {
                                // 向右走的
                                rotate = 90;
                            } else if (tailPre - 10 === tail) {
                                // 向下走的
                                rotate = 180;
                            } else if (tailPre + 1 === tail) {
                                // 向左走的
                                rotate = 270;
                            } else {
                                // 向上走的
                                rotate = 0;
                            }
                        } else {
                            if (bodyPre - 1 === index) {
                                // 向右走的
                                rotate = 90;
                            } else if (bodyPre - 10 === index) {
                                // 向下走的
                                rotate = 180;
                            } else if (bodyPre + 1 === index) {
                                // 向左走的
                                rotate = 270;
                            } else {
                                // 向上走的
                                rotate = 0;
                            }
                        }
                        break;
                }
                return rotate;
            },
        },
    }
</script>
复制代码

1649772878(1).jpg

看,它变得像那么回事了,我们的蛇现在还不会死,我们需要设置它撞到边界,或者自身会死,也就是游戏结束。

<script>
    // ...
    export default {
        // ...
        methods: {
            // ...
            toWards() {
                if (this.snakes.length === 100) {
                    alert("你赢了!");
                    clearInterval(this.timer);
                    return;
                }
                let head = this.snakes[this.snakes.length - 1];
                let tail = this.snakes[0];
                let next;
                switch (this.direction) {
                    case "up":
                        next = head - 10;
                        break;
                    case "down":
                        next = head + 10;
                        break;
                    case "left":
                        next = head - 1;
                        break;
                    case "right":
                        next = head + 1;
                        break;
                }
                let gameover = this.checkGame(next);
                if (gameover) {
                    console.log("游戏结束");
                    clearInterval(this.timer);
                } else {
                    // 游戏没结束
                    this.snakes.push(next);
                    let nextType = this.blocks[next];
                    this.blocks[next] = 2;
                    // 如果是空白格
                    if (nextType === 0) {
                        this.snakes.shift();
                    } else {
                        // 如果是虫子格
                        this.worms = this.worms.filter((x) => x !== next);
                    }
                    this.blocks[tail] = 0;
                    this.paint();
                }
            },
            checkGame(next) {
                let gameover = false;
                let isSnake = this.snakes.indexOf(next) > -1;
                if (isSnake) {
                    gameover = true;
                }
                switch (this.direction) {
                    case "up":
                        if (next < 0) {
                            gameover = true;
                        }
                        break;
                    case "down":
                        if (next >= 100) {
                            gameover = true;
                        }
                        break;
                    case "left":
                        if (next % 10 === 9) {
                            gameover = true;
                        }
                        break;
                    case "right":
                        if (next % 10 === 0) {
                            gameover = true;
                        }
                        break;
                }
                return gameover;
            },
        },
    }
</script>
复制代码

1649773393(1).jpg

撞到上边缘,挂了!

随机生成虫子

Actualmente, hemos escrito tres bichos muertos, de hecho, nuestros bichos se generan aleatoriamente, y después de comer uno, se generará el otro, y el bicho no aparecerá en la posición de la serpiente:

methods: {
    // ... 
     toWards() {
        if (this.snakes.length === 100) {
            alert("你赢了!");
            clearInterval(this.timer);
            return;
        }
        let head = this.snakes[this.snakes.length - 1];
        let tail = this.snakes[0];
        let next;
        switch (this.direction) {
            case "up":
                next = head - 10;
                break;
            case "down":
                next = head + 10;
                break;
            case "left":
                next = head - 1;
                break;
            case "right":
                next = head + 1;
                break;
        }
        let gameover = this.checkGame(next);
        if (gameover) {
            console.log("游戏结束");
            clearInterval(this.timer);
        } else {
            // 游戏没结束
            this.snakes.push(next);
            let nextType = this.blocks[next];
            this.blocks[next] = 2;
            // 如果是空白格
            if (nextType === 0) {
                this.snakes.shift();
            } else {
                // 如果是虫子格
                this.worms = this.worms.filter((x) => x !== next);
                let nextWorm = this.createWorm();
                this.worms.push(nextWorm);
            }
            this.blocks[tail] = 0;
            this.paint();
        }
    },
    // 生成下一只虫子
    createWorm() {
        let blocks = Array.from({length: 100}, (v, k) => k);
        let restBlocks = blocks.filter(x => this.snakes.indexOf(x) < 0);
        let worm = restBlocks[Math.floor(Math.random() * restBlocks.length)];
        return worm;
    },
}
复制代码

Optimización de detalles

Elimina los bordes de la cuadrícula y los números en la cuadrícula:

1649773690(1).jpg

Hasta ahora, las funciones básicas de Snake se han completado y felicitaciones a los estudiantes que solo saben div. Habrá actualizaciones en el futuro, por favor dale me gusta y sigue

Función de tareas pendientes

  • Puede moverse hacia arriba, abajo, izquierda y derecha.
  • Golpea los cuatro lados, se acabó el juego.
  • Golpéate a ti mismo juego terminado
  • Las serpientes crecerán de lado después de comer insectos
  • Comer un error generará un error aleatorio
  • Calcula cuánto mide la serpiente actual
  • Se agregaron errores explosivos
  • Tierra contaminada después de la explosión de un insecto
  • Golpea el juego de la tierra contaminada
  • añadir sistema de usuario
  • Añadir tabla de clasificación

Gracias especiales

@大 simio viejo y guapo @Sophora . Gracias al Sr. Dashuai por liderar el equipo, brindar apoyo material y soporte técnico, y gracias a la ayuda de los compañeros de equipo.

Dirección del código fuente , los comentarios son bienvenidos, si se siente bien, haga clic en estrella. Adiós Jianghu~

Supongo que te gusta

Origin juejin.im/post/7085727363547283469
Recomendado
Clasificación