IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 面向对象编程案例—贪吃蛇小游戏 -> 正文阅读

[游戏开发]面向对象编程案例—贪吃蛇小游戏

1. 搭建页面

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

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

2. 分析对象

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

3. 创建食物对象

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

  • x
  • y
  • width
  • height
  • color

(2)通过原型设置方法

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

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

4. 创建蛇对象

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

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

(2)通过原型设置方法

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

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

5. 创建游戏对象

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

  • food
  • snake
  • map

(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、bottom
    this.direction = "right";
    // 添加一个元素的数组,存储所有渲染的 div
    this.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) {
  // 定义一个全局变量,存储 this
  var 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 ---- 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;
      }
    };
  }
  // 封装一个私有函数,这个函数只能在模块内部进行调用,没必要暴露在外面
  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);
// 可以在自调用函数之前,强行加一个分号,意思是让前面的程序结束
// 自调用函数必须要有分号隔开,在前面也可,在后面也可
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 11:53:00  更:2021-08-08 11:53:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 18:42:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码