【新年第一篇博客】自己完成的第一款游戏 FlappyBird

本文详细解析了热门游戏FlappyBird的内部构造,包括游戏的基本框架、各场景的功能与实现细节,以及游戏核心机制如小鸟飞行、碰撞检测等。文章还分享了使用Cocos游戏引擎复刻此游戏的经验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

  • 游戏简介

《flappy bird》是一款由来自越南的独立游戏开发者Dong Nguyen所开发的作品,游戏于2013年5月24日上线,并在2014年2月突然暴红。2014年2月,《Flappy Bird》被开发者本人从苹果及谷歌应用商店撤下。2014年8月份正式回归APP STORE,正式加Flappy迷们期待已久的多人对战模式。游戏中玩家必须控制一只小鸟,跨越由各种不同长度水管所组成的障碍。我通过用cocos游戏引擎实现该游戏的编写,这也是我第一次使用cocos游戏引擎,如果有的地方编辑不当,请见谅!


  • 游戏基本框架截图

  • Game

  • Menu

  • GameOver


  • 游戏主要场景简单介绍

  1. Menu:控制游戏登录界面,点击按钮进入游戏,控制包括背景滚动和场景切换

  2. Game:控制游戏的具体内容,控制包括背景滚动、钢管类及其控制类、小鸟的操作及其形态的变化、碰撞域设置和对本局得分的统计显示以及最高分的显示

  3. GameOver:游戏结束界面,包括对当局游戏得分的统计以及对改游戏最高分的统计


  • Menu场景的详细介绍

    • bgN节点:控制背景滚动

    • game_name节点:游戏名称图片插入【tap节点同理:游戏背景装饰】

    • floorN节点:控制地板滚动

    •  btnStart节点:开始按钮节点


  • Game场景的详细介绍 

    • bgN节点:控制背景滚动

    • pipeMgr:钢管控制类节点

    • floorN:地板滚动节点

    • bird3节点:小鸟节点

    • touchN节点:抓取区域节点

    • pipe节点:钢管节点

    • botton_pipe节点:钢管下部分区域【碰撞域】

    • top_pipe节点:钢管上部分区域【碰撞域】

    • passN节点:小鸟通过碰撞域

    • floorCol节点:地板碰撞域

    • scoreL节点:当前得分

    • scoreMaxL节点:游戏最高分

       

       


  • GameOver场景的详细介绍  

    • bgN节点:滚动背景节点

    • 图片节点:三个

    • scoreL节点:当前游戏得分

    • scoreMaxL节点:该游戏最高分

    • rGame节点:重新开始新的一轮游戏


  •  anims:小鸟形态

    • drop:小鸟下降状态
    • rise:小鸟上升状态

  • 游戏中的所有script: 

    • Game:

      • cc.Class({
            extends: cc.Component,
        
            properties: {
                touchN:cc.Node,
                //playerN:cc.Node
                labelMaxScore:cc.Label
            },
        
            // LIFE-CYCLE CALLBACKS:
        
             onLoad () {
        
        /*
                //全局区域分数
                window.score=0;
                //或者cc.score=0
                cc.maxScore=0
        */
                cc.gameDt={};
                cc.gameDt.score=0
                cc.gameDt.maxScore=0;
                let maxScore=cc.sys.localStorage.getItem('maxScore');
                if(!maxScore){
                    cc.sys.localStorage.setItem('maxScore',0);
                }else{
                    cc.gameDt.maxScore=maxScore;
                    this.labelMaxScore.string='当前最高分:'+maxScore+'分';
                }
             
        
        
        
                let manager=cc.director.getCollisionManager();
                 manager.enabled=true;
        
        
                /* manager.enabledDebugDraw=true;
                 manager.enabledDrawBoundingBox=true;*/
        
                 
               // let playerJs=this.playerN.getComponent('Player');
                 // this.canMove=false;
                 //this.playerJs=this.playerN.getComponent('Player');//通过节点名取组件
                //this.node.on(cc.Node.EventType.TOUCH_START,this.onTouchStart, this);
                 //this.node.on(cc.Node.EventType.TOUCH_MOVE,this.onTouchMove, this);
                 //this.node.on(cc.Node.EventType.TOUCH_END,this.onTouchEnd, this);
                    //1、参数1:事件类型
                    //2、参数2:回调函数
                    //3、参数3
                    this.playerN=cc.find('Canvas/bird3');
                    let testN = cc.find('Canvas/test');
                    this.touchN.on(cc.Node.EventType.TOUCH_START,function(event){
                    let playerJs=this.playerN.getComponent('Player');
                   //let playerJs=this.playerN.getComponent('bird');
                    //playerJs.speed=playerJs.riseSpeed;
                    playerJs.rise();
                   // playerJs.changeState(1);
                   //playerJs.changeState(STATE.STATE_RISE);
                 },this);
                
             },
            //  onTouchStart(event){
            //      let worldPos=event.getLocation();
            //     let pos=this.playerN.parent.convertToNodeSpaceAR(worldPos);
            //      let rect=this.playerN.getBoundingBox();//获取节点作用范围的包围盒
            //      if(rect.contains(pos)){
            //        //说明鼠标在玩家包围盒内
            //         this.canMove=true;
            //      }
        
            //     this.playerJs.rise();
            //  },
        //    onTouchMove(event){
        //          if(this.canMove){
        //             let worldPos=event.getLocation();
        //             let pos=this.playerN.parent.convertToNodeSpaceAR(worldPos);//将后者坐标相对前者坐标
        //             this.playerN.x=pos.x;
        //             this.playerN.y=pos.y;
        //          }
                    
        //      },
        //      onTouchEnd(event){
        //             this.canMove=false;
        //      },
        
            start () {
        
            },
        
            // update (dt) {},
        });
        

         

    • GameOver:

      • 
        cc.Class({
            extends: cc.Component,
        
            properties: {
                scoreL:cc.Label,
                scoreMaxL:cc.Label
            },
        
            // LIFE-CYCLE CALLBACKS:
        
            // onLoad () {},
        
            start () {
                let scoreSave=cc.sys.localStorage.getItem('scoreSave');
                this.scoreL.string=scoreSave+'分';
                let maxScore=cc.sys.localStorage.getItem('maxScore');
                this.scoreMaxL.string=maxScore+'分';
            },
            onBtnRStart(){
                cc.director.loadScene('Menu');
            }
            // update (dt) {},
        });
        

         

    • Menu:

      • 
        cc.Class({
            extends: cc.Component,
        
            properties: {
                
                // bird:{
                //     type:cc.Node,
                //     default:null
                // },
                // speed:0,
                // speed_x:0,
                // accel:0
            },
        
            // LIFE-CYCLE CALLBACKS:
            //当前脚本所挂载的节点被加载时引擎会调用该脚本的onLoad函数,一般用于初始化操作
            onLoad () {
                console.log('Menu is load!');
            },
        
            start () {
                console.log('Menu is start!');
            },
            onBtnStart(){
                console.log('开始按钮被点击了!');
                cc.sys.localStorage.setItem('scoreSave',0);
                cc.director.loadScene('Game');
            },
        
            update (dt) {
                //console.log('Menu is update!');
                //拿到bird节点
                //this.bird.y--;
                //this.bird.y = this.bird.y + this.speed*dt;
        
                // this.speed += this.accel*dt;
                // this.bird.y += this.speed*dt;
                // this.bird.x=this.bird.x+this.speed_x*dt;
        
        
                
            },
            // lateUpdate(dt){
            //     console.log('Menu is late update!');
            // }
        });
        

         

    • Pipe:

      • 
        
        cc.Class({
            extends: cc.Component,
        
            properties: {
               topPipe:cc.Node,
               bottomPipe:cc.Node,
               minBottomY:0,
               maxBottomY:0,
               minSpace:300,
               maxSpace:0
        
            },
        
        
            onLoad () {
                //下方水管,y轴取-450-200的随机整数
                this.bottomPipe.y=Math.floor(Math.random()*
                (this.maxBottomY-this.minBottomY)+this.minBottomY);
        
                //随机间隔200-300
                let space=Math.floor(Math.random()*
                (this.maxSpace+this.minSpace)+this.minSpace);
        
                this.topPipe.y=this.bottomPipe.y+space;
            },
        
            start () {
        
            },
        
            // update (dt) {},
        });
        

         

    • PipeMgr:

      • 
        cc.Class({
            extends: cc.Component,
        
            properties: {
               pipePrefab:cc.Prefab
            },
        
        
             onLoad () {
                 //定时器 this.schedule(回调函数,时间间隔)
                 //设计一个随机数2-7
                 //let pipeWidth=Math.floor(Math.random()*5+2);
        
                 //根据分数来获取间隔
                 //1-10分:6
                 //11-20分: 5
                 //20分-:   3
        
                //     if(scoreSave<5){
                //         pipeWidth=6;
                //     }else if(scoreSave<10){
                //         pipeWidth=5;
                //     }else{
                //         pipeWidth=3;
                //     }
                //  let pipeWidth=scoreSave
        
                let pipeWidth=Math.floor(Math.random()*5+2);
                cc.sys.localStorage.setItem('widthP',pipeWidth);
                let widthP=cc.sys.localStorage.getItem('widthP');
                this.schedule(this.addPipe,widthP);
             },
        
            start () {
        
            },
            addPipe(){
                //创建钢管
                //根据预制体prefabs创建模板
                let pipeN=cc.instantiate(this.pipePrefab);
                pipeN.x=cc.winSize.width+100;//获取屏幕宽度 从外面运动进来
                //1、this.node.addChild(pipe);
                //2.
                pipeN.parent=this.node;
        
                //动作系统
                //cc.moveTo(时间,目的点);
                let moveTo=cc.moveTo(8,cc.v2(-100,pipeN.y));
                //let movBy=cc.moveBy(cc.v2(-(cc.winSize.width+200),0));
                let removeSelf=cc.removeSelf();
                //pipeN.runAction(moveTo);
                //pipeN.runAction(removeSelf);
                let seq=cc.sequence(moveTo,removeSelf);//串行序列动作 顺序进行
                //moveBy移动了多少距离
                //moveBy移动到什么位置
                pipeN.runAction(seq);
                let pipeWidth=Math.floor(Math.random()*5+2);
                this.unschedule(this.addPipe);
                this.schedule(this.addPipe,pipeWidth);
                //cc.sys.localStorage.setItem('widthP',pipeWidth);
            },
            update (dt) {
                //取到所有钢管
                // let children=this.node.children;
                // for(let i=0;i<children.length;++i){
                //     children[i].x += (-200)*dt;
                // }
                // for(let i=0;i<children.length;++i){
                //     if(children[i].x<-100){
                //         children[i].destroy();
                //     }
                // }
            },
        });
        

         

    • Player:

      • //枚举表示状态
        let STATE=cc.Enum({
            STATE_NONE:-1,//-1从0开始代表默认值
            STATE_RISE:-1,
            STATE_DROP:-1
        })
        //默认第一个为0,后一个比第一个多1
        cc.Class({
            extends: cc.Component,
        
            properties: {
                accel:0,
                speed:0,
                riseSpeed:300,
                scoreL:cc.Label,
                scoreMaxL:cc.Label,
                // audioSource: {
                //     type: cc.AudioSource,
                //     default: null
                // },
                // play: function () {
                //     this.audioSource.play();
                // },
                // pause: function () {
                //     this.audioSource.pause();
                // },
            },
        
            // LIFE-CYCLE CALLBACKS:
        
            onLoad () {
                //this.state=0;//0代表初始状态 1代表上升 2代表下降
                this.state=STATE.STATE_NONE;
                this.anim=this.node.getComponent(cc.Animation);
            },
        
            start () {
        
            },
            rise(){
                //this.anim.play('rise');
                this.speed=this.riseSpeed;
                this.changeState(STATE.STATE_RISE);
            },    
            update (dt) {
                //速度=当前速度+加速度*时间
                this.speed+=this.accel*dt;
                this.node.y+=this.speed*dt;
                if(this.speed<=0){
                    //this.changeState(2);
                    this.changeState(STATE.STATE_DROP);
                }
            },
            //简答到状态机
            changeState(state){
                if(this.state===state){
                    return;
                }
                this.state=state;
                this.anim.stop();//停止播放动作
                //if(this.state===1){
                if(this.state===STATE.STATE_RISE){
                    this.anim.play('rise');
                    this.node.rotation=0;
                    //this.speed=this.riseSpeed;
                }else if(this.state===STATE.STATE_DROP){
                    //头朝下
                    this.anim.play('drop');
                }
            },
            onCollisionEnter(other,self){
               // console.log('下啊啊啊');
               // other.node.destroy();
               if(other.tag===1){
                   console.log('钢管碰到');
                   //other.destroy();
                   //调用游戏结束界面
                   cc.director.loadScene('GameOver');
               }else if(other.tag===2){
                   console.log('地板碰到');
                   //调用游戏结束界面
                   cc.director.loadScene('GameOver');
               }else if(other.tag===3){
                console.log('通过');
                   //得分
                   cc.gameDt.score++;
                   this.scoreL.string='当前得分:'+cc.gameDt.score+'分';
                   let scoreSave=cc.sys.localStorage.setItem('scoreSave',cc.gameDt.score);
        
                   if(cc.gameDt.score>cc.gameDt.maxScore){
                   
                       //cc.gameDt.loadScene.setItem('maxScore',cc.gameDt.maxScore);
                       cc.gameDt.maxScore=cc.gameDt.score;
                       this.scoreMaxL.string='当前最高分:'+cc.gameDt.maxScore+'分';
                       cc.sys.localStorage.setItem('maxScore',cc.gameDt.maxScore);
                   }
            }
            
            }
        });
        

         

    • Scorell:

      • 
        cc.Class({
            extends: cc.Component,
        
            properties: {
               scrollN1:cc.Node,
               scrollN2:cc.Node,
               speed:0
            },
            start () {
        
            },
        
            update (dt) {
                this.scrollN1.x+=this.speed*dt;
                this.scrollN2.x+=this.speed*dt;
                if(this.scrollN1.x<=-this.scrollN1.width){
                    this.scrollN1.x=this.scrollN2.x+this.scrollN2.width;
                }
                if(this.scrollN2.x<=-this.scrollN2.width){
                    this.scrollN2.x=this.scrollN1.x+this.scrollN1.width;
                }
            },
        });
        

         

游戏链接:https://pan.baidu.com/s/15nWxE0ZYVVc3vQ4mcbFZKA        提取码:hidu 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值