HTML5学习+javascript学习:打飞机游戏简介以及Model层

本着好记性不如烂博客以及分享成功的喜悦和分享失败的苦楚,今天我来分享下一个练手项目:打飞机游戏~从小就自己想做游戏,可是一直没有机会。HTML5给了我们这个平台,这个平台可以有很多以前想都不敢想的东西存在,让我们怀着感激与敬畏的心情,开始我们HTML5的旅程。

练手:打飞机游戏---思路。

1 需求:

这个不多说了,玩儿过雷电的同学都知道,有己机,敌机,还有子弹,当然BOSS也算但是BOSS也是敌机了。

2 分析:

2.1 开发工具: 采用HTML5+JS来完成,JS面向对象虽然有点复杂,但是很好用。采用Function作为对象的载体,返回的是Object(后面会详细讲到,这里只是先说说开发工具),采用HTML5的canvas对象作为表现层。

2.2 游戏原理:大家都喜欢打灰机吧,可是谁知道打灰机是怎么实现的呢?且容我抽丝剥茧,宽衣解带,沐浴更衣,慢慢道来~ 大体的思路是,图片一帧一帧地更新自己在canvas上的位置,利用人眼的缓存实现动画的效果。在更新图片之前更新坐标,在更新坐标之前更新状态。

2.3 游戏架构: 采用mvc模式,这种模式应该大家都不陌生,Model层就是在屏幕上显示的元素它的实体类,实体类有自己的方法包括更新自己的坐标,发生碰撞时的反应等,它也有自己的私有变量,比如坐标,图片,爆炸效果图片,碰撞体积,等等。view层就是显示层,它只根据返回的图片更新到画布上与业务无关。Service层,Control层与Model层之间的一层,它拥有所有要在屏幕上显示的实体(除了背景)的引用,处理Model层返回的事件,并缓存View层上要显示的内容,经过处理后返回要更新的图片信息给Control层。Control层,它会处理游戏中的事件,连接view层,并且会更新游戏的进度。它调用service,得到返回结果更新view层。

思路介绍完了,下面将介绍打灰机的具体实现了。

3.Model层

从己机,敌机,子弹中提取它们的共同点:它们都可以飞,,,,因此我把它们的父类叫fly,然后竭尽脑汁去想它们其他的共同点:它们都可以爆炸,它们会发生碰撞,它们都会移动,它们都可能会超出边界。目前我想到的就这么多了。但是它们的私有变量却都是大同小异的。它们都有一个hp属性代表血值,有个x代表x坐标,有个y代表y坐标,有个图片的引用表示要在屏幕上显示的图片,有个图片的引用表示要显示爆炸的图片,有个目标表示自己的愿景:是要消灭向上飞的呢还是要消灭向下飞的还是除了自己要消灭一切,不管怎么说得有个愿景。因此,把这个类抽象出来作为所有要表现在屏幕上的飞行物的父类:fly。我们看下它的继承关系(由于没太多接触过类图,画的粗糙,大家对付着过吧,聊胜于无。)右边的是属性和方法,属性是一个javascript中的Object

 

这里我们先给出fly和bullet的代码,其他的就自己扩展

