此代码存在一定的小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. [转帖]15 个必须知道的 chrome 开发工具技巧

    在Web开发者中,Google Chrome是使用最广泛的浏览器.六周一次的发布周期和一套强大的不断扩大开发功能,使其成为了web开发者必备的工具.你可能已经熟悉了它的部分功能,如使用console和 ...

  2. Windows设置一键安装Mysql数据库的方法

    Windows设置一键安装Mysql数据库的方法 前言 因为MySQL数据库的8126 65536 以及3072最大索引长度等问题 研发这边提交的补丁总是出现稀奇古怪的问题. mysql数据库又因为D ...

  3. elementui 的tabs组件出现蓝色边框问题

    elementui 的tabs组件出现蓝色边框问题 /deep/ .el-tabs__item:focus.is-active.is-focus:not(:active) { -webkit-box- ...

  4. 【解决一个小问题】golang 的 `-race`选项导致 unsafe代码 panic

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 为了提升性能,使用 unsafe 代码来重构了凯撒加密的代 ...

  5. chaincode中使用第三方库

    本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处. 在fabric的chaincode开发时,有时候需要用到第三方库提供的功能.这 ...

  6. Gin 框架之Cookie与Session

    目录 一.Cookie和Session的由来 二.Cookie简介 1. 什么是Cookie 2. Cookie规范 3. 安全性 4. Cookie 关键配置 三.Session简介 1. 什么是S ...

  7. py 学习(c++ to py)

    py1: print 2024-01-27 23:18:57 星期六 #这里是注释 # py1 : 基础print总结 ''' aaa 有时候也用三个单引号当注释 但其实是字符串 交互式会输出 ''' ...

  8. 若依、vue三级路由缓存失败

    router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { // 三级菜单组件无法缓存问题 if (t ...

  9. Exception message: CreateSymbolicLink error (1314): ???????????

    window下运行任务报错:Exception message: CreateSymbolicLink error (1314): ??????????? 报错信息如下: Diagnostics: E ...

  10. Java并发编程面试题

    Synchronized 用过吗,其原理是什么? Synchronized是jvm实现的一种互斥同步访问方式,底层是基于对象的监视器monitor实现的. 被synchronize修饰的代码在反编译后 ...