FlappBird——canvas

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FlappBird</title>
    <style type="text/css">
        html,body{margin:0;padding:0;}
        #canvas{
            display: block;
            margin:0 auto;
            background-color: gainsboro;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script src="script/Background.js"></script>
    <script src="script/Bird.js"></script>
    <script src="script/Land.js"></script>
    <script src="script/Pipe.js"></script>
    <script src="script/SceneManager.js"></script>
    <script src="script/Game.js"></script>
    <script type="text/javascript">
        let game = new Game();
    </script>
</body>
</html>

 

class Background{
    constructor(){
        /** @type {HTMLCanvasElement} */
        this.w = game.allImg["bg_day"].width;
        this.h = game.allImg["bg_day"].height;
        this.x = 0;
        this.speed = 1;
    }
    //更新
    update(){
        this.x -= this.speed;
        this.x <= -this.w ? this.x=0 : null;
    }
    //渲染
    render(){
        game.draw.fillStyle="#4ec0ca";
        game.draw.fillRect(0,0,game.canvas.width,game.canvas.height);
        game.draw.drawImage(game.allImg["bg_day"],this.x,game.canvas.height-this.h);
        game.draw.drawImage(game.allImg["bg_day"],this.x+this.w,game.canvas.height-this.h);
        game.draw.drawImage(game.allImg["bg_day"],this.x+this.w*2,game.canvas.height-this.h);
    }
}

class Land{
    constructor(){
        /** @type {CanvasRenderingContext2D} */
        this.w = game.allImg["land"].width;
        this.h = game.allImg["land"].height;
        this.x = 0;
        this.y = game.canvas.height - this.h;
        this.speed = 1;
    }
    update(){
        this.x -= this.speed;
        this.x < -this.w ? this.x=0 : null
    }
    render(){
        game.draw.drawImage(game.allImg["land"],this.x,this.y)
        game.draw.drawImage(game.allImg["land"],this.x+this.w,this.y)
        game.draw.drawImage(game.allImg["land"],this.x+this.w*2,this.y)
    }
}

class Bird{
    constructor(){
        this.w = game.allImg["bird0_0"].width;
        this.h = game.allImg["bird0_0"].height;
        this.x = game.canvas.width/2 - this.w/2;
        this.y = game.canvas.height * (1-0.618) - 50;
        this.wing = 0;
        this.status = "drop";//up
        this.changY = 0;
        this.rotate = 0;
    }
    update(){
        if(this.status == "drop"){
            this.changY += 0.5;
            this.y += this.changY;
            this.rotate += 0.08;
        }else if(this.status == "up"){
            this.changY -= 0.6;
            this.changY <= 0 ? this.status = "drop" : null;
            this.y -= this.changY;
            this.y <= 0 ? this.y = 0 : null;
            this.wing >= 2 ? this.wing = 0 : (game.frame%4==0 ? this.wing++ : null);
        }
        //34*24,小鸟上下左右四个值
        this.x1 = this.x - 17;
        this.x2 = this.x + 17;
        this.y1 = this.y - 12;
        this.y2 = this.y + 12;
        if(this.y >= game.canvas.height - game.allImg["land"].height){
            // this.y = game.canvas.height - game.allImg["land"].height;
            game.SM.enter(3);
        }
    }
    render(){
        game.draw.save();//包裹一下,只对里面的图起作用
        //下落旋转,更改中心点,坐标改为图片宽高负数的一半
        game.draw.translate(this.x,this.y);
        game.draw.rotate(this.rotate);
        game.draw.drawImage(game.allImg["bird0_" + this.wing],-this.w/2,-this.h/2);
        game.draw.restore();
    }
    fly(){
        this.changY = 6;
        this.rotate = -1.2;
        this.status = "up";
    }
}

