贪吃蛇设计与实现

效果图

在这里插入图片描述

设计

贪吃蛇游戏是一款休闲益智类游戏。既简单又耐玩。该游戏通过控制蛇头方向吃蛋,从而使得蛇变得越来越长。

玩法:
点击屏幕控制蛇的移动方向,寻找吃的东西,每吃一口就能得到一定的积分,而且蛇的身子会越吃越长,身子越长玩的难度就越大,不能咬到自己的身体,更不能咬自己的尾巴,等到了一定的分数,游戏胜利。

设计:
首先需要创建一个棋盘,然后需要生成一条贪吃蛇,接着随机生成食物。每当蛇吃到食物的时候,随机生成新的食物,蛇头吃到自己的身体的时候游戏结束。

棋盘设计:

元素 :行数,列数,基础细胞(可表现为空,食物,蛇身体);

属性 :创建棋盘,清空棋盘;

基础细胞设计:

属性 :重设颜色,重设大小;

食物:

需求 : 需要在棋盘剩余空白位置随机位置生成食物;

贪吃蛇:

元素 : 位置集合(数组),移动速率,移动方向

需求: 初始随机生成只有一节的贪吃蛇,定时器函数(根据移动方向求得下一个要移动到的位置,需要注意的是到达边界后进行特殊处理。判断下个位置是否为蛇本身,如果是蛇就吃到自己,游戏结束。接着将下个位置添加到蛇位置集合内,最后判断下个位置 是否与食物相同,如果相同,则重现生成新的食物,否则移除蛇尾)。

方向控制:
本游戏使用点击屏幕,控制蛇移动方向。

实现

cell.js

/*
 * @Author: ls
 * @Date: 2020-09-01 18:23:09
 * @LastEditTime: 2020-09-16 14:23:37
 * @LastEditors: Please set LastEditors
 * @Description: 基础细胞类
 * @FilePath: \snake\assets\cell.js
 */

cc.Class({
    extends: cc.Component,

    properties: {},

    onLoad() {},

    /**
     * @param {*} cellColor
     */
    setCellColor(cellColor = new cc.color(255, 255, 255, 255)) {
        this.node.getChildByName('color').color = cellColor;
    },

    /**
     * @param {*} cellSize
     */
    setCellPos(cellSize = new cc.v2(20, 20)) {
        this.node.width = cellSize.x;
        this.node.height = cellSize.y;
    },
});

guideCtrl.js

/*
 * @Author: ls
 * @Date: 2020-09-03 18:09:18
 * @LastEditTime: 2020-09-14 08:55:47
 * @LastEditors: Please set LastEditors
 * @Description: 引导类
 * @FilePath: \snake\assets\guideCtrl.js
 */

cc.Class({
    extends: cc.Component,

    properties: {
        step: [cc.Node],
        startToggle: cc.Toggle,
    },

    onLoad() {
        this.startGuide();
        this.startToggle.isChecked = false;
    },

    /**
     * 开始引导
     */
    startGuide() {
        if (!this.step.length) {
            this.node.destroy();
            return;
        }
        for (let index = 0, length = this.step.length; index < length; index++) {
            this.step[index].active = false;
        }
        this._step = 0;
        this.step[0].active = true;
    },

    /**
     * 下一个引导页面
     */
    nextGuide() {
        this._step++;
        if (this._step < this.step.length - 1) {
            this.step[this._step].active = true;
            this.step[this._step - 1].active = false;
            if (this._step === this.step.length - 2) {
                this.step[this._step + 1].active = true;
            }
        } else {
            this.node.active = false;
        }
    },

    callback: function (toggle) {
        cc.sys.localStorage.setItem('isStart', toggle.isChecked);
    },
});

gameCtrl.js

/*
 * @Author: ls
 * @Date: 2020-09-01 15:44:33
 * @LastEditTime: 2020-09-16 14:23:18
 * @LastEditors: Please set LastEditors
 * @Description: 游戏导演类
 * @FilePath: \snake\assets\gameController.js
 */

var noneColor = new cc.color(120, 120, 120, 255);
var foodColor = new cc.color(254, 168, 23, 255);
var snakeColor = new cc.color(243, 60, 66, 255);

