10分钟编写一个贪吃蛇

先来了解一些简单的基础知识

下面是一个20 x 20的矩阵 用来表示贪吃蛇的地图

坐标原点为左下角

我们需要有一个数组来保存当前蛇的蛇身体

snake数组是存在顺序的,数组的第一个元素是蛇头的位置,数组的最后一位就是蛇的尾巴

let snake: Array<number> = [202, 201];

接下来是蛇的移动,也就是数组数据的移动

  • 上边移动 蛇头数据+20
  • 下边移动 蛇头数据-20
  • 左边移动 蛇头数据-1
  • 右边移动 蛇头数据+1

上边只说到蛇头数据,因为我们只需要修改一个蛇头的数据,不用修改整条蛇的数据

蛇的数据

snake = [202, 201];

当前蛇的方向是右边,也就是数据+1,加入一个新的蛇头

snake = [203, 202, 201];

当前蛇的长度变成了3,可是我们的蛇应该只有2的长度,还需要把蛇的尾巴去掉一个

snake = [203, 202];

这样蛇就完成了向右一格的移动 上下左移动都是同理,这里就不赘述了

下面进入正题了

10分钟编写一个贪吃蛇,先确定下规则

  • 贪吃蛇由上下左右键位控制
  • 蛇头能穿越墙壁从另一面出来
  • 吃到食物增加一个长度
  • 撞到自己的身体即为失败

新建一个空的项目

加入一个空节点,用来承载地图,命名为map

  • map需要挂载一个画图的组件

接下来是编写脚本Map.ts

初始化一些变量

    snake: Array<number> = [202, 201]; // 定义蛇的数组 
    food = 203; // 食物 
    moveOffset = 1; // 蛇头的变化值 
    nextIndex = 0; // 下一个蛇头的位置id
    g: cc.Graphics = null;
    canControl = false; // 是否可以控制

先加入控制的方法

    onEnable() {
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown.bind(this));
    }

    onDisable() {
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown.bind(this));
    }

    onKeyDown(e: cc.Event.EventKeyboard) {
        if (!this.canControl){
            return;
        }
        this.moveOffset = this.snake[1] - this.snake[0] == (this.nextIndex = [-1, 20, 1, -20][e.keyCode - 37] || this.moveOffset) ? this.moveOffset : this.nextIndex;
    }

简单分析这段代码:

this.moveOffset = this.snake[1] - this.snake[0] == (this.nextIndex = [-1, 20, 1, -20][e.keyCode - 37] || this.moveOffset) ? this.moveOffset : this.nextIndex;
  • this.moveOffset 是蛇头的移动变量 也就是到下一格变化量
  • this.snake[1] - this.snake[0] 获得当前蛇头不能变化的方法
  • this.nextIndex 在这里做了下临时变量,记录操作值
  • 左上右下的keyCode 分别为37 38 39 40 如果按其他按键就使用原来的移动变量

如果还不理解可以看下运算符的执行顺序

主要的移动和碰撞判断代码,有几个点需要简单分析下

  • [].unshift 从头部加入数组
  • [].indexOf 获得元素在数组中的位置(这里用来判断是否在数组中)
  • 循环遍历食物不存在与当前的蛇体内 不会无限循环 因为覆盖会在上面的函数中跳出
  • 尾部去除的格子用背景色覆盖
    move() {
        // 增加当前蛇头的位置
        this.nextIndex = this.snake[0] + this.moveOffset
        // 加入新蛇头
        this.snake.unshift(this.nextIndex);
        // 判断新蛇头有没撞到自己
        if(this.snake.indexOf(this.nextIndex, 1) > 0) { 
            this.replay.getChildByName('result').getComponent(cc.Label).string = `score: ${this.snake.length * 100}`;
            this.replay.active = true;
            // 变红
            this.snake.forEach(v => {
                this.draw(v, cc.Color.RED);
            });
            return;
        }
        // 蛇头的越界修复
        if (this.moveOffset == -20 && this.nextIndex < 0) {
            this.snake[0] = this.snake[0] + 400;  
        } else if(this.moveOffset == 20 && this.nextIndex > 399) {
            this.snake[0] = this.snake[0] - 400;  
        } else  if (this.moveOffset == 1 && (this.nextIndex % 20 == 0 || this.nextIndex > 399)) {
            this.snake[0] = this.snake[0] - 20;  
        } else  if (this.moveOffset == -1 && (this.nextIndex % 20 == 19 || this.nextIndex < 0)) {
            this.snake[0] = this.snake[0] + 20;  
        }
        // 画出当前的蛇头
        this.draw(this.snake[0], cc.Color.GREEN);
        // 如果当前的蛇头是产生的糖果
        // 产生新的糖果
        if(this.nextIndex == this.food){
            // 当前产生的块不存在蛇
            while(this.snake.indexOf(this.food = ~~(Math.random() * 400)) >= 0);
            // 产生新的事物
            this.draw(this.food, cc.Color.YELLOW);
        }else{
            // 删除原来的点
            this.draw(this.snake.pop(), cc.Color.GRAY);
        }
        setTimeout(this.move.bind(this), 1000/10)
    };

最简单的Draw方法

  • 画出当前方块的实体
    draw(mapIndex: number, color: cc.Color) {
        this.g.fillColor = color;
        this.g.fillRect(mapIndex % 20 * 20 - 2 , ~~(mapIndex / 20) * 20 - 2, 16, 16)
    }

博客地址:https://allknowboy.com/posts/ca90cf50/

6赞

Mark一下,楼主流掰

mark

mark

mark

make

厉害厉害啊

谢谢分享

mark

mark

流弊大神

厉害厉害!


不过使用graphics画图有残留怎么回事?

厉害!

mark

mark

mark

应该是你画的灰色块没有盖住绿色的蛇身

mark

我也很奇怪,参数都一样,只有颜色不同,重复在一个地方画不同颜色的方块就会出现这样的问题。

mark