fly.js:

  1 /*飞行物类,不管敌机,boss,己机,还是子弹,都是飞行物,它们都有改变位置,碰撞,越界判定的方法,也都有碰撞体积,图像,坐标,序号和hp等属性。具体参数如下:
  2 *var spec={
  3     x:1,画布中的横坐标
  4     y:CANVAS_HEIGHT-5,//画布中的纵坐标
  5     hp:1,//飞行物的血量
  6     index:svc.total(),//序列号
  7     exploreImg:getImg("img/blasts3.png"),//爆炸的图片
  8     img:getImg("img/mybullet2.png"),//自身图片
  9     target:0,//目标
 10     conflictSquare:20,//碰撞体积
 11     speedX:5,//X方向的速度
 12     speedY:2,//Y方向的速度
 13     movex:0,//X轴移动的方向
 14     movey:0//Y轴移动的方向
 15     };
 16 *这个对象完全可以用json来代替!可扩展性就不多说了。
 17 *它大多数方法(除了对私有变量访问的方法)均返回数组格式为:[{func:'',params:[]},{func:'',params:[]}]看到了什么?很像json吧?不过不是用json来调用的,
 18 *它是在控制层(service)接收,然后把有用的信息注册到一个array中来管理,根据func这个字段的不同,调用不同的方法。对象有index,删除的时候就可以很方便的删掉了。
 19 *整体来说,这个fly类是所有用到的模型的父类,
 20 *这种以方法(function)来构建对象的形式是安全地,因为它返回的是有很多属性的对象(return{a:function(){},b:function(){},,,,};),对象的所有内容均可访问其私有变量,
 21 *但是脚本不能在外部访问其自身私有变量。这对于透明性和扩展性都是有好处的。
 22 */
 23 var fly=function(spec){
 24     
 25     var imgWidth=spec.img.width;//图片的宽度,该变量是私有变量,当fly这个函数完蛋了,它的私有变量仍然可以访问,神奇吧,这就是javascript的魅力。
 26         var imgHeight=spec.img.height;//图片的高度
 27     return {
 28     move:function(directionX,directionY){//根据方向来移动0,1表示Y轴向下。1,0表示X轴向右,依次类推
 29         
 30     
 31         spec.x=(directionX||0)*spec.speedX+spec.x;//水平移动,任何移动的类型,只要不要太变态,应该都能调用这个方法来改变坐标,如果有不能调用的再说。
 32             
 33         spec.y=(directionY||0)*spec.speedY+spec.y;//垂直移动,其他同上。
 34 
 35         },
 36     hpChange:function(newHp){//碰撞了,血量改变了或者飞机挂掉了就要用到这个方法改变私有变量的值
 37         spec.hp=newHp;
 38     },
 39     x:function(){//得到画布中的x坐标
 40         return spec.x;
 41     },
 42     y:function(){//得到画布中的y坐标
 43         return spec.y;
 44     },
 45     hp:function(){//血量,访问私有属性血量要用到这个方法
 46         return spec.hp;
 47     },
 48     index:function(){//这个序列属性是用来删除自己的,使用array的splice方法,然后我们再做些手脚,就可以让index和array的序列相同啦。
 49         return spec.index;
 50     },
 51     setIndex:function(ndex){//这个方法用来改变自身的序列
 52         spec.index=ndex;
 53     },
 54     exploreImg:function(){//得到爆炸的图片,如果爆炸事件发生,图片被缓存,之后与其他内容一起画到画布上去。
 55         return spec.exploreImg;
 56     },
 57     img:function(){//得到自身图片,之后在控制层(service)缓存
 58         return spec.img;
 59     },
 60     target:function(){//得到敌对目标的编号,敌飞行物和友飞行物当然不能一样了。
 61         return spec.target;
 62     },
 63     width:function(){//图片的宽度
 64         return spec.img.width;
 65     },
 66     height:function(){//图片的高度
 67         return spec.img.height;
 68     },
 69     cflctSqr:function(){//碰撞体积(是一个正方形,这个方法得到碰撞体积的边长)
 70         if(spec.conflictSquare){
 71             return spec.conflictSquare;
 72         }
 73         return 0;
 74     },
 75     onConflict:function(other){//事件,后台判断碰撞,当碰撞发生时就调用这个方法,返回一个事件交给控制层处理。
 76         //alert(spec.hp+"  "+other.hp());
 77         if((spec.hp>0) && (other.hp()>0)){//如果碰撞接收者和碰撞发出者的hp属性都大于零,才能进行碰撞,碰撞的结果就是它们的血值相减
 78             //这又造成了有一个血值变为了零或负,或者两者同归于尽
 79             //dispear 这个事件代表消失,即如果一个飞行物被干掉了,我就让后台来处理这个被干掉的家伙的后事,把它从生龙活虎的队列中删除~
 80             //disapear函数可以接受多个参数,这个参数是一个数组,数组包含多个消失的对象(当然一般都是只会消失一个对象)。
 81             
 82             var hpo=other.hp();//hpo就是传入的参数的hp,两个飞行物碰撞,就假设一个是主动者,这样好分析点。
 83             var hps=spec.hp;//hps是被碰的hp
 84             spec.hp -= hpo;//被碰的hp减去碰撞的hp
 85             other.hpChange(other.hp()-hps);//碰撞的hp再减去被碰的hp这两件事造成了如下的代码来判断碰撞双方的状态
 86             var returnArray=[];//返回一个事件的集合先给他初始化为一个数组
 87             //alert(spec.hp+" "+other.hp());
 88             if(spec.hp == 0 && other.hp() == 0){//如果同归于尽,两者都爆炸,两者都消失
 89             returnArray=[];
 90             returnArray.push({func:"explore",params:[other.exploreImg(),other.x(),other.y()]});//碰撞发起的爆炸事件
 91             returnArray.push({func:"explore",params:[spec.exploreImg,spec.x,spec.y]});//碰撞接受者的爆炸事件
 92             returnArray.push({func:"disapear",params:[spec.index]});//碰撞接收者的消失事件入栈
 93             returnArray.push({func:"disapear",params:[other.index()]});//碰撞发起者的消失事件入栈
 94             
 95             
 96             return returnArray;
 97             
 98             
 99             }else if((spec.hp <=0) && (other.hp()>0)){//如果碰撞发起者它的hp远比接受者要大,那么接收者爆炸,消失
100             returnArray=[];
101             returnArray.push({func:"explore",params:[spec.exploreImg,spec.x,spec.y]});
102             returnArray.push({func:"disapear",params:[spec.index]});
103             return returnArray;
104 
105             }else if((spec.hp > 0) && (other.hp()<=0)){//如果碰撞发起者它的hp小于接受者,那么碰撞发起者爆炸,消失。
106             returnArray=[];
107             returnArray.push({func:"explore",params:[other.exploreImg(),other.x(),other.y()]});
108             returnArray.push({func:"disapear",params:[other.index()]});
109             return returnArray;
110             }else{//这种情况我不知道为什么可以发生,因此不打算处理这种情况,但为了保险起见返回一个undefined。
111             return ;    
112             }
113         }
114         
115     },
116     judgeBundle:function(){//超出边界把它揪回来。
117         
118         if(spec.x<=0){//如果横坐标超出了左边的界限,揪回来。
119             spec.x=0;
120         }else if(spec.x>=(CANVAS_WIDTH-imgWidth)){//如果横坐标大于画布边界与图片边界的差就是到了边缘
121             
122             spec.x=CANVAS_WIDTH-imgWidth;//让它等于边界的值
123             
124         }
125         if(spec.y<=0){//和x轴类同。
126             spec.y=0;
127         }else if(spec.y>=CANVAS_HEIGHT-imgHeight){
128             spec.y=CANVAS_HEIGHT-imgHeight;
129         }
130     },
131     onMove:function(){//onMove:移动方法->judgeBundle->move,此处只是循环的移动。,当然也可以自己定义移动方法,但要在子类中定义了。
132         
133         if(spec.y>CANVAS_HEIGHT-imgHeight||spec.y<0){//如果到了最上边,或者最下边就向相反方向移动
134             spec.movey=-spec.movey;
135             
136         }
137         if(spec.x>(CANVAS_WIDTH-imgWidth)||spec.x<0){//类似于上面的判断
138             spec.movex=-spec.movex;
139             
140         }
141         this.judgeBundle();//判断是否超出边界
142         this.move(spec.movex,spec.movey);//移动
143         
144         return {type:"drawimg",func:"drawimg",params:[spec.img,spec.x,spec.y]};//移动了,把移动事件返回去,缓存图片和坐标,最后一起画出来。
145     }
146     };
147     };
fly.js

