失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 面向对象编程案例—贪吃蛇小游戏

面向对象编程案例—贪吃蛇小游戏

时间:2021-05-13 12:22:34

相关推荐

面向对象编程案例—贪吃蛇小游戏

1. 搭建页面

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

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

2. 分析对象

游戏对象 Game蛇对象 Snake食物对象 Food

3. 创建食物对象

(1)创建 Food 的构造函数,并设置属性

xywidthheightcolor

(2)通过原型设置方法

render 随机创建一个食物对象,并输出到 map 上

(3)通过自调用函数,进行封装,通过 window 暴露 Food 对象

4. 创建蛇对象

(1)创建 Snake 的构造函数,并设置属性

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

(2)通过原型设置方法

render 随机创建一个蛇对象,并输出到 map 上

(3)通过自调用函数,进行封装,通过 window 暴露 Snake 对象

5. 创建游戏对象

(1)创建 Game 的构造函数,并设置属性

foodsnakemap

(2)通过原型设置方法

start 开始游戏(绘制所有游戏对象,渲染食物对象和蛇对象)

(3)通过自调用函数,进行封装,通过 window 暴露 Game 对象

6. 游戏逻辑1:写蛇的 move 方法

在蛇对象 (snake.js) 中,在 Snake 的原型上新增 move 方法

让蛇移动起来,把蛇身体的每一部分往前移动一下蛇头部分根据不同的方向决定 往哪里

7. 游戏逻辑2:让蛇自己动起来

在 snake 中添加删除蛇的私有方法,在 render 中调用在 game.js 中添加 runSnake 的私有方法,开启定时器调用蛇的 move 和 render 方法, 让蛇动起来判断蛇是否撞墙在 game.js 中添加 bindKey 的私有方法,通过键盘控制蛇的移动方向,在 start 方法中调 用 bindKey

8. 游戏逻辑3:判断蛇是否吃到食物

在 Snake 的 move 方法中判断在移动的过程中蛇是否吃到食物如果蛇头和食物的位置重合代表吃到食物食物的坐标是像素,蛇的坐标是几个宽度,需要进行转换吃到食物,往蛇节的最后加一节最后把现在的食物对象删除,并重新随机渲染一个食物对象

9. 其他处理1

把 html 中的 js 代码放到 index.js 中避免 html 中出现 js 代码

10. 其他处理2

自调用函数必须加分号

11. 其他处理3

自调用函数的参数

传入 window 对象:将来代码压缩的时候,可以吧 function (window) 压缩成 function (w)传入 undefined在将来会看到别人写的代码中会把 undefined 作为函数的参数(当前案例没有使用)因为在有的老版本的浏览器中 undefined 可以被重新赋值,防止 undefined 被重新赋值

代码

1. HTML 代码

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><link rel="stylesheet" href="css/index.css" /></head><body><div class="map" id="map"></div><script src="js/index.min.js"></script></body></html>

2. CSS 代码

* {margin: 0;padding: 0;}.map {position: relative;width: 800px;height: 800px;background-color: lightgray;}

3. JS 代码

