HTML5小游戏之爱心鱼

项目的github地址:https://github.com/chenmonkey/game

在线演示网址:https://chenmonkey.github.io/game/


准备工作:


1.搭建html环境

新建tinyHeart.html文件

<div class="all_bg">
    <div id="allcanvas">
     <canvas id="canvas1" width="800" height="600"></ canvas>
     < canvas id="canvas2" width="800" height="600"></ canvas>
    </div>
  </div>


其中,canvas1和canvas2叠加在一起,使用absolute绝对定位,用z-index设置堆叠顺序。

2.绘制背景

(1)新建main.js文件(主要的js文件)
   想要boay内容加载完之后执行一个函数:windows.onload
   使用canvas API,获得场景:ctx1=can1.getContext('2d')
   让游戏动起来,即刷新:requestAnimationFrame(gameloop)
     ***requestAnimationFrame在不同浏览器上需要进行不同的配置(commonFunction.js)

(2)绘制背景:background.js(cxt2.drawImage(src,x1,y1,x2,y2))

第一阶段

1.静止的海葵(ane.js)

 
  在ane.js中:
      定义一个类aneObj
      初始化:aneObj.prototype.init=function(){}:确定每个海葵的位置

     绘制海葵:aneObj.prototype.draw=function(){}

2.果实绘制(黄色果实和蓝色果实,果实从有到无,漂浮速度不同,飘到屏幕外消失)(fruit.js)

 a. 状态
        活跃:在海葵上生长,长大后向上漂浮
        不活跃:排队等候
 b. 在fruit.js中
        定义一个类fruitObj
         初始化:fruitObj.prototype.init=function(){}:确定果实的活跃状态
       使用到的API:drawImage()
        定义规则:1)长大=》成熟。
                         2)当一个果实漂浮出去之后,告诉闲置的果实,要有一个需要出生了。
                         3) 保持屏幕上有15个果实

       
    (1)静态果实  
            
       绘制果实:fruitObj.prototype.draw=function(){}:在海葵上绘制果实
       果实生长:fruitObj.prototype.born=function(i){}:找到一个位置让果实出生
       果实更新状态:fruitObj.prototype.update=function(){}

(2)果实上浮(fruit.js)

果实增长:this.l[i]+=this.spd[i]*deltaTime;

果实上浮:this.y[i]-=this.spd[i]*7*deltaTime;


(3)果实数量控制(绘制蓝色果实,屏幕上保持有15个果实)
     function fruitMonitor(){}  //监视果实的状态
    function sendFruit(){}   //判断哪些果实处于闲置状态,哪些果实活着

3. 大鱼绘制(画大鱼,身子,尾巴,眼睛)

 (1)画大鱼

  1)使用到的API:

      translate()  //canvas的API,转换画布的用户坐标
      rotate();   //canvas的API,绘图在画布中都显示为旋转的
      Math.atan2(y,x)  //javascript的API,反正切,返回值为数字(PI to -PI)
 
2)使用到的方法
   大鱼初始化:momObj.prototype.init = function(){}
   绘制大鱼:  momObj.prototype.draw = function(){}

***中draw()方法中: context.translate(x,y)函数可以使画布的原点坐标变为(x,y),即画笔从这个点开始画。因为我们画完一部分内容之后希望重新定义画笔的属性,所以用context.save()和context.restore()包裹例如context.translate(),context.fillStyle等属性设定
(2)大鱼随鼠标移动、旋转

         知识点:极坐标

    1)获取鼠标移动的值
   在main.js中添加方法:鼠标移动:function onMouseMove(e){}
  
2)在mom.js中
       大鱼随鼠标移动:this.x=lerpDistance(mx, this.x, 0.98);   //0.99>>0.98大鱼的运动速度变缓
                                   this.y=lerpDistance(my, this.y, 0.99);
       大鱼和鼠标之间的角度差:var deltaY=my-this.y;//鼠标跟大鱼的坐标差
                                               var deltaX=mx-this.x;
                                               var beta=Math.atan2(deltaY,deltaX);
       大鱼的角度不断趋向鼠标的角度:this.angle=lerpAngle(beta,this.angle,0.6);   // 0.9>>0.6,大鱼的反应变灵敏