bullets.js:

 1 /*子弹类,继承了fly类,增加了Function类的method方法,给Object增加了superior方法使其可以调用父类的方法。,增加了isbullet方法。
 2 *改写了onConflict方法使子弹之间不能发生碰撞。
 3 */
 4 var bullets=function(spec){
 5         var isbullet=true;//是否子弹(父类没有)
 6     var that=fly(spec);//子弹类继承fly类
 7     Function.prototype.method = function(name,func){//为Function增加method,调用模式为:function1.method('xxx',function(){});
 8         if(!this.prototype[name])  {
 9             this.prototype[name] = func;
10         }
11         return this;    
12     }
13     Object.method('superior',function(name){//调用上面的method方法,给Object增加父类的内容。
14                         var that=this;
15                          var method = that[name];
16                         return function(){
17                                     return method.apply(that,arguments);//第一个参数为上下文,第二个参数为传递的参数。
18                           };
19             });
20     var super_onConflict=that.superior('onConflict');//父类的onConflict方法
21     that.onConflict=function(other){
22         if(typeof other.isbullet === 'function' && other.isbullet()){//如果碰撞双方均为子弹,不发生碰撞。
23             return;
24         }
25         super_onConflict(other);
26     }
27     that.isbullet=function(){
28         return true;//该方法仅仅表明这个类是子弹类。。。
29     };
30     return that;
31 };
32 var playerbullet=function(spec){//选手子弹类,发出子弹就新给一个子弹类
33     var that=bullets(spec);//子弹类继承bullets类,好多方法父类都实现了,子类就改个onMove方法就行了
34     
35     that.onMove=function(){
36         if(that.y()<0||that.y()>CANVAS_HEIGHT||that.x()<0||that.x()>CANVAS_WIDTH){//这里只判定如果超出边界,就让它消失,其他的方法父类已经实现了。
37             //alert("outbundle"+that.x()+that.y());
38             //alert(that.index());
39             return [{func:"disapear",params:[that.index()]},{func:"reduceBulet",params:[1]}];
40             
41         }
42         //that.judgeBundle();
43         that.move(0,-1);//移动垂直向上
44         
45         return {type:"drawimg",func:"drawimg",params:[that.img(),that.x(),that.y()]};//如果没超出边界,更新它在画布上的位置
46     
47     };
48     return that;//这个变量就是父类的引用。子类可以对父类进行扩展,就是通过这样的方式。
49     };
bullets.js

