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

之前有关JavaScript相关的内容我们已经更新了:


本篇内容JavaScript面向对象游戏案例:贪吃蛇

一、案例介绍

1.1 游戏演示

演示:贪吃蛇

1.2 案例目标

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

1.3 功能实现

1.4 搭建页面

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

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

1.5 分析对象

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

1.6 创建食物对象

  • 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;

  // 动态创建食物对应的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对象
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: 'blue'},
    {x: 1, y: 2, color: 'blue'}
  ];
  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);
}

四、游戏的逻辑

4.1 写蛇的move方法

  • 在蛇对象(snake.js)中,在Snake的原型上新增move方法
  • 让蛇移动起来,把蛇身体的每一部分往前移动一下
  • 蛇头部分根据不同的方向决定 往哪里移动
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);

4.2 让蛇自己动起来

  • 私有方法
    什么是私有方法? 不能被外部访问的方法 如何创建私有方法? 使用自调用函数包裹 - 在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.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);
}
  • 在game中通过键盘控制蛇的移动方向
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);
}
  • 在start方法中调用
bindKey();

4.3 判断蛇是否吃到食物

// 在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);
}

五、其它处理

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

避免html中出现js代码

5.2 自调用函数的参数

(function (window, undefined) {
  var document = window.document;

}(window, undefined))
  • 传入window对象

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

  • 传入undefined

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

5.3 整理代码

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

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

  • 问题1
// 如果存在多个自调用函数要用分号分割,否则语法错误
// 下面代码会报错
(function () {
}())

(function () {
}())
// 所以代码规范中会建议在自调用函数之前加上分号
// 下面代码没有问题
;(function () {
}())

;(function () {
}())
  • 问题2
// 当自调用函数 前面有函数声明时,会把自调用函数作为参数
// 所以建议自调用函数前,加上;
var a = function () {
  alert('11');
}

(function () {
  alert('22');
}())



更多HTML+CSS学习笔记如下(已完结):

HTML入门级

CSS入门总结CSS之emmet语法CSS的复合选择器
CSS的显示模式CSS背景CSS三大特性CSS盒子模型及PS基础
CSS之圆角CSS之浮动知识点汇总CSS前端基础了解PS切图CSS属性书写顺序(重点)
CSS练手之学成在线页面制作CSS定位的4种分类CSS综合案例:学成在线模块添加HTML+CSS之定位(position)的应用
HTML+CSS案例:淘宝轮播图CSS之元素的显示与隐藏HTML+CSS综合案例:土豆网鼠标经过显示遮罩【重点】CSS之精灵图
CSS字体图标CSS三角以及京东三角案例CSS用户界面样式源码CSS之vertical-align 属性应用
CSS:溢出的文字省略号显示CSS常见布局技巧+案例面试的时候你能说出几个HTML5的新特性?已完结

 前端自学内容尽在黑马前端专栏哦,关注再学,好方便~

2022年前端学习路线图:课程、源码、笔记,技术栈
欢迎小伙伴们留言哦,期待看到大家的进步。另外此线路图实时更新!需要课后资料的友友们,可以直接告诉我。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值