4.大鱼吃果实

 (1)大鱼和果实的碰撞检测
    如果大鱼和果实的距离足够近,则判断大鱼吃掉该果实;如果距离足够远,则没有吃到该果实
       1)在fruit.js中,添加dead()方法
      2)在momFruitCollision.js中,判断大鱼跟每一个果实的距离:调用封装函数var l=calLength2(fruit.x[i], fruit.y[i], mom.x, mom.y),若l<900,则fruit.dead(i),果实死亡。

(2)优化
   
当我们在浏览器中打开html页面,一段时间不去管它之后,我们会发现下面情况。
原因:这是因为我们在绘制果实的时候,它的尺寸是和deltaTime成正比的。
     if(this.l[i]<=14){
            this.l[i]+=this.spd[i]*deltaTime;//果实增长(随时间逐渐增长)
         }


5.小鱼绘制

和大鱼绘制相同

第二阶段

1.小鱼身体活动完善

(1)尾巴摆动
在main.js中:定义babyTail数组,存放小鱼尾巴资源,var babyTail=[];
                     在draw()方法里:小鱼尾巴摆动

                    for(var i=0;i<7;i++)
                     {
                          babyTail[i]=new Image();
                         babyTail[i].src="img/babyTail"+i+".png";
                      }
  在baby.js中,定义计时器以及记录当前图片序号的变量:
                  this.babyTailTimer=0;//计时器
                  this.babyTailCount=0;//记录当前图片序号的变量
              在draw方法中:绘制小鱼尾巴: ctx1.drawImage(babyTail[babyTailCount],-babyTail[babyTailCount].width*0.5+25,-babyTail[babyTailCount].height*0.5);
  
(2)眨眼睛

同小鱼摇尾巴相似,只不过眨眼睛是随机的

if(this.babyEyeCount==0)//如果小鱼睁着眼睛
  {
   this.babyEyeInterval=Math.random()*1500+2000;//睁眼睛时间为1500到3500毫秒之间
  }else
  {
   this.babyEyeInterval=200;//眯眼睛时间为200毫秒
  }

(3)身体变白
跟小鱼摇尾巴相似,只不过当小鱼身体完全变白后,游戏结束,不恢复
if(this.babyBodyTimer>300)
    {
     this.babyBodyCount=this.babyBodyCount+1;
     this.babyBodyTimer%=300;
     if(this.babyBodyCount>19)
     {
      this.babyBodyCount=19;
     }
    }


2.大鱼动画完善

(1)大鱼喂小鱼

判断大鱼和小鱼的距离

function momBabyCollision()
{
 var l=calLength2(mom.x,mom.y,baby.x,baby.y);
 if(l<900)
 {
  //小鱼恢复满血状态
     baby.babyBodyCount=0;
 }
}


(2)大鱼摇尾巴
同小鱼摇尾巴


(3)大鱼眨眼睛
同小鱼眨眼睛

(4)大鱼身体升级准备

      a. 在main.js中 
        在init()方法中,实例化dataObj变量:data=new dataObj();
       在gameloop()方法中,执行draw()方法:data.draw();

     b. 在data.js中
      起始状态(大鱼吃果实之前):this.fruitNum=0;//大鱼吃果实数量
                                               this.double=1;//大鱼吃果实游戏分值(若吃一个蓝色果实,则分值增倍)
   

      大鱼喂完小鱼之后身体状态恢复原态:dataObj.prototype.reset=function(){}

      在画布1上画出:大鱼吃果实数量(未喂小鱼之前),大鱼游戏分值:dataObj.prototype.draw=function(){}
   c. 在collision.js中

         在momFruitCollision()方法中,判断大鱼是否吃到蓝色果实,若是,分值为2

          if(fruit.fruitType[i]=="blue")
         {
             data.double=2;
          }
         在momBabyCollision()方法中,大鱼若喂小鱼,则身体恢复原态,即执行data.reset()方法

(5)大鱼身体升级
  大鱼身体有两种颜色,橘色和蓝色,当吃到橘色果实时,大鱼身体变橘色;当吃到蓝色果实时,身体变白色。
 在main.js中://大鱼身体变化
                     for(var i=0;i<8;i++)
                      {
                      bigBodyOrg[i]=new Image();
                      bigBodyBlu[i]=new Image();
                      bigBodyOrg[i].src="img/bigSwim"+i+".png";
                      bigBodyBlu[i].src="img/bigSwimBlue"+i+".png";
                     }