friendplane.js:

//这个全局变量用来响应按键,上下左右,改变它的私有变量,并具有返回私有变量的方法。外部不可访问其私有变量。
$myplane=function(){
var movex=0;//x轴的方向
var movey=0;//y轴方向
//var status=0;
$(document).bind('keydown',function(event){//按键按下事件
    if(event.which){
     switch (event.which) {
       case 37://左边按下
        movex=-1;
        //movey=0;
        status=0;
        break;
    case 38://上边按下
        
        movey=-1;
        //status=0;
        break;
    case 39://右边
        
        movex=1;
        //movey=0;
        //status=0;
        break;
    case 40://下边
        //movex=0;
        
        
        movey=1;
        
        //status=0;
        break;
    //case 17://control按下(先不处理)
        //status=1;
        //break;
    default:    //默认
        movex=0;
        movey=0;
        status=0;
        break;
    }
    
}
});
//键盘按上事件
$(document).bind('keyup',function(event){
          if(event.which){
     switch (event.which) {
       case 37://左边
        
    case 39://右边    
        movex=0;
        break;
    case 38://上边
    case 40://下边
        movey=0;
        break;
    default:    //默认
        
        break;
    }
    }
    });
return {
    init:function(){
        movex=0;
        movey=0;
        status=0;
    },
    x:function(){
        return movex;    
    },
    y:function(){
        return movey;
    },
    status:function(){
        return status;
    }
    };
}();

