首页 > 代码库 > js原生捕鱼达人(二)

js原生捕鱼达人(二)

昨天写到构造炮弹,有点小bug不知道大家发现没有,今天继续昨天的步骤

7》构造炮弹   思路和前面都是一样一样的   注意构造函数中需要考虑的属性  和  构造函数的原型上面的方法

    <script src="http://www.mamicode.com/js/resource.js"></script>
    <script src="http://www.mamicode.com/js/com.js"></script>
    <script src="http://www.mamicode.com/js/fish.js"></script>
    <script src="http://www.mamicode.com/js/cannon.js"></script>
    <script>
        //开始构造炮弹
        //炮弹具体尺寸
        var BULLET_SIZE=[
            null,
            {x: 86, y: 0, w: 24, h: 26},
            {x: 62, y: 0, w: 25, h: 29},
            {x: 30, y: 0, w: 31, h: 35},
            {x: 32, y: 35, w: 27, h: 31},
            {x: 30, y: 82, w: 29, h: 33},
            {x: 0, y: 82, w: 30, h: 34},
            {x: 0, y: 0, w: 30, h: 44}
        ];
        //炮弹的构造函数,同样先在resource.js中加载炮弹的资源, 炮弹的属性有 type 位置x y rotate iSpeed move
        function Bullet(type){
            this.type=type;
            this.x=0;
            this.y=0;
            this.rotate=0;
            this.iSpeed=1;
            this.move();
        }
        //暂时想到的炮弹原型上的方法有draw move ,先写,后面出现其他的再补充
        Bullet.prototype.draw=function(gd){
          //同样的炮弹的尺寸数据表中已经量好并且给出
            var w=BULLET_SIZE[this.type].w;
            var h=BULLET_SIZE[this.type].h;
            //这里与前面不同的是需要定义不同尺寸炮弹的起始位置,数据表中已经给出,直接获取
            var x=BULLET_SIZE[this.type].x;
            var y=BULLET_SIZE[this.type].y;
            //开始画炮弹,
            gd.save();
            gd.translate(this.x,this.y);
            gd.rotate(this.rotate);
            gd.drawImage(JSON[‘bullet‘],
                x,y,w,h,
                -w/2,-h/2,w,h
            );
            gd.restore();
        };
        //添加炮弹move的方法,和fish运动的思路相同
        Bullet.prototype.move=function(){
            //开启定时器不停的改变炮弹的位置并且重绘,同样,注意事件中的定时器里的this有问题,需要提前存正确的this的指向
            var _this=this;
            setInterval(function(){
                //和鱼的move有些不同的是炮弹的y轴的方向不同炮弹都是是向上射出的
                _this.x+=Math.sin(d2a(_this.rotate))*_this.iSpeed;
                _this.y-=Math.cos(d2a(_this.rotate))*_this.iSpeed;
            },30);

        };
        document.addEventListener(‘DOMContentLoaded‘,function(){
            var oC=document.getElementById(‘c1‘);
            var gd=oC.getContext(‘2d‘);
            //开始画炮台  画炮台需要先加载资源,然后再画,这里没有使用面向对象的概念
            loadImage(resource,function(){
                //设置炮的初始位置,初始位置在资源文件中已经写明
                var c=new Cannon(4);
                c.x=431;
                c.y=570;
                //存放炮弹的数组
                var arrBullet=[];
                setInterval(function(){
                    //炮是在炮台上的,可以在画炮台的时候一起画出来,画之前为了避免重绘,需要先清除画布
                  /* gd.save();*/
                    gd.clearRect(0,0,oC.width,oC.height);
                    gd.drawImage(JSON[‘bottom‘],
                            0,0,765,70,
                            0,532,765,70
                    );
                    //调用炮的方法draw来画炮 和鱼的转动相同,当点击画布的时候,炮需要跟随鼠标的指向来转动,这里在转动的时候我们改改变炮的转动角度,然后重新不停的删除,再画炮  这个效果思路和画鱼相同,需要配合定时器来实现
                    c.draw(gd);
                    //将当次点击所产生的炮弹画出来
                    for(var i=0;i<arrBullet.length;i++){
                        arrBullet[i].draw(gd);
                    }
                    /*gd.restore();*/
                },30);
                //当点击画布的时候炮的角度对着鼠标点击的位置,并进行重绘
                oC.onclick=function(ev){
                    //这里需要梳理鼠标点击的位置和炮旋转角度之间的关系(附图说明--炮的旋转角度.png)
                    var x=ev.clientX-oC.offsetLeft- c.x;
                    var y= c.y-(ev.clientY-oC.offsetTop);
                    //计算角度,注意角度的公式tan是临边比对边,和数学公式的有所不同 Math.atan2(y,x);并且这里是弧度转角度,需要在com.js中添加a2d的函数
                    var d=90-a2d(Math.atan2(y,x));
                    c.rotate=d;
                    //当点击的时候生成炮弹,所以在点击事件中添加炮弹
                    var bullet=new Bullet(c.type);
                    //炮弹的位置和旋转角度和炮的位置和旋转角度相同,
                    bullet.x= c.x;
                    bullet.y= c.y;
                    bullet.rotate = c.rotate;
                    //注意炮弹不能画在这里,如果画在这里会被画炮和炮台时所清空,当然潘丹并不是只画一个,可以用一个数组来存储所画出来的炮弹,然后在炮旋转重绘的时候同时添加炮弹,为了让点击事件和定时器都能用到这个数组,这个数组应该写到事件和定时器的父级的变量空间中
                    /*bullet.draw(gd);*/
                    //讲当次点击画布所创建的炮弹存入arrBullet中
                    arrBullet.push(bullet);
                };
            });

      /*    //调用面向对象方法中创造的鱼,并在画布上画出鱼
            loadImage(resource,function(){
                var f1=new Fish(1);
                //给出新创建出鱼的出事位置
                f1.x=300;
                f1.y=300;
                //在画鱼的时候需要先清除一次画布 同样画之前需要先保存,结束以后再存储
                //使鱼动起来需要不停的在画布上擦除上衣画的鱼并且不停的创建新的鱼,需要配合定时器来实现
                setInterval(function(){
                    gd.clearRect(0,0,oC.width,oC.height);
                    gd.save();
                    //画鱼的方法在面向对象中都已经创建,在这直接使用就可以
                    f1.draw(gd);
                    gd.restore();
                },30);
            });*/
        },false);
    </script>
