Créez un jeu de serpent à partir de zéro, sachez juste vue

Je participe au concours d'équipe du concours de création de jeu de la communauté Nuggets. Pour plus de détails, veuillez consulter : Concours de contribution à la création de jeu

Aujourd'hui, je vais partager avec vous un petit jeu de serpent gourmand, il est mis en html + css + jsoeuvre à l' vue + uniCloudétat pur. Étant donné que la pile technologique est très peu profonde, j'espère que les grands ne la vaporiseront pas. Sans plus tarder, commençons tout droit.

Le diagramme d'effet est le suivant

rendus.jpg

Initialiser le projet

Ce projet est uni-appbasé sur le développement, il est donc préférable de l'utiliser HBuildercomme éditeur de votre choix.

Nouveau projet

Ouvrez HBuilder, créez un nouveau projet, nommez le projet snake_eat_worm(le serpent mange des insectes), je lui ai donné un grand nom : Snake Fighting Group Worm. Toutes les notes pour le nom, donnez-vous une cuisse de poulet. Sélectionnez le modèle par défaut, cochez Activer uniCloud(Alibaba Cloud est utilisé par défaut) et cliquez sur Créer.

Nouveau projet.jpgCela HBuildergénérera automatiquement le répertoire du projet :

répertoire du projet.png

Nous gérons le projet directement :

exécuter projet.png

Voir l'interface suivante indique que le projet s'exécute avec succès

exécuter avec succès.jpg

Préparation des ressources du jeu

mettre des ressources dans un static/imagesrépertoire

ressources du jeu.jpg

carte

Ouvrez la pages/indexpage, modifions-la et ajoutons nos propres éléments :

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

Modifier la page d'accueil.jpg

ok, la page est déjà sous notre contrôle, nous allons la démarrer officiellement.

dessiner le sol

Nous définissons le sol comme une grille 10 * 10 et utilisons un div pour dessiner une grille 10 * 10 ?

Initialiser la grille.png

Jeune héros ! Attendez, juste un div suffit ! Si vous ne me croyez pas, voyez :

<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>
复制代码

Je crois que les amis qui ne connaissent que les divs devraient être plus familiers avec le rendu des divs répétés ci-dessus que moi:

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

xx.jpg

Bravo, maintenant il y a un morceau de terrain jaune dans notre interface de jeu, voyons comment mettre quelques bugs au sol.

bogues de dessin

Avant de dessiner, réfléchissons à la façon d'utiliser div pour dessiner différents motifs (serpents et insectes ou terre) dans différentes grilles. Nous pourrions aussi bien considérer ces 100 grilles comme un tableau. Chaque grille correspond aux indices 0-99 du tableau. Nous initialisons le tableau à 100 zéros et nous utilisons le nombre 0 pour représenter le terrain. Utilisez le numéro 1 pour l'insecte et le numéro 2 pour le serpent. Nous avons légèrement modifié le code ci-dessus :

<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>
复制代码

tableau.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

撞到上边缘,挂了!

随机生成虫子

À l'heure actuelle, nous avons écrit trois bogues morts. En fait, nos bogues sont générés de manière aléatoire, et après en avoir mangé un, l'autre sera généré, et le bogue n'apparaîtra pas dans la position du serpent :

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;
    },
}
复制代码

Optimisation des détails

Supprimez les bordures de la grille et les nombres dans la grille :

1649773690(1).jpg

Jusqu'à présent, les fonctions de base de Snake ont été complétées, et félicitations aux étudiants qui ne connaissent que div. Il y aura des mises à jour à l'avenir, veuillez aimer et suivre

Fonction à faire

  • Peut monter, descendre, gauche et droite
  • Frappez quatre côtés game over
  • Cognez-vous game over
  • Les serpents pousseront sur le côté après avoir mangé des insectes
  • Manger un insecte générera un insecte aléatoire
  • Calculer la longueur du serpent actuel
  • Ajout de bugs explosifs
  • Terre polluée après l'explosion d'un insecte
  • Frappez le jeu de la terre polluée
  • ajouter un système utilisateur
  • Ajouter un classement

Remerciement spécial

@大 beau vieux singe @Sophora . Merci à M. Dashuai pour avoir dirigé l'équipe, apporté un soutien matériel et un soutien technique, et merci à l'aide des coéquipiers.

Adresse du code source , les commentaires sont les bienvenus, si vous vous sentez bien, cliquez sur l'étoile. Au revoir Jianghu~

Je suppose que tu aimes

Origine juejin.im/post/7085727363547283469
conseillé
Classement