var playerplane=function(spec){//选手飞机
    var shootTimes=0;//这个用来做频率的次数统计,多少帧发一个子弹,后面会给它递增,然后求模,然后又初始化为1
    var frq=spec.bullet.frq;//如果子弹中有频率字面量,那么这个频率能确定,否则给一个不会看花眼的频率
    var frqcy=Math.floor((typeof frq === 'undefined')?20:frq);

    if(typeof Object.beget != 'function'){//这个步骤完成了Object的beget方法,新建立Object就不用new Object()了,那简直弱爆了,只要调用Object.beget('xxx')就直接实
                        //例化了,又复制了'xxx'这个类的所有的键值对。这个方法得自《javascript语言精粹》一书
    Object.beget=function(o){
                var F=function(){};
                F.prototype=o;
                return new F();
        };
    }

    //var specblt=Object.beget(spec.bullet);
    //var specblt=spec.bullet;
    spec.x=0;//选手飞机的横坐标初始为0,或者任意值
    spec.y=CANVAS_HEIGHT;//选手飞机的纵坐标为最下面
    var that=fly(spec);//选手也是fly的子类。
    that.shoot=function(){//不过选手能发炮弹,fly就不可以
    var specblt=Object.beget(spec.bullet);//这个是调用了Object刚刚初始化的原型,复制一个bullet的实例
    specblt.x=that.x()+(that.width()-specblt.img.width)/2;//然后bullet最好是在正中央发出去,当然如果你不想这么做,也没关系。
    specblt.y=that.y()-(that.height()-specblt.img.height)/2;
    return {func:"shoot",params:[specblt]};//给个发射事件交给后台去处理吧。

    };
    that.onMove=function(x,y){//选手飞机不需要来回跳来跳去的,只要根据键盘响应进行移动就好啦,还是调用了move方法。
    shootTimes++;
    if(shootTimes==frqcy)
    {
        shootTimes=1;
    }
    
    that.move($myplane.x(),$myplane.y());
    that.judgeBundle();
    //$myplane.init();
    //alert(shootTimes%frqcy);
    if(shootTimes%frqcy === 1){
    
    return [{type:"drawimg",func:"drawimg",params:[that.img(),that.x(),that.y()]},that.shoot()];//如果这个帧已经过了预定的次数,就发射一颗炮弹并根据需求改变自己的位置
    }
    return {type:"drawimg",func:"drawimg",params:[that.img(),that.x(),that.y()]};//否则就改变自己的位置就好啦。
    
    };
    
    return that;
    };
friendplane.js

 

注意:这里的对象实例化形式是:

var objct = function(spec){//spec为私有变量不可全局访问因此这种方式有良好的封装性
       return {//此处返回了一个对象,对象中的内容以字面量:属性值的形式
         oo:function(){
             return spec.oo;
            },
           xx:function(){
              return spec.xx;
           }
        };
};
View Code

我们得到父类方法的引用的形式是:

Function.prototype.method = function(name,func){//为Function增加method,调用模式为:function1.method('xxx',function(){});
        if(!this.prototype[name])  {
            this.prototype[name] = func;
        }
        return this;    
    }
    Object.method('superior',function(name){//调用上面的method方法,给Object增加父类的内容。
                        var that=this;
                         var method = that[name];
                        return function(){
                                    return method.apply(that,arguments);//第一个参数为上下文,第二个参数为传递的参数。
                          };
            });
    var super_onConflict=that.superior('onConflict');//父类的onConflict方法

这种形式是书上写的,对于这种形式,我有些不懂得地方:我们在Function的原型中链接了一个method方法,又在Object调用method方法那么按照书上所说:“Function的prototype(原型链)是隐藏链接到Object的,而javascript在查找原型时,是一级一级查找的,如果当前级别的原型字面量可以找到就返回这个字面量对应的值,否则就根据原型链查找上一级的原型直到找到为止,而如果在Object中都找不到原型就返回'undefined'”这里我在Object中岂不是根本就找不到method这个字面量对应的内容了?这岂不是相互矛盾?而程序却明显没有报错。希望有人能够解我困惑,指我迷津。

有关这种形式,推荐一本书:《javascript语言精粹》(Douglas Crockford著,赵泽欣译)后面会给出一些此书中自己认为比较重要的笔记

