zoukankan      html  css  js  c++  java
  • 【默默努力】fishingGame

    这个捕鱼游戏挺有意思的,通过发射子弹,打鱼。打鱼的子弹会消耗金币,但是打鱼如果打到了鱼,就会奖励金币的数量。
    我如果写这个的话,应该会画一个 背景海底,然后生成很多鱼的图片,还要有一个大炮,金币。大炮会发射子弹,角度不同发摄子弹的方向不同。
    发射子弹就消耗金币,如果打中鱼了就奖励金币,大炮两边的加号和减号就是控制让子弹连续发射的。不过此时发送炸弹的角度不好控制。
    接下来我们看效果

    先放下作者大大的项目地址:https://github.com/JayHowe/fishingGame
    接下来我们分析代码
    页面初始化绘制图片

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style type="text/css">
        body {
            text-align: center;
            background-color: #000;
        }
    
        #c1 {
             800px;
            height: 600px;
            background: url(img/game_bg_2_hd.jpg) no-repeat;
            margin: 0 auto;
        }
        </style>
        <script type="text/javascript" src="js/common.js"></script>
        <script type="text/javascript" src="js/drawRect.js"></script>
        <script type="text/javascript" src="js/sprite.js"></script>
        <script type="text/javascript" src="js/fish.js"></script>
        <script type="text/javascript" src="js/cannon.js"></script>
        <script type="text/javascript" src="js/button.js"></script>
        <script type="text/javascript" src="js/bullet.js"></script>
        <script type="text/javascript" src="js/coin.js"></script>
        <script>
        window.onload = function() {
            // 绘制背景
            let oC = document.getElementById('c1');
            let gd = oC.getContext('2d');
    
            let lastFire=0;
            let fired=false;
            let MAX_FISH=30;
            const coinCollector={x:106,y:576};
            //游戏总得分
            let playerScore=1000;
    
            const W = oC.width,
                H = oC.height;
                // 加载图片
            loadImages(_resources, function() {
                //炮台
                let tower = new Sprite(new DrawRect(_imgs.bottom, 0, 0, 756, 71, ));
                tower.x = 400;
                tower.y = H - 71 / 2 + 1;
    
                //炮
                let cannon = new Cannon(1);
    
                cannon.x = 443;
                cannon.y = 574;
    
                //炮弹
                let bullets=[];
    
                //鱼
                let fishs=[];
    
                // 金币
                let coins=[];
    
                //分数数字
                let scores=[];
    
                for(let i=0;i<6;i++){
                    let sprite=new Sprite(new DrawRect(_imgs.number,0,9*24,20,24));
                    sprite.x=51+23*i;
                    sprite.y=586;
                    scores.push(sprite);
                };
    
                //鼠标事件
                oC.onmousemove = ev => {
                    let a = ev.offsetX - cannon.x;
                    let b = ev.offsetY - cannon.y;
                    let ang = a2d(Math.atan2(b, a)) + 90;
                    cannon.rotation = ang;
                }
    
                //加号、减号
                let btnMinus = new Button(
                    new DrawRect(_imgs.bottom, 135, 75, 36, 28),
                    new DrawRect(_imgs.bottom, 91, 75, 36, 28)
                );
                btnMinus.x = 371;
                btnMinus.y = 566;
    
                let btnPlus = new Button(
                    new DrawRect(_imgs.bottom, 47, 75, 36, 28),
                    new DrawRect(_imgs.bottom, 3, 75, 36, 28)
                );
                btnPlus.x = 516;
                btnPlus.y = 566;
    
                btnMinus.onclick = function() {
                    if (cannon.type > 1) {
                        cannon.setType(cannon.type - 1);
                    } else {
                        cannon.setType(1);
                    }
                };
                btnPlus.onclick = function() {
                    if (cannon.type < 7) {
                        cannon.setType(cannon.type + 1);
                    } else {
                        cannon.setType(7);
                    }
                };
    
                let aBtn = [btnMinus, btnPlus];
    
                oC.onmousedown = function(ev) {
                    //检测按钮
                    aBtn.forEach(btn => {
                        btn.down(ev.offsetX, ev.offsetY);
                    });
    
                    if(Date.now()-lastFire >= 300) {
                        lastFire=Date.now();
                        //炮弹
                        let bullet=new Bullet(cannon.type, cannon.x,cannon.y,cannon.rotation);
                        bullets.push(bullet);
    
                        playerScore-=cannon.type*2;
    
                        fired=true;
                    }
                    
                };
                oC.onmouseup = function(ev) {
                    aBtn.forEach(btn => {
                        btn.up(ev.offsetX, ev.offsetY);
                    });
                };
    
                function animate() {
                    requestAnimationFrame(animate);
    
                    //生成鱼
                    if(rnd(1,20)==1 && fishs.length<MAX_FISH) {
                        let fish=new Fish(rnd(1,5));
                        if(rnd(0,2)==0) {
                            //左边
                            fish.x=-100;
                            fish.rotation=90;
                        }else {
                            //右边
                            fish.x=W+100;
                            fish.rotation=-90;
                        }
                        fish.y=rnd(0,H-100);
                        fishs.push(fish);
                    }
    
                    gd.clearRect(0, 0, oC.width, oC.height);
    
                    coins=coins.filter(coin=>{
                        coin.move(coinCollector.x,coinCollector.y);
                        coin.nextFrame();
                        coin.draw(gd);
    
                        if(Math.abs(coin.x-coinCollector.x)<5 && Math.abs(coin.y-coinCollector.y)<5) {
                            playerScore+=50;
                            return false;
                        }else {
                            return true;
                        }
                    });
                    tower.draw(gd);
    
                    bullets=bullets.filter(bullet=>{
                        bullet.move();
                        bullet.draw(gd);
                        return !bullet.outRect(-100,-100,W+200,H+200); 
                    });
                    // console.log(bullets.length);
    
                    fishs=fishs.filter(fish=>{
                        fish.move();
                        fish.draw(gd); 
                        fish.nextFrame();
                        return !fish.outRect(-100,-100,W+200,H+200); 
                    });
                    // console.log(fishs.length);
    
    
    
                    cannon.draw(gd);
                    if(fired) {
                        ret=cannon.nextFrame();
                        if(ret) {
                            fired=false;
                        }
                    }
    
                    btnMinus.draw(gd);
                    btnPlus.draw(gd);
    
                    //碰撞
                    fishs=fishs.filter(fish=>{
                        let colled=false;
                        bullets=bullets.filter(bullet=>{
                            if(!colled && fish.collTest(bullet)){ 
                                if(Math.random()<bullet.type*10/(10+(fish.type-1)*20)) {
                                   colled=true;
                                }
                                return false; 
                            }else {
                                return true;
                            }
                        });
                        
                        if(colled) {
                            fish.isdead=true;
                            fish.speed=0;
    
                            setTimeout(function() {
                                //金币
                                let a=fish.x-coinCollector.x;
                                let b=coinCollector.y-fish.y;
    
                                let i=0;
                                let timer=setInterval(function(){
                                    let coin=new Coin(1,fish.x,fish.y);
                                    // coin.x+=rnd(-50,50);
                                    // coin.y+=rnd(-50,50);
                                    coins.push(coin);
                                    i++;
    
                                    if(i==Math.pow(2,fish.type)) {
                                        clearInterval(timer);
                                    }
                                },60);
                                
                                fishs=fishs.filter(item=>item!=fish);
                            },500);
    
                            return true;
                        }else {
                            return true;
                        }
                    });
    
                    //分数
                    let str=playerScore+'';
                    while(str.length<6) {
                       str='0'+str; 
                    }
                    scores.forEach((score,index)=>{
                        playerScore
                        score.setDrawRect(new DrawRect(_imgs.number,0,(9-parseInt(str[index]))*24,20,24));
                        score.draw(gd);
                    });
                }
                requestAnimationFrame(animate);
    
            });
        }
        </script>
    </head>
    <body>
        <canvas id="c1" width="800" height="600"></canvas>
    </body>
    </html>
    
    //bullet
    //绘制子弹形状
    class Bullet extends Sprite{
    	constructor(type,x=0,y=0,rotation=0){
    		const SIZE=[
    			null,
    			new DrawRect(_imgs.bullet,86,0,24,26),
    			new DrawRect(_imgs.bullet,61,0,25,29),
    			new DrawRect(_imgs.bullet,32,36,29,30),
    			new DrawRect(_imgs.bullet,30,82,29,33),
    			new DrawRect(_imgs.bullet,0,82,30,34),
    			new DrawRect(_imgs.bullet,30,0,31,26),
    			new DrawRect(_imgs.bullet,0,44,32,38)
    		];
    
    		super(SIZE[type],x,y,rotation);
    
    		this.type=type;
    		this.speed=5;
    
    		this.radius=14;
    	}
    }
    

    绘制按钮

    //button
    class Button extends Sprite{
      constructor(drawRectNormal, drawRectActive, x=0, y=0, rotation=0){
        super(drawRectNormal, x, y, rotation);
    
        this.drawRectNormal=drawRectNormal;
        this.drawRectActive=drawRectActive;
    
        this.downAtMe=false;
      }
    
      down(x, y){
        if(this.inRect(x, y)){
          this.setDrawRect(this.drawRectActive);
    
          this.downAtMe=true;
        }else{
          this.downAtMe=false;
        }
      }
      up(x, y){
        this.setDrawRect(this.drawRectNormal);
    
        if(this.inRect(x, y) && this.downAtMe){
          //触发onclick
          this.onclick && this.onclick();
        }
      }
    }
    

    绘制大炮

    //cannon.js
    class Cannon extends Sprite {
        constructor(type, x = 0, y = 0, rotation = 0) {
            if (type > 7 || type < 1) {
                throw new Error('unkonw cannon type');
            }
            const SIZE = [
                null,
                { w: 74, h: 74 },
                { w: 74, h: 76 },
                { w: 74, h: 76 },
                { w: 74, h: 83 },
                { w: 74, h: 85 },
                { w: 74, h: 90 },
                { w: 74, h: 94 }
            ];
    
             //父级
            super(
              new DrawRect(_imgs[`cannon${type}`], 0, 0, SIZE[type].w, SIZE[type].h),
              x, y, rotation
            );
    
            this.SIZE=SIZE;
    
            this.setType(type);
    
            this.MAX_FRAME=5;
        }
    
        setType(type){
            this.type = type;
            this.setDrawRect(
              new DrawRect(_imgs[`cannon${type}`], 0, 0, this.SIZE[type].w, this.SIZE[type].h)
            );
        }
    }
    

    金币

    //jscoin.js
    class Coin extends Sprite{
    	constructor(type,x=0,y=0,rotation=0){
    		const SIZE=[
    			null,
    			new DrawRect(_imgs.coin1,0,0,60,60),
    			new DrawRect(_imgs.coin2,0,0,60,60)
    		];
    
    		super(SIZE[type],x,y,rotation);
    
    		this.MAX_FRAME=10;
    		this.speed=10;
    	}
    
    
    }
    

    初始化加载图片

    //common.js
    let _imgs=null;
    
    const _resources={
      fish1: 'img/fish1.png',
      fish2: 'img/fish2.png',
      fish3: 'img/fish3.png',
      fish4: 'img/fish4.png',
      fish5: 'img/fish5.png',
      cannon1: 'img/cannon1.png',
      cannon2: 'img/cannon2.png',
      cannon3: 'img/cannon3.png',
      cannon4: 'img/cannon4.png',
      cannon5: 'img/cannon5.png',
      cannon6: 'img/cannon6.png',
      cannon7: 'img/cannon7.png',
      bottom: 'img/bottom.png',
      bullet: 'img/bullet.png',
      coin1: 'img/coinAni1.png',
      coin2: 'img/coinAni2.png',
      number: 'img/number_black.png',
    };
    
    function loadImages(json, fn){
      let res={};
      let complete=0;
      let total=0;
    
      for(let name in json){
        total++;
    
        let oImg=new Image();
    
        res[name]=oImg;
    
        oImg.onload=function (){
          complete++;
    
          if(complete==total){
            _imgs=res;
            fn();
          }
        };
    
        oImg.onerror=function (){
          alert('图片加载失败'+oImg.src);
        };
    
        oImg.src=json[name];
      }
    }
    
    function d2a(n){
      return n*Math.PI/180;
    }
    function a2d(n){
      return n*180/Math.PI;
    }
    function rnd(n, m){
      return Math.floor(Math.random()*(m-n)+n);
    }
    

    画方形的类

    //jsdrawRect.js
    class DrawRect{
    	constructor(img,sx,sy,sw,sh){
    		if(!img || !sw || !sh) {
    			throw new Error('img and sw and sh is required');
    		}
    		this.img=img;
    		this.sx=sx;
    		this.sy=sy;
    		this.sw=sw;
    		this.sh=sh;
    	}
    }
    

    定义的鱼的类型

    //jsfish.js
    class Fish extends Sprite {
        constructor(type, x = 0, y = 0, rotation = 0) {
            if (type > 5 || type < 1) {
                throw new Error('unkonw fish type');
            }
            const SIZE = [
                null,
                { w: 55, h: 37, r: 12 },
                { w: 78, h: 64, r: 18 },
                { w: 72, h: 56, r: 15 },
                { w: 77, h: 59, r: 15 },
                { w: 107, h: 122, r: 23 }
            ];
            super(new DrawRect(_imgs[`fish${type}`], 0, 0, SIZE[type].w, SIZE[type].h), x, y, rotation);
    
            this.type = type;
            this.curFrame = 0;
            this.MAX_FRAME = 4;
    
            this.speed = rnd(1, 4);
    
            this.frameRate = 5;
    
            this.radius=SIZE[type].r;
    
            //死鱼
            this.isdead=false;
        }
    
        draw(gd) {
            if(this.isdead) {
                this.curFrame+=4;
            }
    
            if (this.rotation == -90) {
                this.scaleY = -1;
            }
    
            this.rotation -= 90;
            super.draw(gd);
            this.rotation += 90;
    
    
            if (this.rotation == -90) {
                this.scaleY = 1;
            }
    
    
            if(this.isdead) {
                this.curFrame-=4;
            }
        }
    }
    

    绘制鱼的类型

    //jssprite.js
    
    class Sprite {
        //w,h,x,y,rotate
        //draw(),碰撞检测()
        constructor(drawRect, x = 0, y = 0, rotation = 0) {
            if (!(drawRect instanceof DrawRect)) {
                throw new Error('img must be DrawRect');
            }
            this.setDrawRect(drawRect);
            this.x = x;
            this.y = y;
            this.rotation = rotation;
    
            this.speed = 0;
    
            //动画
            this.MAX_FRAME=0;
            this.curFrame=0;
    
            this.scaleX=1;
            this.scaleY=1;
    
            this.frameRate=1;
            this.frameRateNow=0;
    
            // 碰撞检测
            this.radius=0;
        }
    
        setDrawRect(drawRect) {
            this.drawRect = drawRect;
            this.width = drawRect.sw;
            this.height = drawRect.sh;
        }
    
        nextFrame(){
            this.frameRateNow++;
            // console.log(this.frameRateNow);
    
            if(this.frameRateNow==this.frameRate) {
                this.frameRateNow=0;
    
                this.curFrame++;
                if(this.curFrame>=this.MAX_FRAME) {
                    this.curFrame=0;
                    return true;
                }
    
                return false;
            }
        }
    
        draw(gd) {
            gd.save();
    
            gd.translate(this.x, this.y);
            gd.rotate(d2a(this.rotation));
            gd.scale(this.scaleX,this.scaleY);
    
            gd.drawImage(
                this.drawRect.img,
                this.drawRect.sx, this.drawRect.sy+this.height*this.curFrame, this.width, this.height,
                -this.width / 2, -this.height / 2, this.width, this.height,
            );
            gd.restore();
        }
    
        inRect(x, y) {
            if (
                this.x - this.width / 2 <= x && x <= this.x + this.width / 2 &&
                this.y - this.height / 2 <= y && y <= this.y + this.height / 2
            ) {
                return true;
            } else {
                return false;
            }
        }
    
        outRect(x,y,w,h){
            if(this.x<x || this.y<y || this.x>x+w || this.y>y+h) {
                return true;
            }else {
                return false;
            }
        }
    
        move(x,y) {
            if(arguments.length == 0) {
                let x_speed = this.speed * Math.sin(d2a(this.rotation));
                let y_speed = this.speed * Math.cos(d2a(this.rotation));
                this.x += x_speed;
                this.y -= y_speed;
            }else {
                this.x+=(x-this.x)/20;
                this.y+=(y-this.y)/20;
            }
            
        }
    
        collTest(other){
            return Math.sqrt(Math.pow(this.x-other.x,2)+Math.pow(this.y-other.y,2))<this.radius+other.radius;
        }
    
    
    }
    

    后记:代码我并没有完全看懂

  • 相关阅读:
    微信支付收款限制
    手机自动化截图调试工具——PhotoShop
    ZipSecureFile$ThresholdInputStream cannot be cast to java.base/java.util.zip.ZipFile$ZipFileInputStream
    [Leetcode题解]605. 种花问题-贪心算法+卫语句重构
    「问题修复」「cargo」warning: spurious network error (2 tries remaining): [6] Couldn't resolve host name (Could not resolve host: crates)
    久坐程序员,简单高效的保命技巧,以及某人久坐的惨样
    [Leetcode题解]2. 两数相加-链表遍历和重构
    Go语言基础知识01-用Go打个招呼
    【Qt Tips】QLineEdit内容过滤之setValidator和setInputMask浅析
    Ubuntu12.10 使用JLink连接开发板用arm-gdb调试ARM程序
  • 原文地址:https://www.cnblogs.com/smart-girl/p/11448026.html
Copyright © 2011-2022 走看看