javascript贪吃蛇游戏设计与实现


Posted in Javascript onSeptember 17, 2020

本文为大家分享了javascript实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下

效果图

javascript贪吃蛇游戏设计与实现

设计

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

玩法:

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

设计:

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

棋盘设计:

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

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

基础细胞设计:

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

食物:

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

贪吃蛇:

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

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

方向控制:

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

实现

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);
 }
 },
});

完整代码:js贪吃蛇游戏

更多有趣的经典小游戏实现专题,分享给大家:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 未结束的字符串常量常见解决方法
Jan 24 Javascript
jquery 面包屑导航 具体实现
Jun 05 Javascript
轮播图组件js代码
Aug 08 Javascript
jquery validation验证表单插件
Jan 07 Javascript
JS实现多级菜单中当前菜单不随页面跳转样式而发生变化
May 30 Javascript
jQuery length 和 size()区别总结
Apr 26 jQuery
jQuery实现导航样式布局操作示例【可自定义样式布局】
Jul 24 jQuery
JavaScript常用事件介绍
Jan 21 Javascript
vue将后台数据时间戳转换成日期格式
Jul 31 Javascript
vue页面更新patch的实现示例
Mar 25 Javascript
Vue自动构建发布脚本的方法示例
Jul 24 Javascript
原生JavaScript实现简单五子棋游戏
Jun 28 Javascript
js实现简单的随机点名器
Sep 17 #Javascript
谈谈JavaScript中的垃圾回收机制
Sep 17 #Javascript
js对象属性名驼峰式转下划线的实例代码
Sep 17 #Javascript
详细分析JavaScript中的深浅拷贝
Sep 17 #Javascript
js实现鼠标滑动到某个div禁止滚动
Sep 17 #Javascript
原生js+css实现tab切换功能
Sep 17 #Javascript
vue使用screenfull插件实现全屏功能
Sep 17 #Javascript
You might like
PHP实现手机归属地查询API接口实现代码
2012/08/27 PHP
php MessagePack介绍
2013/10/06 PHP
用Javascript实现Sleep暂停功能代码
2010/09/03 Javascript
对象无length属性时IE6/IE7中无法将其转换成伪数组(ArrayLike)
2011/07/31 Javascript
jQuery日期范围选择器附源码下载
2017/05/23 jQuery
基于ES6 Array.of的用法(实例讲解)
2017/09/05 Javascript
Angular 5.0 来了! 有这些大变化
2017/11/15 Javascript
vue.js中created方法作用
2018/03/30 Javascript
Vue无限滑动周选择日期的组件的示例代码
2018/07/18 Javascript
分享5个小技巧让你写出更好的 JavaScript 条件语句
2018/10/20 Javascript
javascript中一些奇葩的日期换算方法总结
2018/11/14 Javascript
JavaScript封闭函数及常用内置对象示例
2019/05/13 Javascript
vue使用混入定义全局变量、函数、筛选器的实例代码
2019/07/29 Javascript
[03:08]迎霜节狂欢!2018年迎霜节珍藏Ⅰ一览
2018/12/25 DOTA
在Python中使用cookielib和urllib2配合PyQuery抓取网页信息
2015/04/25 Python
在Django的视图中使用form对象的方法
2015/07/18 Python
使用Python发送各种形式的邮件的方法汇总
2015/11/09 Python
使用Python3 编写简单信用卡管理程序
2016/12/21 Python
Python编程把二叉树打印成多行代码
2018/01/04 Python
使用实现XlsxWriter创建Excel文件并编辑
2018/05/04 Python
Django中的Model操作表的实现
2018/07/24 Python
对pandas数据判断是否为NaN值的方法详解
2018/11/06 Python
解决python3 Pycharm上连接数据库时报错的问题
2018/12/03 Python
Python List cmp()知识点总结
2019/02/18 Python
Python数据类型之Number数字操作实例详解
2019/05/08 Python
Python3安装模块报错Microsoft Visual C++ 14.0 is required的解决方法
2020/07/28 Python
通过HTML5规范搞定i、em、b、strong元素的区别
2017/03/04 HTML / CSS
Melissa鞋马来西亚官方网站:MDreams马来西亚
2018/04/05 全球购物
Anya Hindmarch官网:奢侈设计师手袋及配饰
2018/11/15 全球购物
静心口服夜广告词
2014/03/20 职场文书
2014年销售人员工作总结
2014/11/27 职场文书
工程质量保证书
2015/05/09 职场文书
简单的辞职信模板
2015/05/12 职场文书
无故旷工检讨书
2015/08/15 职场文书
2016优秀青年志愿者事迹材料
2016/02/25 职场文书
Redis特殊数据类型bitmap位图
2022/06/01 Redis