在mom.js中:1)this.bigBodyCount=0;//(大鱼身体)记录当前图片序号的变量
                      2)在draw方法中,通过判断double的值来确定大鱼身体的颜色,当double=1时,身体为橘色;否则,身体为蓝色                   3)在collision中,判断大鱼与果实之间的距离,当大鱼吃到果实时,
                                    a. mom.bigBodyCount++;//大鱼身体变化计数; 
                                   b. if(mom.bigBodyCount>7){mom.bigBodyCount=7; }
(6)游戏分值计算(1)
在data.js中:(1)添加addScore方法,计算游戏的分值(this.score+=this.fruitNum*100*this.double;)
                     (2)删除reset方法,因为与addScore方法中相重复
在collision.js中,当大鱼碰到小鱼之后,执行addScore方法  (data.addScore();)  
                 
   游戏分值计算(2)
         游戏设想:(1)只有大鱼吃到果实,大鱼碰到小鱼,小鱼身体才会恢复;
                       (2)只有游戏未结束,大鱼才能继续吃果实,喂小鱼;否则,大鱼不能继续吃果实喂小鱼,而且鼠标不能移动,并会出现GAMEOVER字样。
        代码实现:(1)在data.js中,添加gameOver属性(this.gameOver=false)
                          (2)在baby.js中,当小鱼身体完全变白,data.gameOver=true(即游戏结束)
                         (3)在collision.js中的momBabyCollision方法中,判断大鱼吃的果实数量是否大于0切游戏是否未结束,是则继续游戏;否则结束游戏
                       

                               if(data.fruitNum>0 && !data.gameOver){}

                      

3、动画特效

(1)大鱼吃果实特效
        大鱼每碰到一个果实就要产生一个白色的圈
       物体池(pool):存储很多相同的物体
        检测是否有闲着的物体

        半径逐渐增大,颜色逐渐减弱,半径和颜色呈反比关系

        绘图API
   1)在wave.js中,白色圆圈初始化:waveObj.prototype.init=function(){}
                              绘制白色圆圈:waveObj.prototype.draw=function(){}
                              白色圆圈出生:waveObj.prototype.born=function(x,y){}
  2)在collision.js的momFruitCollision()方法中,当大鱼碰到果实,执行wave.born(fruit.x[i],fruit.y[i]);
  3)在main.js中,在init()方法中,wave=new waveObj();wave.init();
                             在gameloop()方法中,wave.draw();


(2)大鱼喂小鱼特效
跟大鱼吃果实特效相似



(3)海葵摆动特效
a. 绘制二次贝塞尔曲线(起始点和控制点不动,结束点摆动)
b. 正弦函数(控制海葵摆动的范围)
c. this.rootx=[];//起始点的x坐标,y坐标不需要设,即画布底部
   this.headx=[];//结束点的x坐标
   this.heady=[];//结束点的y坐标
   this.amp=[];//海葵摆动的幅度
   this.alpha=0;//正弦函数的角度
(4)果实长在海葵上面(即使海葵飘动)
在ane.js中,海葵的头部坐标:this.headx[i]=this.rootx[i]+l*this.amp[i];
                                              this.heady[i]=canHeight-250+Math.random()*50;
在fruit.js中,果实的坐标:var aneID=Math.floor(Math.random()*ane.num);//海葵的坐标(在所有海葵中随机找一个)
                                        this.aneNO[i]=aneID; //找到一个位置让果实出生
                                         var NO=this.aneNO[i];
                                         this.x[i]=ane.headx[NO];
                                         this.y[i]=ane.heady[NO];

(5)漂浮物制作(随水流轻轻左右摇摆)
三角函数sin()
漂浮物的摆动跟海葵的摆动同步
在dust.js中,dustObj.prototype.init=function(){}
                    dustObj.prototype.draw=function(){}
在main.js中,init()方法中://漂浮物
                                      for(var i=0;i<7;i++)
                                      {
                                         dustPic[i]=new Image();
                                          dustPic[i].src="img/dust"+i+".png";
                                       }
                                      dust=new dustObj();
                                      dust.init();
                     draw()方法中:dust.draw();


猜你喜欢

转载自blog.csdn.net/m0_37920381/article/details/80313391