此代码存在一定的小bug,当蛇出边界之后存在一定的小问题

分析贪吃蛇功能需求:

1.食物

(1)每次生成一个,位置随意但不可超出规定范围

(2)每次蛇吃到食物之后,前一个食物消失同时新的食物又生成

(3)属性:大小、颜色、位置

2.蛇

(1)属性:长度,颜色,位置、方向

(2)每吃掉一个食物,蛇尾新增一节,默认方向向右

3.游戏对象

管理食物对象和蛇对象的生成和逻辑

良好的代码书写习惯及注意:

                                 使用(function(){})(),开启自调用函数,启动新的局部作用域,防止命名冲突

在防止命名冲突的同时,为了使外界可以同时访问到自调用函数中的所有对象,使用Window.函数名的方式,将其悬挂到window对象上

理解此代码需要提前了解原型链的构成

在index文件中注意引用文件顺序提前用的需要先引用

解决随机生成的问题:                       

                                    //使用字面量的方式创建对象,给对象的属性添加了一个 方法
                                      (function(){
                                       var Tools = {
                                                   getRandom:function(min,max){
                                                   //Math.random生成 [0,1)的随机数,Math.floor向下取整
                                                   //此时需要取到[min,max]的值,例如取[2,5],所以[0,1)*(5-2+1)+2=[2,6)即[2,5]
                                                   return Math.floor(Math.random()*(max-min+1)+min);
                                                   }
                                      }
                                     window.Tools = Tools;
                                })()
食物:
(function(){
//记录上一次创建的食物,为删除做准备
var elements=[];
function Food(options){
//new一个对象时如果没有传参数,此时options为空,后面的语句就会出错所以需要给options一个默认值
options = options || {};
this.x = options.x || 0;
this.y = options.y || 0;
this.width = options.width || 20;
this.height = options.height || 20;
this.color = options.color || 'pink';
}
//应该把食物传到父容器上
Food.prototype.render = function(map){
//删除之前创建的食物
remove();
this.x = Tools.getRandom(0,map.offsetWidth/this.width-1)*this.width;
this.y = Tools.getRandom(0,map.offsetHeight/this.height-1)*this.height;
//动态创建div,也就是食物
var div = document.createElement('div');
map.appendChild(div);
 
elements.push(div);
//设置div的样式
div.style.position = 'absolute';
div.style.left = this.x + 'px';
div.style.top = this.y + 'px';
div.style.width = this.width + 'px';
div.style.height = this.height + 'px';
div.style.backgroundColor = this.color;
 
}
function remove(){
//遍历删除数组中的元素
for(var i=elements.length-1;i>=0;i--){
//找到父元素移除其子元素,删除div
elements[i].parentNode.removeChild(elements[i]);
//删除数组中的元素
elements.splice(i,1);
}
}
//外部无法直接访问测试代码,利用window全局对象访问
// var food = new Food();
// var map = document.getElementById('map');
// food.render(map);
window.Food = Food;
})()
//var food = new Food();
//var map = document.getElementById('map');
//food.render(map);
蛇:
(function(){
//面向对象过程中用变量来存储内容,便于后续的维护开发
var position = 'absolute';
//记录之前创建的蛇
var elements = [];
function Snake(options){
options = options || {};
//设置蛇节的大小
this.width = options.width || 20;
this.height = options.height || 20;
//设置蛇节的方向,默认往右移动
this.direction = options.direction || 'right';
//设置蛇的身体,默认有三个块
this.body = [
//第一个蛇头的位置x:3左边距离有三个方块,y:2上面距离有两个方块
{x:3,y:2,color:'lightgreen'},
{x:2,y:2,color:'white'},
{x:1,y:2,color:'white'}
];
}
//将蛇渲染到地图上
Snake.prototype.render = function(map){
//每次render渲染的时候都应该先移除掉之前创建的蛇
remove();
//要把蛇的每一个部分都渲染到地图上
//i<len比i<length的好处是,每循环一次this.body.length都会计算一次,但是如果在var中定义就只会执行一次,这样会提高效率
for(var i=0, len = this.body.length;i<len;i++){
var obj = this.body[i];
var div = document.createElement('div');
map.appendChild(div);
//记录当前的蛇
elements.push(div);
//设置样式
div.style.position = position;
div.style.width = this.width + 'px';
div.style.height = this.height + 'px';
//个数乘边距离,就是位置
div.style.left = obj.x*this.width +'px';
div.style.top = obj.y*this.height + 'px';
div.style.backgroundColor = obj.color;
}
}
function remove(){
for(var i=elements.length-1;i>=0;i--){
//删除div
//删除数组中的元素
elements[i].parentNode.removeChild(elements[i]);
elements.splice(i,1);
}
}
//控制蛇移动的方法
Snake.prototype.move = function(food,map){
 
//控制蛇的身体移动,每次移动当前蛇节走到上一个蛇节的位置
//只取身体部分不取头部
for(var i=this.body.length-1;i>0;i--){
this.body[i].x = this.body[i-1].x;
this.body[i].y = this.body[i-1].y;
}
//控制蛇头的移动
//判断蛇移动的方向
var head = this.body[0];
switch(this.direction){
case 'right':
head.x +=1;
break;
case 'left':
head.x -=1;
break;
case 'top':
head.y -=1;
break;
case 'bottom':
head.y +=1;
break;
}
//判断食物的坐标是否和蛇头坐标重合
if(food.x === head.x*this.width && food.y === head.y*this.height){
//让蛇身体增加一格
//获取蛇的最后一节
var last = this.body[this.body.length-1];
//将最后一节的属性作为新属性赋值给新的一节
this.body.push({
x:last.x,
y:last.y,
color:last.color
})
//随机在地图上重新生成食物
food.render(map);
}
}
 
//测试:先将Sanke挂到Window对象上,便于外部测试访问
window.Snake = Snake;
})()

 游戏对象: 

