本文共 5792 字,大约阅读时间需要 19 分钟。
案例相关源码以上传到 GitHub :
在线演示地址:
游戏的目的是用来体会js高级语法的使用 不需要具备抽象对象的能力,使用面向对象的方式分析问题,需要一个漫长的过程。
放一个容器盛放游戏场景 div#map,设置样式
#map { width: 800px; height: 600px; background-color: #ccc; position: relative;}
Food
属性
方法
创建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';}
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; // 动态创建食物对应的div var 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 = Food;
Snake
属性
方法
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';}
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'; }}
window.Snake = Snake;
游戏对象,用来管理游戏中的所有对象和开始游戏
Game
属性
food
snake
map
方法
构造函数
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);}
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; }}
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);}
function remove() { // 删除渲染的蛇 var i = elements.length - 1; for(; i >= 0; i--) { // 删除页面上渲染的蛇 elements[i].parentNode.removeChild(elements[i]); // 删除elements数组中的元素 elements.splice(i, 1); }}
function bindKey() { document.addEventListener('keydown', function(e) { switch (e.keyCode) { case 37: // left this.snake.direction = 'left'; break; case 38: // top this.snake.direction = 'top'; break; case 39: // right this.snake.direction = 'right'; break; case 40: // bottom this.snake.direction = 'bottom'; break; } }.bind(that), false);}
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代码
(function (window, undefined) { var document = window.document;}(window, undefined))
将来代码压缩的时候,可以吧 function (window) 压缩成 function (w)
在将来会看到别人写的代码中会把undefined作为函数的参数(当前案例没有使用)
因为在有的老版本的浏览器中 undefined可以被重新赋值,防止undefined 被重新赋值现在的代码结构清晰,谁出问题就找到对应的js文件即可。
通过自调用函数,已经防止了变量命名污染的问题但是,由于js文件数较多,需要在页面上引用,会产生文件依赖的问题(先引入那个js,再引入哪个js)
将来通过工具把js文件合并并压缩。现在手工合并js文件演示// 如果存在多个自调用函数要用分号分割,否则语法错误// 下面代码会报错(function () { }())(function () { }())// 所以代码规范中会建议在自调用函数之前加上分号// 下面代码没有问题;(function () { }());(function () { }())
// 当自调用函数 前面有函数声明时,会把自调用函数作为参数// 所以建议自调用函数前,加上;var a = function () { alert('11');} (function () { alert('22');}())
转载地址:http://pixmi.baihongyu.com/