面向对象游戏案例:贪吃蛇

案例相关源码以上传到 GitHub :https://github.com/lipengzhou/new-snake

案例介绍

游戏演示

在线演示地址:贪吃蛇

案例目标

游戏的目的是用来体会js高级语法的使用 不需要具备抽象对象的能力,使用面向对象的方式分析问题,需要一个漫长的过程。

功能实现

搭建页面

放一个容器盛放游戏场景 div#map,设置样式

#map {width: 800px;height: 600px;background-color: #ccc;position: relative;
}

分析对象

  • 游戏对象
  • 蛇对象
  • 食物对象

创建食物对象

  • Food

    • 属性

      • x
      • y
      • width
      • height
      • color
    • 方法

      • render 随机创建一个食物对象,并输出到map上
  • 创建Food的构造函数,并设置属性

var position = 'absolute';
var elements = [];
function Food(x, y, width, height, color) {this.x = x || 0;this.y = y || 0;// 食物的宽度和高度(像素)this.width = width || 20;this.height = height || 20;// 食物的颜色this.color = color || 'green';
}
  • 通过原型设置render方法,实现随机产生食物对象,并渲染到map上
Food.prototype.render = function (map) {// 随机食物的位置,map.宽度/food.宽度,总共有多少分food的宽度,随机一下。然后再乘以food的宽度this.x = parseInt(Math.random() * map.offsetWidth / this.width) * this.width;this.y = parseInt(Math.random() * map.offsetHeight / this.height) * this.height;// 动态创建食物对应的divvar div = document.createElement('div');map.appendChild(div);div.style.position = position;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;elements.push(div);
}
  • 通过自调用函数,进行封装,通过window暴露Food对象
window.Food = Food;

创建蛇对象

  • Snake

  • 属性

    • width 蛇节的宽度 默认20
    • height 蛇节的高度 默认20
    • body 数组,蛇的头部和身体,第一个位置是蛇头
    • direction 蛇运动的方向 默认right 可以是 left top bottom
  • 方法

    • render 把蛇渲染到map上
  • Snake构造函数

var position = 'absolute';
var elements = [];
function Snake(width, height, direction) {// 设置每一个蛇节的宽度this.width = width || 20;this.height = height || 20;// 蛇的每一部分, 第一部分是蛇头this.body = [{x: 3, y: 2, color: 'red'},{x: 2, y: 2, color: 'red'},{x: 1, y: 2, color: 'red'}];this.direction = direction || 'right';
}
  • render方法
Snake.prototype.render = function(map) {for(var i = 0; i < this.body.length; i++) {var obj = this.body[i];var div = document.createElement('div');map.appendChild(div);div.style.left = obj.x * this.width + 'px';div.style.top = obj.y * this.height + 'px';div.style.position = position;div.style.backgroundColor = obj.color;div.style.width = this.width + 'px';div.style.height = this.height + 'px';}
}
  • 在自调用函数中暴露Snake对象
window.Snake = Snake;

创建游戏对象

游戏对象,用来管理游戏中的所有对象和开始游戏

  • Game

    • 属性

      • food

      • snake

      • map

    • 方法

      • start 开始游戏(绘制所有游戏对象)
  • 构造函数

function Game(map) {this.food = new Food();this.snake = new Snake();this.map = map;
}
  • 开始游戏,渲染食物对象和蛇对象
Game.prototype.start = function () {this.food.render(this.map);this.snake.render(this.map);
}

游戏的逻辑

写蛇的move方法

  • 在蛇对象(snake.js)中,在Snake的原型上新增move方法
  1. 让蛇移动起来,把蛇身体的每一部分往前移动一下
  2. 蛇头部分根据不同的方向决定 往哪里移动
Snake.prototype.move = function (food, map) {// 让蛇身体的每一部分往前移动一下var i = this.body.length - 1;for(; i > 0; i--) {this.body[i].x = this.body[i - 1].x;this.body[i].y = this.body[i - 1].y;}// 根据移动的方向,决定蛇头如何处理switch(this.direction) {case 'left': this.body[0].x -= 1;break;case 'right':this.body[0].x += 1;break;case 'top':this.body[0].y -= 1;break;case 'bottom':this.body[0].y += 1;break;}
}
  • 在game中测试