class Pipe{
    constructor(){
        this.w = game.allImg['pipe_down'].width;
        this.h = game.allImg['pipe_down'].height;
        this.h1 = Math.round(Math.random()*200+100);//上面管子截取高度
        this.space = 140;//空隙
        this.h2 = game.canvas.height - game.allImg["land"].height - this.h1 - this.space;//下面管子截取高度
        this.x = game.canvas.width;
        this.speed = 1;
        this.done = true;
        //每new一个管子放进数组中
        game.pipeArr.push(this);
    }
    update(){
        this.x -= this.speed;
        //销毁没用的管子
        for(let i=0;i<game.pipeArr.length;i++){
            if(game.pipeArr[i].x <= -this.w){
                game.pipeArr.splice(i,1);
                i--;
            }
        }
        //管子的上下左右
        this.x1 = this.x;
        this.x2 = this.x + this.w;
        this.y1 = this.h1;
        this.y2 = this.h1 + this.space;
        //碰撞检测
        if(game.bird.x2 >= this.x1 && game.bird.x1 <= this.x2 && (game.bird.y1 <= this.y1 || game.bird.y2 >= this.y2)){
            // clearInterval(game.timer)
            game.SM.enter(3);
        }
        if(this.done && game.bird.x1 > this.x2){
            game.score++;
            this.done = false;
        }
    }
    render(){
        game.draw.drawImage(game.allImg["pipe_down"],0,this.h-this.h1,this.w,this.h1,this.x,0,this.w,this.h1);
        game.draw.drawImage(game.allImg["pipe_up"],0,0,this.w,this.h2,this.x,this.h1+this.space,this.w,this.h2);
    }
}

 

