zoukankan      html  css  js  c++  java
  • 面向对象案例——贪吃蛇游戏

    最近项目上线,近一个星期没更博了,今天来写一个经典的游戏案例——贪吃蛇。在这个简单的案例里可以体会javaScript 面向对象开发相关模式,学习使用面向对象的方式分析问题。

    1.功能实现

    1.1 搭建页面:放一个容器盛放游戏场景 div#map,设置样式

    <div class="map" id="map"></div>
     1 <style>
     2         #map{
     3             background-color: #000;
     4             width: 1500px;
     5             height: 700px;
     6             position: relative;
     7             left: 0;
     8             top: 0;
     9         }
    10 </style>

    1.2 分析对象:食物对象、蛇对象、游戏对象

    1.3 创建食物对象Food

    ⑴ 属性:位置(x,y)、大小(width、height)、颜色(color)

    1 // 创建Food的构造函数,并设置属性
    2 function Food(width,height,bgColor) {
    3     // 食物的宽度和高度(像素)
    4     this.width=width||10;
    5     this.height=height||10;
    6      // 食物的颜色
    7     this.bgColor=bgColor||"white";
    8 }

    ⑵ 方法:render() 随机创建一个食物对象,并输出到map上

     1 // 通过原型设置render方法,实现随机产生食物对象,并渲染到map上
     2 Food.prototype.render=function (map) {
     3     remove(map);  
     4     // 随机食物的位置,map.宽度/food.宽度,总共有多少分food的宽度,随机一下。然后再乘以food的宽度
     5     this.x=Math.floor(Math.random()*(map.offsetWidth/this.width))*this.width;
     6     this.y=Math.floor(Math.random()*(map.offsetHeight/this.height))*this.height;
     7     // 动态创建食物对应的div
     8     var newDiv=document.createElement("div");
     9     newDiv.style.position="absolute";
    10     newDiv.style.left=this.x+"px";
    11     newDiv.style.top=this.y+"px";
    12     newDiv.style.backgroundColor=this.bgColor;
    13     newDiv.style.width=this.width+"px";
    14     newDiv.style.height=this.height+"px";
    15     map.appendChild(newDiv);
    16     li.push(newDiv);
    17 }

    1.4 创建蛇对象Snake

    ⑴ 属性:大小(width、height)、颜色(color)、方向(direction)、身体数组对象(body)

     1 // Snake构造函数
     2 function Snake(width,height,bgColor,direction) {
     3     // 设置每一个蛇节的宽度
     4     this.width=width||10;
     5     this.height=height||10;
     6     this.bgColor=bgColor||"white";
     7     // 蛇的运动方向
     8     this.direction=direction||"right";
     9     // 蛇的每一部分, 第一部分是蛇头
    10     this.body=[
    11         {x:3,y:1},
    12         {x:2,y:1},
    13         {x:1,y:1}
    14     ];
    15 }

    ⑵ 方法:render() 把蛇渲染到map上

     1 // render方法,原理与渲染食物相同
     2 Snake.prototype.render=function (map) {
     3     remove(map);
     4     for (var i = 0; i < this.body.length; i++) {
     5         var newDiv=document.createElement("div");
     6         newDiv.style.position="absolute";
     7         newDiv.style.left=this.body[i].x*this.width+"px";
     8         newDiv.style.top=this.body[i].y*this.height+"px";
     9         newDiv.style.width=this.width+"px";
    10         newDiv.style.height=this.height+"px";
    11         newDiv.style.backgroundColor=this.bgColor;
    12         map.appendChild(newDiv);
    13         list.push(newDiv);
    14     }
    15 }

    1.5 创建游戏对象Game(用来管理游戏中的所有对象和开始游戏)

    ⑴ 属性:food、snake、map

    1 // Game构造函数
    2 function Game(map) { 
    3     this.map=map;
    4     this.snake=new Snake();
    5     this.food=new Food();
    6     that=this;
    7 }

    ⑵ 方法:start() 开始游戏(绘制所有游戏对象)

    1 //  开始游戏,渲染食物对象和蛇对象
    2 Game.prototype.startGame=function () { 
    3     this.food.render(this.map);
    4     this.snake.render(this.map);
    5     autoMove();
    6     keyBind();
    7 }
    // 在自调用函数中暴露Game对象
    window.Game=Game;

    2.游戏逻辑

    2.1 蛇的move方法

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

    ⑵ 让蛇移动起来,把蛇身体的每一部分往前移动一下

    ⑶ 蛇头部分根据不同的方向决定 往哪里移动

     1 Snake.prototype.move=function (food,map) {
     2     //  让蛇身体的每一部分往前移动一下
     3    for (var i = this.body.length-1; i >0; i-- ){
     4        this.body[i].x=this.body[i-1].x;
     5        this.body[i].y=this.body[i-1].y; 
     6    }
     7     //  根据移动的方向,决定蛇头如何处理
     8     switch (this.direction) {
     9         case "left":
    10             this.body[0].x--;
    11             break;
    12         case "right":
    13             this.body[0].x++;
    14             break;
    15         case "up":
    16             this.body[0].y--;
    17             break;
    18         case "down":
    19             this.body[0].y++;
    20             break;
    21         default:
    22             break;
    23     }
    24 }
    //在game中测试
    this.snake.move(this.food, this.map);
    this.snake.render(this.map);

    2.2 让蛇自己动起来

    ⑴ 在game.js中 添加autoMove的私有方法,开启定时器调用蛇的move和render方法,让蛇动起来(私有方法即不能被外部访问的方法,使用自调用函数包裹)

     1 function autoMove() { 
     2     var timeId=setInterval(function () {
     3         this.snake.move(this.food,this.map);
     4         this.snake.render(this.map);
     5         // 判断蛇是否撞墙
     6         var snakeHeadX=this.snake.body[0].x*this.snake.width;
     7         var snakeHeadY=this.snake.body[0].y*this.snake.width;
     8         if(snakeHeadX<0 || snakeHeadY<0 || snakeHeadX>=this.map.offsetWidth || snakeHeadY>=this.map.offsetHeight){
     9             clearInterval(timeId);
    10             alert("Game over!");
    11         }
    12     }.bind(that),50);
    13 }

    ⑵ 在snake中添加删除蛇的私有方法,在render中调用

    1 function remove(map) {
    2     for (var i = 0; i < list.length; i++) {
    3         map.removeChild(list[i]);
    4     }
    5     list.length=0;
    6 }

    ⑶ 在game中通过键盘控制蛇的移动方向

     1 function keyBind() { 
     2     window.onkeydown=function (e) { 
     3     e=e||window.event;
     4     e.keyCode= e.keyCode|| e.charCode|| e.which;            
     5     // console.log(e.keyCode);
     6     switch (e.keyCode) {
     7         case 37:
     8             if(this.snake.direction!="right"){
     9                 this.snake.direction="left";
    10             }
    11             break;
    12         case 38:
    13             if (this.snake.direction != "down") {
    14                 this.snake.direction = "up";
    15             }
    16             break; 
    17         case 39:
    18             if (this.snake.direction != "left") {
    19                 this.snake.direction = "right";
    20             }
    21             break; 
    22         case 40:
    23             if (this.snake.direction != "up") {
    24                 this.snake.direction = "down";
    25             }
    26             break;
    27         default:
    28             break;
    29         }
    30     }.bind(that);
    31 }

    ⑷ 在start方法中调用keyBind()

    2.3 判断蛇是否吃到食物

    在Snake的move方法中添加判断

     1 // 在移动的过程中判断蛇是否吃到食物
     2 var snakeHeadX=this.body[0].x*this.width;
     3 var snakeHeadY=this.body[0].y*this.height;
     4 var snakeTile=this.body[this.body.length-1];
     5 // 如果蛇头和食物的位置重合代表吃到食物    
     6 if(snakeHeadX==food.x && snakeHeadY==food.y){
     7 // 吃到食物,往蛇节的最后加一节
     8     this.body.push({
     9 // 食物的坐标是像素,蛇的坐标是几个宽度,进行转换            
    10         x:snakeTile.x,
    11         y:snakeTile.y
    12     });
    13 // 把现在的食物对象删除,并重新随机渲染一个食物对象
    14     food.render(map);        
    15 }

    ★ ★ 自调用函数的参数

    1 (function (window, undefined) {
    2   var document = window.document;
    3 }(window, undefined))

    ⑴ 传入window对象:代码压缩的时候,可以把function (window) 压缩成 function (w)

    ⑵ 传入undefined:把undefined作为函数的参数(当前案例没有使用) ,防止undefined 被重新赋值,因为在有的老版本的浏览器中 undefined可以被重新赋值

    ★ ★ 关于自调用函数的问题

    ⑴ 如果存在多个自调用函数要用分号分割,否则语法错误

     1 // 下面代码会报错
     2 (function () {
     3 }())
     4 
     5 (function () {
     6 }())
     7 // 所以代码规范中会建议在自调用函数之前加上分号
     8 // 下面代码没有问题
     9 ;(function () {
    10 }())
    11 
    12 ;(function () {
    13 }())

    ⑵ 当自调用函数前面有函数声明时,会把自调用函数作为参数

    1 // 所以建议自调用函数前,加上;
    2 var a = function () {
    3   alert('11');
    4 }
    5 
    6 (function () {
    7   alert('22');
    8 }())
  • 相关阅读:
    linux ipsec
    inotify+rsync
    多实例tomcat
    Http和Nginx反代至Tomcat(LNMT、LAMT)
    cisco ipsec
    ansible基础
    Qt 汉字乱码
    Model/View
    面对焦虑
    QT中QWidget、QDialog及QMainWindow的区别
  • 原文地址:https://www.cnblogs.com/linqb/p/9420750.html
Copyright © 2011-2022 走看看