恩,Model层就说到这里罢,这个Model层是可以扩展的,只要按照bullets.js的形式,可以有无数个子类的哦。根据抽丝剥茧的原理,下一篇将介绍打灰机service层的实现。

转载于:https://www.cnblogs.com/saajireign/p/3162323.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: HTML5、CSS3和JavaScript是现代Web开发中最重要的技术。HTML5是一种标记语言,用于创建Web页面的结构和内容。CSS3是一种样式表语言,用于控制Web页面的外观和布局。JavaScript是一种脚本语言,用于添加交互性和动态效果。这三种技术的结合使得Web开发更加灵活、响应式和富有创意性。 ### 回答2: HTML5、CSS3 和 JavaScript 是现代 Web 开发所必需的三种技术。它们分别负责网页的结构、样式和交互效果。 HTML5 是 HyperText Markup Language 的第五个版本,用于描述网页的结构和内容。它提供了许多新的标签,如 section、article、header、footer、nav 等,使文档的结构更清晰,更语义化。HTML5 还引入了许多新的 API,例如 canvas、video、audio、localStorage 等,增强了 Web 应用程序的功能。 CSS3 是 Cascading Style Sheets 的第三个版本,用于定义网页的样式和布局。它新增了许多属性和选择器,如 border-radius、box-shadow、transform、@media 等,使样式的实现更加高效、灵活。CSS3 还支持动画和过渡效果,可以让网页更加生动和富有动感。 JavaScript 是一种脚本语言,用于实现网页的交互效果和动态功能。它可以通过 DOM(Document Object Model)和 BOM(Browser Object Model)来操作文档和浏览器。JavaScript 还可以通过 Ajax 技术实现异步请求和局部更新,以提高网页的响应速度和用户体验。 综上所述,HTML5、CSS3 和 JavaScript 三者紧密结合,相互配合,能够实现丰富、动态、交互的 Web 应用程序。对于 Web 开发者来说,学习和掌握这三种技术是必须的,能够使开发效率和质量都有所提升。同时,Web 技术的不断发展也需要我们不断学习和跟进最新的技术,以保持技术的竞争力和应用的先进性。 ### 回答3: HTML5,CSS3和JavaScript是当今Web开发所需的核心基础技术。HTML5是WEB浏览器使用的主要标记语言之一,它的开发旨在优化互联网应用程序的交互性和功能性,进而增强用户体验。CSS3则扮演着设计和展现网页的角色,它提供了更加强大,更加美观的网页布局和样式设计能力。JavaScript是编写在客户端的脚本语言,它可用于在网页上创建丰富的交互效果,包括动态页面、表单校验、数据验证、动画等。 HTML5是WEB编程领域变革性的关键因素,它提供了许多新功能和标签,如地理位置、复合组件、多媒体元素、canvas等等。HTML5将同时支持不同等级的设备,如手机,平板电脑和桌面计算机,一次编写的完整代码将有更广泛的应用,大幅缩短开发周期,提高开发效率和准确性。 CSS3则具有更强大的样式和布局设计能力,可以创建更漂亮更具视觉效果,更简单的Web页面,例如,可以通过CSS3的阴影包装,转换,渐变和动画来实现交互元素的设计。CSS3实现了响应式设计,自适应屏幕尺寸,依靠媒体查询、弹性盒组布局(Equal Columns Trick)、媒体查询和流式布局等方式实现。 JavaScript可以添加交互元素,包括动态页面、表单验证和数据验证,实现可视化效果等。它可以控制对象的行为和状态、创建动态内容和动画、数据的异步加载和更新等。JavaScript的应用非常广泛,也有很多优秀的技术和框架如JQuery,React和AngularJS等等来支持Web开发。JavaScript还实现了Web服务器与客户端之间的通讯,实现了AJAX,WebSockets等技术。 总之,HTML5,CSS3和JavaScript是当前Web开发所不可或缺的三大技术,他们都有各自的长处和优点。随着他们不断的发展和更新,Web前端技术也将不断演化。这样一来,Web开发成为一个不断创新,不断进步的领域,并将继续成为推动互联网发展水平提高的重要一环。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值