this.snake.move(this.food, this.map);
this.snake.render(this.map);

让蛇自己动起来

  • 私有方法

    什么是私有方法?不能被外部访问的方法
    如何创建私有方法?使用自调用函数包裹
    
  • 在game.js中 添加runSnake的私有方法,开启定时器调用蛇的move和render方法,让蛇动起来

  • 判断蛇是否撞墙

function runSnake() {var timerId = setInterval(function() {this.snake.move(this.food, this.map);// 在渲染前,删除之前的蛇this.snake.render(this.map);// 判断蛇是否撞墙var maxX = this.map.offsetWidth / this.snake.width;var maxY = this.map.offsetHeight / this.snake.height;var headX = this.snake.body[0].x;var headY = this.snake.body[0].y;if (headX < 0 || headX >= maxX) {clearInterval(timerId);alert('Game Over');}if (headY < 0 || headY >= maxY) {clearInterval(timerId);alert('Game Over');}}.bind(that), 150);
}
  • 在snake中添加删除蛇的私有方法,在render中调用
function remove() {// 删除渲染的蛇var i = elements.length - 1;for(; i >= 0; i--) {// 删除页面上渲染的蛇elements[i].parentNode.removeChild(elements[i]);// 删除elements数组中的元素elements.splice(i, 1);}
}
  • 在game中通过键盘控制蛇的移动方向
function bindKey() {document.addEventListener('keydown', function(e) {switch (e.keyCode) {case 37:// leftthis.snake.direction = 'left';break;case 38:// topthis.snake.direction = 'top';break;case 39:// rightthis.snake.direction = 'right';break;case 40:// bottomthis.snake.direction = 'bottom';break;}}.bind(that), false);
}
  • 在start方法中调用
bindKey();

判断蛇是否吃到食物

// 在Snake的move方法中// 在移动的过程中判断蛇是否吃到食物
// 如果蛇头和食物的位置重合代表吃到食物
// 食物的坐标是像素,蛇的坐标是几个宽度,进行转换
var headX = this.body[0].x * this.width;
var headY = this.body[0].y * this.height;
if (headX === food.x && headY === food.y) {// 吃到食物,往蛇节的最后加一节var last = this.body[this.body.length - 1];this.body.push({x: last.x,y: last.y,color: last.color})// 把现在的食物对象删除,并重新随机渲染一个食物对象food.render(map);
}

其它处理

把html中的js代码放到index.js中

避免html中出现js代码

自调用函数的参数

(function (window, undefined) {var document = window.document;}(window, undefined))
  • 传入window对象

将来代码压缩的时候,可以吧 function (window) 压缩成 function (w)

  • 传入undefined

在将来会看到别人写的代码中会把undefined作为函数的参数(当前案例没有使用)
因为在有的老版本的浏览器中 undefined可以被重新赋值,防止undefined 被重新赋值

整理代码

现在的代码结构清晰,谁出问题就找到对应的js文件即可。
通过自调用函数,已经防止了变量命名污染的问题

但是,由于js文件数较多,需要在页面上引用,会产生文件依赖的问题(先引入那个js,再引入哪个js)
将来通过工具把js文件合并并压缩。现在手工合并js文件演示

  • 问题1
// 如果存在多个自调用函数要用分号分割,否则语法错误
// 下面代码会报错
(function () {}())(function () {}())
// 所以代码规范中会建议在自调用函数之前加上分号
// 下面代码没有问题
;(function () {}());(function () {}())
  • 问题2
// 当自调用函数 前面有函数声明时,会把自调用函数作为参数
// 所以建议自调用函数前,加上;
var a = function () {alert('11');
}(function () {alert('22');
}())

Web前端学习笔记——JavaScript之面向对象游戏案例:贪吃蛇相关推荐

  1. Web前端学习笔记——JavaScript之WEBAPI、BOM、DOM及获取页面元素

    Web API介绍 API的概念 API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访 ...

  2. Web前端学习笔记——JavaScript之数组、函数、作用域

    数组 为什么要学习数组 之前学习的数据类型,只能存储一个值(比如:Number/String.我们想存储班级中所有学生的姓名,此时该如何存储? 数组的概念 所谓数组,就是将多个元素(通常是同一类型)按 ...

  3. Web前端学习笔记——AngularJS之豆瓣电影案例

    step-01 构建项目结构 克隆项目骨架 bash $ git clone --depth=1 https://github.com/Micua/angular-boilerplate.git mo ...

  4. web前端学习笔记(最新)

    web前端学习笔记 大家好,我是链表哥,新的学期,新的学习,我会为您展示我的学习进程. 一:什么是WEB前端? 所谓的Web前端指的是用户所能接触到的,并服务于用户的前沿端口,经我们程序员编辑修饰后展 ...

  5. 零基础web前端学习之JavaScript 和css 阻塞

    web前端学习之JavaScript 和css 阻塞,JavaScript 是客户端和服务器端的脚本语言,可以插入HTML 页函中, 并且是目前较热门的Web 开发语言.同时, JavaScript ...

  6. web前端学习笔记之JavaScript

    文章目录 1 JavaScript简介 2 JS基础 3 JS代码编写位置 3.1 行内式 3.2 内嵌式 3.3 外联式 4 基本语法 5 字面量和变量 5.1 字面量 5.2 变量 6 标识符 7 ...

  7. 前端学习笔记——JavaScript进阶

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.JavaScript 面向对象 1. 面向对象编程介绍 1.1 两大编程思想 1.2 面向过程编程 1.3 面向对 ...

  8. Web前端学习笔记(1)

    文章目录 一.第一部分 1.1内容一 Web前端导入 二.第二部分 2.1内容一 HTML和CSS的定义 三.第三部分 3.1内容一 宇宙第一编译器Vs Code 3.2内容二 快捷键的使用 四.第四 ...

  9. 重学前端-学习笔记-JavaScript对象

    说明 重学前端是程劭非(winter)在极客时间开的一个专栏,在此主要整理我的学习笔记.如有侵权,请联系我,谢谢. javascript对象特征 对象具有唯一标识性:完全相同的两个对象,也不是同一个对 ...

最新文章

  1. 2021-07-27查看图像像素值类别
  2. MySql按周,按月,按日分组统计数据
  3. (六)观察者模式详解(包含观察者模式JDK的漏洞以及事件驱动模型)决了当时的问题,那时LZ接触JAVA刚几个月,比葫芦画瓢的用了观察者模式。...
  4. matlab中如何求零极点,Matlab中绘制零极点
  5. python实现logistic_用Python实现机器学习算法—Logistic 回归算法
  6. cmd查看python安装路径-从cmd如何查找python的安装路径?
  7. java 打印一棵树_java编程题之从上往下打印出二叉树
  8. SQL_CALC_FOUND_ROWS的用法
  9. el-table 树形表格 自定义展开图标_实践一个树形组件
  10. uva 674 Coin Change 换钱币【完全背包】
  11. 社区团购还是两看,从消极这个角度
  12. 常用Docker 镜像命令(二)
  13. [ACNOI2021]OEIS yyds
  14. mac制作ubuntu 18.04 U盘启动盘
  15. excel粘贴时出现故障_你必将碰到的巨崩溃的问题,Excel复制粘贴时,突然提示“不同单元格格式太多“,我快疯了......
  16. 亚马逊Dash永久下架:智能购物按钮究竟犯了什么错?
  17. js中将从ajax获得的时间戳数字串转换成理解的时间格式
  18. 官方解释:Windows Vista和OpenGL
  19. Java数组实现:一群人围成一圈从123报数 如果报到3就退出该圈中 直到最后一个人留下来!问其位置
  20. 计算机网络自顶向下方法笔记02

热门文章

  1. python 数字运算及格式化_Python基础教程(3)Python数据类型、运算与格式化
  2. 文件被占用无法删除,解决办法
  3. [css] css 3d 动画,跟随鼠标移动做球形旋转
  4. 数据传输性能与安全不能兼顾?Rambus安全方案“动静”两相宜
  5. html框架自动居中,html 宽度固定并布局居中模板框架
  6. 解决ubuntu18.04打不开网易云音乐(亲测可行!
  7. android开发中磁场传感器,Android开发获取传感器数据的方法示例【加速度传感器,磁场传感器,光线传感器,方向传感器】...
  8. 红米手机5获取Root超级权限的步骤
  9. win10配置Sublime Text 3作为latex的编辑器
  10. Qgis的下载安装(Qgis3.16.12)