class SceneManager{
    constructor(){
        game.bg = new Background();//实例化
        game.land = new Land();
        this.bindEvent();
        
    }
    enter(number){
        switch (number){
            case 0://第1场景
                this.titleW = game.allImg["title"].width;
                this.titleX = game.canvas.width/2 - this.titleW/2;
                this.titleY = -50;
                this.btnX = game.canvas.width/2 - game.allImg["button_play"].width/2;
                this.btnY = game.canvas.height;
                this.birdX = game.canvas.width/2 - game.allImg["bird0_0"].width/2;
                this.birdY = 220;
                this.valve = true;
                this.birdChangeY = 2;
                // game.scene = 0;
                break;
            case 1:
                game.scene = 1;
                this.tutorialW = game.allImg["tutorial"].width;
                this.tutorialX = game.canvas.width/2 - this.tutorialW/2;
                this.tutorialY = 250;
                this.alpha = 1
                this.birdChangeA = 0.03;
                break;
            case 2:
                game.scene = 2;
                game.bird = new Bird();
                game.pipeArr = [];
                break;
            case 3:
                game.scene = 3;
                this.isBoom = false;
                this.index = 0;
                break;
            case 4:
                game.scene = 4;
                let arr = JSON.parse(localStorage.getItem("FB"));
                if(arr.length!=0){
                    if(game.score == 0){
                        this.model = "medals_0";
                    }else{
                        for(let i=0;i<=arr.length;i++){
                            if(!arr[0]&&game.score >= arr[0]){
                                this.best = game.score;
                                arr[0] = game.score;
                                this.model = "medals_1";
                            }else if(!arr[1]&&game.score >= arr[1]){
                                arr[1] = game.score;
                                this.model = "medals_2";
                            }else if(!arr[2]&&game.score >= arr[2]){
                                arr[2] = game.score;
                                this.model = "medals_3";
                            }else{
                                this.model = "medals_0";
                            }
                        }
                    }
                }else{
                    arr[0] = game.score;
                    this.model = "medals_1";
                }
                this.best = arr[0];
                localStorage.setItem("FB",JSON.stringify(arr));
                this.overX = game.canvas.width/2 - game.allImg["game_over"].width/2;
                this.overY = -80;
                this.panelX = game.canvas.width/2 - game.allImg["score_panel"].width/2;
                this.panelY = game.canvas.height;
                break;
        }
    }
    updateAndRender(){
        switch (game.scene){
            case 0://第1场景
                game.bg.render();//渲染
                game.land.render();
                this.titleY >= 160 ? this.titleY = 160 : this.titleY += 5;
                this.btnY <= 400 ? this.btnY = 400 : this.btnY -= 10;
                // if(this.valve){
                //     this.birdY > 350 ? this.valve = false : this.birdY += 3;
                // }else{
                //     this.birdY < 220 ? this.valve = true : this.birdY -= 3;
                // }
                if(this.birdY > 300 || this.birdY < 220){
                    this.birdChangeY *= -1
                }
                this.birdY += this.birdChangeY;
                game.draw.drawImage(game.allImg["title"],this.titleX,this.titleY);
                game.draw.drawImage(game.allImg["button_play"],this.btnX,this.btnY);
                game.draw.drawImage(game.allImg["bird0_0"],this.birdX,this.birdY);
                break;
            case 1:
                game.bg.render();
                game.land.render();
                game.draw.drawImage(game.allImg["bird0_0"],game.canvas.width/2-24,150);
                
                game.draw.save();
                if(this.alpha > 1 || this.alpha < 0.3){
                    this.birdChangeA *= -1
                }
                this.alpha += this.birdChangeA;
                game.draw.globalAlpha = this.alpha;
                game.draw.drawImage(game.allImg["tutorial"],this.tutorialX,this.tutorialY);
                game.draw.restore();

                break;
            case 2:
                game.bg.update();
                game.bg.render();
                game.land.update();
                game.land.render();
                if(game.frame%180 == 0){
                    new Pipe();
                }
                game.pipeArr.forEach((item)=>{
                    item.update();
                    item.render();
                })
                game.bird.update();
                game.bird.render();
                this.scoreRender();
                break;
            case 3:
                game.bg.render();
                game.land.render();
                game.pipeArr.forEach((item)=>{
                    item.render();
                })
                if(this.isBoom){
                    this.index++;
                    if(this.index > 8){
                        this.enter(4);
                        return;
                    }
                    game.draw.drawImage(game.allImg["baozha" + this.index],game.bird.x-50,game.bird.y-50,100,100)
                }else{
                    game.bird.y += 5;
                    if(game.bird.y >= game.canvas.height - game.allImg["land"].height) this.isBoom = true;
                    game.bird.render();
                }
                break;
            case 4:
                game.bg.render();
                game.land.render();
                this.overY >= 160 ? this.overY = 160 : this.overY+=5;
                this.panelY <= 250 ? this.panelY = 250 : this.panelY-=10;
                game.draw.drawImage(game.allImg["game_over"],this.overX,this.overY);
                game.draw.drawImage(game.allImg["score_panel"],this.panelX,this.panelY);
                game.draw.drawImage(game.allImg[this.model],this.panelX+32,this.panelY+44);
                game.draw.fillStyle = "#666";
                game.draw.font = "20px consolas";
                game.draw.textAlign = "right";
                game.draw.fillText(game.score,(game.canvas.width/2)+90,this.panelY+50);
                game.draw.fillText(this.best,(game.canvas.width/2)+90,this.panelY+96);
                break;
        }
    }
    bindEvent(){
        game.canvas.onclick = (e)=>{
            switch (game.scene){
                case 0://第1场景
                    if(e.offsetX >= this.btnX && (e.offsetX <= this.btnX + 116) && e.offsetY >= this.btnY && (e.offsetY <= this.btnY + 70)){
                        this.enter(1);
                    }
                    break;
                case 1:
                    this.enter(2);
                    break;
                case 2:
                    game.bird.fly();
                    break;
                case 3:
                    break;
                case 4:
                    this.enter(1);
                    break;
            }
        }
    }
    scoreRender(){
        let str = game.score.toString();
        let line = game.canvas.width/2 - str.length*30/2;
        for(let i=0;i<str.length;i++){
            game.draw.drawImage(game.allImg["shuzi" + str[i]],i*30+line,100)
        }
    }
}

 