// 将所有的模块代码要按照一定的顺序引入// 传入 形参window 和 实参window 的优势:// ①让 window 变成局部作用域就能访问// ②代码压缩时让字符串变短,在内部调用时节省数量// 传入 undefined 的原因:防止 undefined 被篡改,若 undefined 作为参数传入时,接收到的值就是 undefined,不会被恶意更改// ========================== Tools ================================(function (window, undefined) {// 制作一个工具对象,内部添加多种工具的方法var Tools = {// 获取一个范围内部的随机整数getRandom: function (min, max) {min = Math.ceil(min);max = Math.floor(max);return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值},// 获取随机颜色的方法getColor: function () {// rgb(r,g,b) 三个色值的颜色可以随机获取 0-255 之间的数字// 获取三个色值var r = this.getRandom(0, 255);var g = this.getRandom(0, 255);var b = this.getRandom(0, 255);// 返回一个 颜色值return "rgb(" + r + "," + g + "," + b + ")";},};window.Tools = Tools;})(window, undefined);// ================================= Food ========================================// 需要去缩小定义 构造函数的作用// 匿名函数,自调用函数,ITFE,关住作用域(function (window, undefined) {// 相对 Food render remove 的全局变量var ps = "absolute";// 创建 食物 的构造函数function Food(option) {// 避免传入的参数数据类型不对,或者没有传参option = option instanceof Object ? option : {};// 传入的数据可能是类似数组等对象,所以需要进一步判断this.width = option.width || 20;this.height = option.height || 20;this.x = option.x || 0;this.y = option.y || 0;this.color = option.color || "green";// 增加一个属性,存储将来这个对象渲染出来的所有 div 元素this.elements = [];}// 渲染一个元素到页面之上,需要添加到原型对象的方法中Food.prototype.render = function (map) {// 创建一个新的 div 元素var ele = document.createElement("div");// 每次设置样式之前,都随机获取一个 x 和 y 的值this.x = Tools.getRandom(0, map.clientWidth / this.width - 1) * this.width;this.y =Tools.getRandom(0, map.clientHeight / this.height - 1) * this.height;// 添加对应的样式ele.style.width = this.width + "px";ele.style.height = this.height + "px";ele.style.left = this.x + "px";ele.style.top = this.y + "px";ele.style.backgroundColor = this.color;ele.style.position = ps;// 让新元素添加到指定父级中map.appendChild(ele);// 将新元素添加到 数组中,方便后期调用删除this.elements.push(ele);};// 删除一个食物 div 元素Food.prototype.remove = function (map, i) {// 可以通过一些方法获取要被删除的食物的下标// 将元素从 html 结构中删除map.removeChild(this.elements[i]);// 将元素 从 数组中删除this.elements.splice(i, 1);};// 利用 window 对象暴露 Food 函数可以给外部使用window.Food = Food;})(window, undefined);// ================================= Snake =======================================// 使用自调用函数关住作用域(function (window, undefined) {// 全局变量var ps = "absolute";// 创建 蛇 的构造函数function Snake(option) {// 避免传入的参数数据类型不对,或者没有传参option = option instanceof Object ? option : {};// 给对象添加属性// 设置蛇节的宽度和高度属性值this.width = option.width || 20;this.height = option.height || 20;// 设置蛇身的数据this.body = [{x: 3, y: 2, color: "red" },{x: 2, y: 2, color: "blue" },{x: 1, y: 2, color: "blue" },];// 设置蛇移动的方向,还可以设置为left、top、bottomthis.direction = "right";// 添加一个元素的数组,存储所有渲染的 divthis.elements = [];}// 添加一个将元素渲染到页面上的方法Snake.prototype.render = function (map) {// 生成对应个数的 div 元素// 遍历数组for (var i = 0, len = this.body.length; i < len; i++) {// 根据数组的每一项的数据生成一个新的 div 元素var piece = this.body[i];// 创建新元素var ele = document.createElement("div");// 添加样式ele.style.width = this.width + "px";ele.style.height = this.height + "px";ele.style.left = piece.x * this.width + "px";ele.style.top = piece.y * this.height + "px";ele.style.position = ps;ele.style.backgroundColor = piece.color;// 渲染到指定的父级内部map.appendChild(ele);// 将添加的新元素存在数组中this.elements.push(ele);}};// 添加 蛇 运动的方法Snake.prototype.move = function () {// 蛇身的每一节都要变成上一节的位置// 循环需要从最后一项开始,为了避免前面的数据提取发生变化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;}};// 删除上一次渲染的蛇的所有 div 元素Snake.prototype.remove = function (map) {// 遍历数组删除所有元素// 将元素从 html 结构中删掉for (var i = this.elements.length - 1; i >= 0; i--) {map.removeChild(this.elements[i]);}// 数组也需要进行清空this.elements = [];};// 通过 window 暴露构造函数window.Snake = Snake;})(window, undefined);// ================================= Game ========================================// 自调用函数封闭作用域(function (window, undefined) {// 定义一个全局变量,存储 thisvar that;// 创建一个 游戏 的构造函数function Game(map) {// 设置三个属性,存储 食物、蛇、地图this.food = new Food();this.snake = new Snake();this.map = map;that = this;}// 添加一个游戏开始的方法,方法内初始化蛇和食物Game.prototype.start = function () {// 1. 添加蛇和食物 到地图上this.food.render(this.map);this.food.render(this.map);this.food.render(this.map);this.snake.render(this.map);// 2. 让游戏逻辑开始// 2.1 让蛇自动运动起来runSnake();// 2.2 通过上下左右箭头空中蛇的运动方向bindKey();};// 封装一个私有函数,控制上下左右按键更改的方向function bindKey() {// 给文档绑定键盘按下事件document.onkeydown = function (e) {// console.log(e.keyCode);// 键盘的编码// 37 ---- left// 38 ---- top// 39 ---- right// 40 ---- bottomswitch (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;}};}// 封装一个私有函数,这个函数只能在模块内部进行调用,没必要暴露在外面function runSnake() {// 开启一个定时器,让蛇连续运动起来var timer = setInterval(function () {// 定时器函数内部的 this 指向的是 window// 所以利用一个中间变量 that// 让蛇运动起来that.snake.move();// 删掉上一次的蛇that.snake.remove(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;// 每一次蛇走到新的位置,都要判断一下是否吃到食物了// 2.3 判断蛇头与食物是否碰撞,吃掉食物,让自己增加一节// 记录一下食物的坐标// var foodX = that.food.x;// var foodY = that.food.y;// 获取蛇头的具体坐标位置,px 值var hX = headX * that.snake.width;var hY = headY * that.snake.height;// 判断// 将食物的数组中每一个都要进行对比,谁被吃掉,删除自己,渲染一个新的元素for (var i = 0; i < that.food.elements.length; i++) {if (that.food.elements[i].offsetLeft === hX &&that.food.elements[i].offsetTop === hY) {// 吃到了食物// 让食物删除,然后渲染一个新的食物that.food.remove(that.map, i);that.food.render(that.map);// 添加一个新的蛇节var last = that.snake.body[that.snake.body.length - 1];that.snake.body.push({x: last.x,y: last.y,color: last.color,});}}// 每移动一次,都要判断是否出了地图,游戏是否结束// 2.4 判断是否超出地图范围,结束游戏// 进行判断if (headX < 0 || headX >= maxX || headY < 0 || headY >= maxY) {// 停止定时器clearInterval(timer);// 弹出提醒alert("Game over");}}, 150);}// 将构造函数通过 window 暴露window.Game = Game;})(window, undefined);// ================================= Main ========================================// 使用自调用函数关住作用域(function (window, undefined) {var map = document.getElementById("map");var game = new Game(map);game.start();})(window, undefined);// 可以在自调用函数之前,强行加一个分号,意思是让前面的程序结束// 自调用函数必须要有分号隔开,在前面也可,在后面也可

如果觉得《面向对象编程案例—贪吃蛇小游戏》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。