(function(){
//记录游戏对象,避免定时器this指向错误
var that;
function Game(map){
this.food = new Food();
this.snake = new Snake();
this.map = map;
that = this;
}
Game.prototype.start = function (){
//把食物和蛇渲染到地图
this.food.render(this.map);
this.snake.render(this.map);
//测试move方法
// this.snake.move();
// this.snake.render(this.map);
// this.snake.move();
// this.snake.render(this.map);
 
//开始游戏逻辑:让蛇移动起来;用键盘控制蛇移动的方向;当蛇遇到食物做相应的处理;当蛇遇到边界,游戏结束
runSnake();
controlSnake();
}
//通过键盘控制蛇的移动
function controlSnake(){
document.addEventListener('keydown',function(e){
//console.log(e.keyCode);
//输出按下键盘方向的键盘码:37-left,38-top,39-right,40-bottom
switch(e.keyCode){
case 37:
that.snake.direction = 'left';
break;
case 38:
that.snake.direction = 'top';
break;
case 39:
that.snake.direction = 'right';
break;
case 40:
that.snake.direction = 'bottom';
break;
}
},false);
}
//开启定时器让蛇移动,用私有函数而不是原型,因为不需要外部访问
function runSnake(){
var timerId = setInterval(function (){
//让蛇走一格,在定时器中this指向window,没法用this.sanke
that.snake.move(that.food,that.map);
that.snake.render(that.map);
//蛇遇到边界游戏结束
//获取蛇头的坐标
var maxX = that.map.offsetWidth / that.snake.width;
var maxY = that.map.offsetHeight / that.snake.height;
var headX = that.snake.body[0].x;
var headY = that.snake.body[0].y;
//此时有个小问题,alert之后蛇头仍然会超出边界,这是渲染问题
if(headX < 0 || headX >= maxX){
alert('Game Over');
clearInterval(timerId);
timerId = null;
}
if(headY < 0 || headY >= maxY){
alert('Game Over');
clearInterval(timerId);
timerId = null;
}
},300);
}
 
window.Game = Game;
})()

测试:        

(function(){
//测试代码
var map = document.getElementById('map');
var game = new Game(map);
game.start();
})()

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>贪吃蛇</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="map"></div>
<script src="js/tool.js"></script>
<script src="js/food.js"></script>
<script src="js/snake.js"></script>
<script src="js/game.js"></script>
<script src="js/main.js"></script>
</body>
</html>
 