class Game{
    constructor(){
        /** @type {HTMLCanvasElement} */
        this.canvas = document.getElementById("canvas");
        //创建一个绘制环境
        this.draw = this.canvas.getContext("2d");
        let W = document.documentElement.clientWidth > 420 ? 420 : document.documentElement.clientWidth;
        let H = document.documentElement.clientHeight > 750 ? 750 : document.documentElement.clientHeight;
        this.canvas.width = W;
        this.canvas.height = H;
        this.timer = null;
        this.frame = 0;
        this.score = 0;//分数
        this.scene = 0;//记录场景编号
        if(!localStorage.getItem("FB")){
            localStorage.setItem("FB","[]")
        }
        this.imgLoad();
        // this.bindEvent();
    };
    //清屏
    clear(){
        this.draw.clearRect(0,0,this.canvas.width,this.canvas.height);
    };
    //开始
    start(){
        this.SM = new SceneManager();
        this.SM.enter(this.scene)
        /*
        this.bg = new Background();//实例化
        this.land = new Land();
        this.bird = new Bird();
        this.pipeArr = [];
        */
        this.timer = setInterval(()=>{
            this.frame++;
            this.clear();
            this.SM.updateAndRender();
            /*
            this.clear();
            this.bg.update();//更新
            this.bg.render();//渲染
            this.land.update();
            this.land.render();
            if(this.frame%180 == 0){
                new Pipe();
            }
            //将数组中存放的管子更新渲染出来
            this.pipeArr.forEach((item)=>{
                item.update();
                item.render();
            })
            this.bird.update();
            this.bird.render();
            */
        },20)
    };
    imgLoad(){
        this.allImg={
            "bg_day":"images/bg_day.png",
            "bg_night":"images/bg_night.png",
            "land":"images/land.png",
            "pipe_down":"images/pipe_down.png",
            "pipe_up":"images/pipe_up.png",
            "bird0_0":"images/bird0_0.png",
            "bird0_1":"images/bird0_1.png",
            "bird0_2":"images/bird0_2.png",
            "title":"images/title.png",
            "button_play":"images/button_play.png",
            "tutorial":"images/tutorial.png",
            "number_score_00":"images/number_score_00.png",
            "number_score_01":"images/number_score_01.png",
            "number_score_02":"images/number_score_02.png",
            "number_score_03":"images/number_score_03.png",
            "number_score_04":"images/number_score_04.png",
            "number_score_05":"images/number_score_05.png",
            "number_score_06":"images/number_score_06.png",
            "number_score_07":"images/number_score_07.png",
            "number_score_08":"images/number_score_08.png",
            "number_score_09":"images/number_score_09.png",
            "blink_00":"images/blink_00.png",
            "blink_01":"images/blink_01.png",
            "blink_02":"images/blink_02.png",
            "medals_0":"images/medals_0.png",
            "medals_1":"images/medals_1.png",
            "medals_2":"images/medals_2.png",
            "medals_3":"images/medals_3.png",
            "game_over":"images/text_game_over.png",
            "score_panel":"images/score_panel.png",
            "shuzi0":"images/font_048.png",
            "shuzi1":"images/font_049.png",
            "shuzi2":"images/font_050.png",
            "shuzi3":"images/font_051.png",
            "shuzi4":"images/font_052.png",
            "shuzi5":"images/font_053.png",
            "shuzi6":"images/font_054.png",
            "shuzi7":"images/font_055.png",
            "shuzi8":"images/font_056.png",
            "shuzi9":"images/font_057.png",
            "baozha1":"images/Bomb1.png",
            "baozha2":"images/Bomb2.png",
            "baozha3":"images/Bomb3.png",
            "baozha4":"images/Bomb4.png",
            "baozha5":"images/Bomb5.png",
            "baozha6":"images/Bomb6.png",
            "baozha7":"images/Bomb7.png",
            "baozha8":"images/Bomb8.png",
          };
        let count = 0,total = Object.keys(this.allImg).length;
        for(let key in this.allImg){
            let img = new Image();
            img.src = this.allImg[key];
            img.onload = ()=>{
                this.allImg[key] = img;
                count++;
                if(count>=total) this.start();
            }
        }
    };
    /*
    bindEvent(){
        this.canvas.onclick = ()=>{
            this.bird.fly();
        }
    }
    */
}

 

posted @ 2024-12-13 10:34  石头记1  阅读(11)  评论(0)    收藏  举报