cc.Class({
    extends: cc.Component,

    properties: {
        // 棋盘
        node_grid: cc.Node,
        // 分数
        lab_score: cc.Label,
        // 最好分数
        lab_best: cc.Label,

        // 开始
        node_start: cc.Node,
        // 新人引导
        node_guide: cc.Node,
        // 结束
        node_over: cc.Node,

        // 基础类
        cellPrefab: cc.Prefab,

        // 移动速度
        mSpeed: 5,
        // 列数
        colCount: 30,
        // 行数
        rowCount: 30,
    },

    onLoad() {
        // 初始化方向
        // 静止、上、下、左、右
        // (0,0)、(0,1)、(0,-1)、(-1,0)、(1,0)
        this._direction = { x: 0, y: 0 };
        // 初始化细胞大小
        this._cellSize = { x: 10, y: 10 };

        this._map = [];
        this.initCellPool();
        this.onCreateMap();
        // 显示开始游戏界面
        this.showStartGame();
    },

    /**
     * 初始化细胞对象池
     */
    initCellPool() {
        this.cellPool = new cc.NodePool();
        let initCount = this.rowCount * this.colCount;
        for (let i = 0; i < initCount; i++) {
            let cell = cc.instantiate(this.cellPrefab); // 创建节点
            this.cellPool.put(cell); // 通过 put 接口放入对象池
        }
    },

    /**
     * 创建地图
     */
    onCreateMap() {
        this._map = [];
        let node_bg = this.node_grid.getChildByName('background');
        this._cellSize = { x: node_bg.width / this.rowCount, y: node_bg.height / this.colCount };

        for (var y = 0; y < this.colCount; y++) {
            for (let x = 0; x < this.rowCount; x++) {
                var obj = {};
                obj.x = x;
                obj.y = y;
                obj.node = this.createCell(node_bg, x, y);

                this._map.push(obj);
            }
        }
    },

    /**
     * 从对象池请求对象
     * @param {*} parentNode
     */
    createCell: function (parentNode, x, y) {
        let cell = null;
        if (this.cellPool.size() > 0) {
            // 通过 size 接口判断对象池中是否有空闲的对象
            cell = this.cellPool.get();
        } else {
            // 如果没有空闲对象,也就是对象池中备用对象不够时,我们就用 cc.instantiate 重新创建
            cell = cc.instantiate(this.cellPrefab);
        }

        cell.getComponent('cell').setCellPos(this._cellSize);

        cell.x = this._cellSize.x * x;
        cell.y = this._cellSize.y * y;

        cell.parent = parentNode;
        return cell;
    },

    /**
     * 还原地图
     */
    clearMap() {
        for (let index = 0, length = this._map.length; index < length; index++) {
            this._map[index].node.getComponent('cell').setCellColor(noneColor);
        }
    },

    /**
     * 显示开始界面
     */
    showStartGame() {
        this.node_over.active = false;
        this.node_start.active = true;
    },

    /**
     * 显示结束界面
     */
    showOverGame() {
        this.node_start.active = false;
        this.node_over.active = true;
    },

    /**
     * 游戏开始
     */
    startGame() {
        this.node_guide.active = false;
        this.node_over.active = false;
        this.node_start.active = false;
        this.lab_score.node.active = true;
        this.lab_best.node.active = true;
        this.node_grid.active = true;
        // 是否首次进入界面
        if (!cc.sys.localStorage.getItem('isStart')) {
            this.node_guide.active = true;
        }

        this._score = 0;
        // 更新最高分数
        this.updateBest();

        this._canControl = true;
        this._direction = { x: 1, y: 0 };
        this._snakeGrid = [];
        this._foodGrid = {};

        // 初始化触摸事件
        this.openTouchEvent();

        this.clearMap();
        this.onCreateSnake();
        this.onCreateFood();

        // 开启移动
        this.schedule(this.move, 1 / this.mSpeed);
    },

    /**
     * 更新分数
     */
    updateBest() {
        this._best = cc.sys.localStorage.getItem('best');
        if (this._best) {
            if (this._best < this._score) {
                this._best = this._score;
                cc.sys.localStorage.setItem('best', this._best);
            }
        } else {
            this._best = this._score;
            cc.sys.localStorage.setItem('best', this._best);
        }

        this.lab_best.string = this._best;
    },

    /**
     * 游戏结束
     */
    gameOver() {
        // 是否能控制 蛇改变移动方向
        this._canControl = false;

        this.unschedule(this.move);
        this.closeTouchEvent();
        this.clearMap();
        this.showOverGame();
    },

    /**
     * 创建蛇
     */
    onCreateSnake() {
        let x = ~~(Math.random() * this.rowCount);
        let y = ~~(Math.random() * this.colCount);

        for (let index = 0, length = this._map.length; index < length; index++) {
            if (this._map[index].x === x && this._map[index].y === y) {
                this._map[index].node.getComponent('cell').setCellColor(snakeColor);
                this._snakeGrid.push(this._map[index]);
            }
        }
    },

    /**
     * 创建食物
     */
    onCreateFood() {
        if (this._map.length !== this._snakeGrid.length) {
            let r = ~~(Math.random() * (this._map.length - this._snakeGrid.length));

            let subGrid = [];
            for (let i = 0; i < this._map.length; i++) {
                subGrid.push(this._map[i]);
            }

            for (let m = 0; m < subGrid.length; m++) {
                for (let n = 0; n < this._snakeGrid.length; n++) {
                    if (subGrid[m].x === this._snakeGrid[n].x && subGrid[m].y === this._snakeGrid[n].y) {
                        subGrid.splice(m, 1);
                        if (m > 0) {
                            m--;
                        }
                    }
                }
            }

            for (let index = 0; index < subGrid.length; index++) {
                if (index === r) {
                    this._foodGrid = subGrid[index];
                    this._foodGrid.node.getComponent('cell').setCellColor(foodColor);

                    // 增加分数
                    this._score++;
                    this.lab_score.string = this._score;
                }
            }
        }
    },

    /**
     * 打开触摸
     */
    openTouchEvent() {
        var self = this;
        this.node.on(
            cc.Node.EventType.TOUCH_START,
            function (touch) {
                if (self._canControl) {
                    self._canControl = false;
                    let touchPos = self.node.convertToNodeSpaceAR(touch.getLocation());
                    self._direction = self.getTouchDirection(touchPos);

                    this.scheduleOnce(function () {
                        self._canControl = true;
                    }, 1 / this.mSpeed);
                }
            },
            this
        );
    },

    /**
     * 关闭触摸
     */
    closeTouchEvent() {
        this.node.off(cc.Node.EventType.TOUCH_START, this);
    },

    /**
     * 获取选择的方向
     * @param {* 触摸位置} touchPos
     */
    getTouchDirection(touchPos) {
        // 获取向量长度
        function getABS(pos) {
            return Math.sqrt(pos.x * pos.x + pos.y * pos.y);
        }
        // 获取横向 方向
        function getLandscape(touchPos) {
            if (touchPos.x > 0) {
                cc.log('更改为 向 右 移动');
                return { x: 1, y: 0 };
            } else {
                cc.log('更改为 向 左 移动');
                return { x: -1, y: 0 };
            }
        }
        // 获取竖向 方向
        function getPortrait(touchPos) {
            if (touchPos.y > 0) {
                cc.log('更改为 向 上 移动');
                return { x: 0, y: 1 };
            } else {
                cc.log('更改为 向 下 移动');
                return { x: 0, y: -1 };
            }
        }

        if (getABS(this._direction) === 1) {
            cc.log('蛇 正在移动');
            if (this._direction.y === 1) {
                cc.log('蛇 正在向 上 移动');
                return getLandscape(touchPos);
            } else if (this._direction.y === -1) {
                cc.log('蛇 正在向 下 移动');
                return getLandscape(touchPos);
            } else if (this._direction.x === -1) {
                cc.log('蛇 正在向 左 移动');
                return getPortrait(touchPos);
            } else if (this._direction.x === 1) {
                cc.log('蛇 正在向 右 移动');
                return getPortrait(touchPos);
            }
        } else {
            cc.log('蛇 未开始 或 停止了移动。此时修改方向无效!');
        }
    },

    /**
     * 移动
     */
    move() {
        let nextGrid = {};
        nextGrid.x = this._snakeGrid[this._snakeGrid.length - 1].x + this._direction.x;
        nextGrid.y = this._snakeGrid[this._snakeGrid.length - 1].y + this._direction.y;

        if (this._direction.x === 1) {
            // 向右
            if (nextGrid.x > this.colCount - 1) {
                nextGrid.x = 0;
            }
        } else if (this._direction.x === -1) {
            // 向左
            if (nextGrid.x < 0) {
                nextGrid.x = this.colCount - 1;
            }
        } else if (this._direction.y === 1) {
            // 向上
            if (nextGrid.y > this.rowCount - 1) {
                nextGrid.y = 0;
            }
        } else if (this._direction.y === -1) {
            // 向下
            if (nextGrid.y < 0) {
                nextGrid.y = this.rowCount - 1;
            }
        }

        for (let m = 0, l = this._map.length; m < l; m++) {
            if (this._map[m].x === nextGrid.x && this._map[m].y === nextGrid.y) {
                nextGrid = this._map[m];
            }
        }

        for (let n = 0, length = this._snakeGrid.length; n < length; n++) {
            if (nextGrid.x === this._snakeGrid[n].x && nextGrid.y === this._snakeGrid[n].y) {
                this.gameOver();
                // return false;
            }
        }

        nextGrid.node.getComponent('cell').setCellColor(snakeColor);
        this._snakeGrid.push(nextGrid);

        if (nextGrid.x === this._foodGrid.x && nextGrid.y === this._foodGrid.y) {
            this.onCreateFood();
        } else {
            let startGrid = this._snakeGrid.shift();
            startGrid.node.getComponent('cell').setCellColor(noneColor);
        }
    },
});

完整代码

GiteeCSDN

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值