js之以面向对象的形式书写贪吃蛇的更多相关文章

  1. JavaScript面向对象编程小游戏---贪吃蛇

    1 面向对象编程思想在程序项目中有着非常明显的优势: 1- 1 代码可读性高.由于继承的存在,即使改变需求,那么维护也只是在局部模块 1- 2 维护非常方便并且成本较低. ​ 2 这个demo是采用了 ...

  2. C语言用面向对象的思想写贪吃蛇

    大概一年前这时候,接触C语言一个月,那时候知之甚少,对面向对象只觉”可远观而不可亵玩“,而且会看到很多言论说C语言就是面向过程的语言,C++就是面向对象的语言.不过,不记得什么时候在网上看到过一篇博文 ...

  3. 一个原生JS实现的不太成熟的贪吃蛇游戏

    一个初初初初级前端民工 主要是记录一下写过的东西,复习用 大佬们如果看到代码哪里不符合规范,或者有更好写法的,欢迎各位批评指正 十分感谢 实现一个贪吃蛇游戏需要几步? 1.有地图 2.有蛇 3.有食物 ...

  4. JS仿贪吃蛇:一串跟着鼠标的Div

    贪吃蛇是一款80后.90后比较熟悉的经典游戏,下面通过简单的JS代码来实现低仿版贪吃蛇效果:随着鼠标的移动,在页面中呈现所有Div块跟随鼠标依次移动,效果如下图所示. <!DOCTYPE htm ...

  5. 前端笔记之JavaScript面向对象(三)初识ES6&underscore.js&EChart.js&设计模式&贪吃蛇开发

    一.ES6语法 ES6中对数组新增了几个函数:map().filter().reduce() ES5新增的forEach(). 都是一些语法糖. 1.1 forEach()遍历数组 forEach() ...

  6. JS高级---面向对象的编程思想(贪吃蛇梳理)

    面向对象的编程思想(贪吃蛇梳理) 模拟贪吃蛇游戏,做的项目 地图: 宽,高,背景颜色,因为小蛇和食物都是相对于地图显示的, 这里小蛇和食物都是地图的子元素, 随机位置显示, 脱离文档流的, 地图也需要 ...

  7. Javascript基础示例:用JS写简易版贪吃蛇(面向对象)

    废话不多说,代码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> & ...

  8. js基础之面向对象

    一.基本概念 Array类 ————> 不具备实际的功能,只能用来构造对象 arr对象  ————> 有实际的功能,被类给构造出来 如:var arr=new Array(); proto ...

  9. html5面向对象做一个贪吃蛇小游戏

    canvas加面向对象方式的贪吃蛇 2016-08-25 这个小游戏可以增加对面向对象的理解,可以加强js逻辑能力,总之认真自己敲一两遍收获还是不少啊!!适合刚学canvas的同学练习!! 废话不多说 ...

  10. JS小游戏:贪吃蛇(附源码)

    javascript小游戏:贪吃蛇 此小游戏采用的是面向对象的思想,将蛇,食物,和游戏引擎分为3个对象来写的. 为方便下载,我把js写在了html中, 源码中暂时没有注释,等有空我在添加点注释吧. 游 ...

随机推荐

  1. 根目录被赋予777 -R权限后的处理过程

    解决某研发手残导致的系统宕机问题的处理过程 背景 2022.8.8 公司一台服务器出现了宕机的现象: 所有的人都无法远程, 都提示密码错误. 但是网络还是通的. 2022.8.12 出差前一天去了一趟 ...

  2. 从零开始配置vim(28)——代码的编译、运行与调试

    在前面几个章节,我们逐渐为 Vim 配置了语法高亮.代码的跳转和自动补全功能.现在的 Vim 已经可以作为代码编辑器来使用了.但是想将它作为日常发开的主力编辑器来用还需要很长一段路要走,其中一个就是要 ...

  3. vim 从嫌弃到依赖(22)——自动补全

    这篇文章我们将讨论 vim 自带的自动补全功能.当然,针对自动补全功能有许多好用的插件,但是了解vim自带的功能有助于我们更好的用来插件的补全功能.因为我见过有的配置文件将插件的功能配置的比原有的更难 ...

  4. C++ LibCurl实现Web隐藏目录扫描

    LibCurl是一个开源的免费的多协议数据传输开源库,该框架具备跨平台性,开源免费,并提供了包括HTTP.FTP.SMTP.POP3等协议的功能,使用libcurl可以方便地进行网络数据传输操作,如发 ...

  5. 19.6 Boost Asio 文本压缩传输

    Base64是一种二进制到文本的编码方案,用于将二进制数据转换为ASCII字符串格式.它通过将二进制数据流转换为一系列64个字符来工作,这些字符都可以安全地传输到设计用于处理文本数据的系统中. 如下代 ...

  6. 使用window.print进行前端打印,批量打印,设置分页,ie、火狐下设置页眉页脚

    window.print() print() 方法用于打印当前窗口的内容.谷歌调用 print() 方法会产生一个打印预览弹框,让用户可以设置打印请求. 但谷歌貌似不能自定义设置页眉页脚的文字:ie和 ...

  7. element-ui表格筛选,根据表头属性显示隐藏列

    效果: 步骤: 1.标签上添加要过滤的源数组 <el-table-column label="标签" :filters="filterList" filt ...

  8. ch583/ch582/ch573/ch571 central(主机)程序

    本程序是在CH582m上运行的, 一.主从连接 主机这里可以根据从机的MAC地址进行连接.static uint8_t PeerAddrDef[B_ADDR_LEN] = {0x02, 0x02, 0 ...

  9. .NET 大数据实时计算--学习笔记

    摘要 纯 .Net 自研大数据实时计算平台,在中通快递服务数百亿包裹,处理数据万亿计!将分享大数据如何落地以及设计思路,技术重难点. 目录 背景介绍 计算平台架构 项目实战 背景介绍 计算平台架构 分 ...

  10. Markdown:简洁高效的文本标记语言

    引言 在当今信息爆炸的时代,我们需要一种简洁.高效的文本标记语言来排版和发布内容.Markdown应运而生,它是一种轻量级的文本标记语言,以其简单易学.易读易写的特点,成为了广大写作者的首选工具.本文 ...