</head>
<body>
    <canvas id="c1" width="800" height="600"></canvas>
</body>

  8》设置炮弹的显示范围     在炮弹跑出屏幕的时候,将画出的炮弹在炮弹的数组中删除

loadImage(resource,function(){
    //设置炮的初始位置,初始位置在资源文件中已经写明
    var c=new Cannon(4);
    c.x=431;
    c.y=570;
    //存放炮弹的数组
    var arrBullet=[];
    setInterval(function(){
        //炮是在炮台上的,可以在画炮台的时候一起画出来,画之前为了避免重绘,需要先清除画布
      /* gd.save();*/
        gd.clearRect(0,0,oC.width,oC.height);
        gd.drawImage(JSON[‘bottom‘],
                0,0,765,70,
                0,532,765,70
        );
        //调用炮的方法draw来画炮 和鱼的转动相同,当点击画布的时候,炮需要跟随鼠标的指向来转动,这里在转动的时候我们改改变炮的转动角度,然后重新不停的删除,再画炮  这个效果思路和画鱼相同,需要配合定时器来实现
        c.draw(gd);
        //将当次点击所产生的炮弹画出来
        for(var i=0;i<arrBullet.length;i++){
            arrBullet[i].draw(gd);
        }
        //这里由于炮弹不停的被创造,数组中也变得越来越大,当炮弹到达一定位置(移出屏幕)的时候,应该清除前面没用的炮弹,避免性能的浪费。注意,我们需要检测每个炮弹的位置,有我就是整个数组所有参数身上的x y
        for(var i=0;i<arrBullet.length;i++){
            if(arrBullet[i].x<0 || arrBullet[i].x>oC.width || arrBullet[i].y>oC.height || arrBullet[i].y<0){
                arrBullet.splice(i,1);
                i--;
            }
        }
        /*gd.restore();*/
    },30);

  昨天的小bug,旋转的时候角度的调试不对,是在写炮弹属性rotate的时候没有将角度转为弧度

//暂时想到的炮弹原型上的方法有draw move ,先写,后面出现其他的再补充
Bullet.prototype.draw=function(gd){
  //同样的炮弹的尺寸数据表中已经量好并且给出
    var w=BULLET_SIZE[this.type].w;
    var h=BULLET_SIZE[this.type].h;
    //这里与前面不同的是需要定义不同尺寸炮弹的起始位置,数据表中已经给出,直接获取
    var x=BULLET_SIZE[this.type].x;
    var y=BULLET_SIZE[this.type].y;
    //开始画炮弹,
    gd.save();
    gd.translate(this.x,this.y);
//这里没有将角度转为弧度
    gd.rotate(d2a(this.rotate));       
    gd.drawImage(JSON[‘bullet‘],
        x,y,w,h,
        -w/2,-h/2,w,h
    );
    gd.restore();
};

  9》加入鱼  

下面的代码只加入鱼从左面出来的情况

loadImage(resource,function(){
    //设置炮的初始位置,初始位置在资源文件中已经写明
    var c=new Cannon(4);
    c.x=431;
    c.y=570;
    //存放炮弹的数组
    var arrBullet=[];
    //存放鱼的数组
    var arrFish=[];

    setInterval(function(){
        //炮是在炮台上的,可以在画炮台的时候一起画出来,画之前为了避免重绘,需要先清除画布
      /* gd.save();*/
        gd.clearRect(0,0,oC.width,oC.height);
        //创造鱼并规定鱼生成的位置,这里只写了鱼只能从左面出来
        //画鱼
        var f1=new Fish(rnd(1,6));
        f1.x=-50;
        f1.y=rnd(0,oC.height);
        //
        f1.rotate=rnd(-90,90);
        arrFish.push(f1);
        for(var i=0;i<arrFish.length;i++){
            arrFish[i].draw(gd);
        }
        gd.drawImage(JSON[‘bottom‘],
                0,0,765,70,
                0,532,765,70
        );
        //调用炮的方法draw来画炮 和鱼的转动相同,当点击画布的时候,炮需要跟随鼠标的指向来转动,这里在转动的时候我们改改变炮的转动角度,然后重新不停的删除,再画炮  这个效果思路和画鱼相同,需要配合定时器来实现
        c.draw(gd);
        //将当次点击所产生的炮弹画出来
        for(var i=0;i<arrBullet.length;i++){
            arrBullet[i].draw(gd);
        }
        //这里由于炮弹不停的被创造,数组中也变得越来越大,当炮弹到达一定位置(移出屏幕)的时候,应该清除前面没用的炮弹,避免性能的浪费。注意,我们需要检测每个炮弹的位置,有我就是整个数组所有参数身上的x y
        for(var i=0;i<arrBullet.length;i++){
            if(arrBullet[i].x<0 || arrBullet[i].x>oC.width || arrBullet[i].y>oC.height || arrBullet[i].y<0){
                arrBullet.splice(i,1);
                i--;
            }
        }
        /*gd.restore();*/
    },30);
    //当点击画布的时候炮的角度对着鼠标点击的位置,并进行重绘
    oC.onclick=function(ev){
        //这里需要梳理鼠标点击的位置和炮旋转角度之间的关系(附图说明--炮的旋转角度.png)
        var x=ev.clientX-oC.offsetLeft- c.x;
        var y= c.y-(ev.clientY-oC.offsetTop);
        //计算角度,注意角度的公式tan是临边比对边,和数学公式的有所不同 Math.atan2(y,x);并且这里是弧度转角度,需要在com.js中添加a2d的函数
        var d=90-a2d(Math.atan2(y,x));
        c.rotate=d;
        //当点击的时候生成炮弹,所以在点击事件中添加炮弹
        var bullet=new Bullet(c.type);
        //炮弹的位置和旋转角度和炮的位置和旋转角度相同,
        bullet.x= c.x;
        bullet.y= c.y;
        bullet.rotate = c.rotate;
        //注意炮弹不能画在这里,如果画在这里会被画炮和炮台时所清空,当然潘丹并不是只画一个,可以用一个数组来存储所画出来的炮弹,然后在炮旋转重绘的时候同时添加炮弹,为了让点击事件和定时器都能用到这个数组,这个数组应该写到事件和定时器的父级的变量空间中
        /*bullet.draw(gd);*/
        //讲当次点击画布所创建的炮弹存入arrBullet中
        arrBullet.push(bullet);
    };
});

  相同的,鱼只从右面出来的情况值是改变了鱼初始位置的x 和rotate

//画鱼  鱼从右面出来的情况
var f1=new Fish(rnd(1,6));
f1.x=oC.width+50;
f1.y=rnd(0,oC.height);
//
f1.rotate=rnd(90,270);
arrFish.push(f1);
for(var i=0;i<arrFish.length;i++){
    arrFish[i].draw(gd);
}

  

鱼随机从两边出来,
com.js文件中添加随机数的公用函数
function rnd(m,n){
    return parseInt(Math.random()*(n-m)+m);
}

  index.html中的内容 

document.addEventListener(‘DOMContentLoaded‘,function(){
    var oC=document.getElementById(‘c1‘);
    var gd=oC.getContext(‘2d‘);
    var rule=0.5;
    //开始画炮台  画炮台需要先加载资源,然后再画,这里没有使用面向对象的概念
    loadImage(resource,function(){
        //设置炮的初始位置,初始位置在资源文件中已经写明
        var c=new Cannon(4);
        c.x=431;
        c.y=570;
        //存放炮弹的数组
        var arrBullet=[];
        //存放鱼的数组
        var arrFish=[];

        setInterval(function(){
            //炮是在炮台上的,可以在画炮台的时候一起画出来,画之前为了避免重绘,需要先清除画布
          /* gd.save();*/
            gd.clearRect(0,0,oC.width,oC.height);
            //创造鱼并规定鱼生成的位置,这里只写了鱼只能从左面出来
            //画鱼  鱼从左右两边同时随机出现  实现这个的原理是Math.random()是0-1的数,这里设定一个规则,rule=0.5,所以两边几率各为50%;
            if(Math.random()<rule){
                var f1=new Fish(rnd(1,6));
                f1.x=oC.width+50;
                f1.y=rnd(0,oC.height);
                //
                f1.rotate=rnd(90,270);
            }else{
                var f1=new Fish(rnd(1,6));
                f1.x=-50;
                f1.y=rnd(0,oC.height);
                //
                f1.rotate=rnd(-90,90);
            }

            arrFish.push(f1);
            for(var i=0;i<arrFish.length;i++){
                arrFish[i].draw(gd);
            }
            gd.drawImage(JSON[‘bottom‘],
                    0,0,765,70,
                    0,532,765,70
            );
            //调用炮的方法draw来画炮 和鱼的转动相同,当点击画布的时候,炮需要跟随鼠标的指向来转动,这里在转动的时候我们改改变炮的转动角度,然后重新不停的删除,再画炮  这个效果思路和画鱼相同,需要配合定时器来实现
            c.draw(gd);
            //将当次点击所产生的炮弹画出来
            for(var i=0;i<arrBullet.length;i++){
                arrBullet[i].draw(gd);
            }
            //这里由于炮弹不停的被创造,数组中也变得越来越大,当炮弹到达一定位置(移出屏幕)的时候,应该清除前面没用的炮弹,避免性能的浪费。注意,我们需要检测每个炮弹的位置,有我就是整个数组所有参数身上的x y
            for(var i=0;i<arrBullet.length;i++){
                if(arrBullet[i].x<0 || arrBullet[i].x>oC.width || arrBullet[i].y>oC.height || arrBullet[i].y<0){
                    arrBullet.splice(i,1);
                    i--;
                }
            }
            /*gd.restore();*/
        },30);
        //当点击画布的时候炮的角度对着鼠标点击的位置,并进行重绘
        oC.onclick=function(ev){
            //这里需要梳理鼠标点击的位置和炮旋转角度之间的关系(附图说明--炮的旋转角度.png)
            var x=ev.clientX-oC.offsetLeft- c.x;
            var y= c.y-(ev.clientY-oC.offsetTop);
            //计算角度,注意角度的公式tan是临边比对边,和数学公式的有所不同 Math.atan2(y,x);并且这里是弧度转角度,需要在com.js中添加a2d的函数
            var d=90-a2d(Math.atan2(y,x));
            c.rotate=d;
            //当点击的时候生成炮弹,所以在点击事件中添加炮弹
            var bullet=new Bullet(c.type);
            //炮弹的位置和旋转角度和炮的位置和旋转角度相同,
            bullet.x= c.x;
            bullet.y= c.y;
            bullet.rotate = c.rotate;
            //注意炮弹不能画在这里,如果画在这里会被画炮和炮台时所清空,当然潘丹并不是只画一个,可以用一个数组来存储所画出来的炮弹,然后在炮旋转重绘的时候同时添加炮弹,为了让点击事件和定时器都能用到这个数组,这个数组应该写到事件和定时器的父级的变量空间中
            /*bullet.draw(gd);*/
            //讲当次点击画布所创建的炮弹存入arrBullet中
            arrBullet.push(bullet);
        };
    });

  

降低鱼出现的概率
这里改rule=0.05;
//画鱼  鱼从左右两边同时随机出现  实现这个的原理是Math.random()是0-1的数,定时器的触发时间是30ms一秒钟30多条鱼的诞生有些多,所以在这里我们需要修改规则rule来降低鱼出现的概率,当rule=0.05(概率为原来的20%),再加入一个参数decoration,然后用Math.random()-0.5得出的这个值的范围时0.5到-0.5,这样正负的概率分别为50%,这样我们就能继续进行鱼诞生的方向了;
var decoration=Math.random()-0.5;
if(Math.random()<rule){
    if(decoration<0){
        var f1=new Fish(rnd(1,6));
        f1.x=oC.width+50;
        f1.y=rnd(0,oC.height);
        //
        f1.rotate=rnd(91,269);
    }else{
        var f1=new Fish(rnd(1,6));
        f1.x=-50;
        f1.y=rnd(0,oC.height);
        //
        f1.rotate=rnd(-89,89);
    }
    arrFish.push(f1);
}
for(var i=0;i<arrFish.length;i++){
    arrFish[i].draw(gd);
}

  和炮弹相同的,对鱼进行了优化

for(var i=0;i<arrFish.length;i++){
    arrFish[i].draw(gd);
}
//和炮弹一样,对鱼进行性能优化,再鱼游出屏幕一定范围之后,便将鱼从鱼的数组中清除
for(var i=0;i<arrFish.length;i++){
    if(arrFish[i].x<-50 || arrFish[i].x>(oC.width+50) || arrFish[i].y<0 || arrFish[i].y>(oC.height+50)){
        arrFish.splice(i,1);
        i--;
    }
}

  10》检测是否打到鱼

for(var i=0;i<arrBullet.length;i++){
    if(arrBullet[i].x<0 || arrBullet[i].x>oC.width || arrBullet[i].y>oC.height || arrBullet[i].y<0){
        arrBullet.splice(i,1);
        i--;
    }
}
//进行碰撞检测,这里只做了简单的碰撞检测,我们把每个模型(鱼和子弹)都考虑成了原型,当两个物体的距离小于两个物体的半径之和的时候表明两个物体碰撞,注意,需要循环检测存在的所有的鱼,我们可以提前做一个函数来判断这个距离,当得到碰撞距离的时候函数返回true,当得到没有碰撞的距离的时候,函数返回的是false,这个函数是每条鱼身上的一个方法,每条鱼在游动的时候都在不停的计算这个值,并不停的返回真或者假来供我们判断是否和子弹碰撞
for(var i=0;i<arrFish.length;i++){
    for(var j=0;j<arrBullet.length;j++){
        if(arrFish[i].isIn(arrBullet[j].x, arrBullet[j].y)){
            alert(‘碰到了‘);
        }
    }
}

  在鱼的fish.js文件中给鱼添加检测碰撞的方法 , 大家可以更改子弹的速度this.iSpeed=this.type*2; 这样子弹的型号越大,子弹的速度也就越快

//传入的两个参数x y是子弹的实时位置
Fish.prototype.isIn=function(x,y){
    var a=this.x-x;
    var b=this.y-y;
    var c=Math.sqrt(a*a+b*b);
    //这里需要提前在鱼的构造函数中添加this.collR的属性this.collR=FISH_SIZE[this.type].collR;
    if(c<=this.collR){
        return true;
    }else{
        return false;
    }
};

  碰撞之后删除鱼和子弹

//进行碰撞检测,这里只做了简单的碰撞检测,我们把每个模型(鱼和子弹)都考虑成了原型,当两个物体的距离小于两个物体的半径之和的时候表明两个物体碰撞,注意,需要循环检测存在的所有的鱼,我们可以提前做一个函数来判断这个距离,当得到碰撞距离的时候函数返回true,当得到没有碰撞的距离的时候,函数返回的是false,这个函数是每条鱼身上的一个方法,每条鱼在游动的时候都在不停的计算这个值,并不停的返回真或者假来供我们判断是否和子弹碰撞
for(var i=0;i<arrFish.length;i++){
    for(var j=0;j<arrBullet.length;j++){
        if(arrFish[i].isIn(arrBullet[j].x, arrBullet[j].y)){
            /*alert(‘碰到了‘);*/
            //在鱼碰到了之后,我们需要做的是让相互碰撞的鱼和子弹都消失,也就是从鱼和子弹的数组中删除
            arrFish.splice(i,1);
            i--;
            arrBullet.splice(j,1);
            j--;
        }
    }
}

今天先写到这里,明天继续  

 

转载请注明‘转载于Jason齐齐的博客http://www.cnblogs.com/jasonwang2y60/’

js原生捕鱼达人(二)