首页 > 代码库 > libgdx自制简易Flappy Bird

libgdx自制简易Flappy Bird

Flappy Bird,好吧,无需多说。今天年初不知咋的,一下子就火了,而且直接跃居榜首,在ios和android平台都是如此,实在难以理解。传说其作者每天收入能达到5w刀,着实碉堡了。。。

好吧,咱没创意,不过山寨一个还是可以的,话说!!!

好了,不罗嗦了,直接代码了。

我使用libgdx框架(你要说是引擎也行)实现的,版本为0.9.9。就设计了一个开始画面和一个游戏画面。

游戏入口和主类:

package com.fxb.flappy;

import com.badlogic.gdx.Game;

public class FlappyBird extends Game{

    //static Skin skin;
    StartScreen startScreen;
    GameScreen gameScreen;
    LevelScreen levelScreen;
    
    @Override
    public void create() {
        // TODO Auto-generated method stub
        
        Assets.init();
        
        startScreen = new StartScreen(this);
        gameScreen = new GameScreen(this);
        levelScreen = new LevelScreen(this);
        
        setScreen( startScreen );
    }

    @Override
    public void render() {
        // TODO Auto-generated method stub
        super.render();
    }
    
    @Override
    public void dispose() {
        // TODO Auto-generated method stub
        //skin.dispose();
        Assets.clean();
        super.dispose();
    }

}

开始画面:

package com.fxb.flappy;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;

public class StartScreen extends ScreenAdapter{
    
    FlappyBird game;
    Stage stage;
    TextButton btnStart, btnMode;
    
    public StartScreen(FlappyBird game0){
        game = game0;
        stage = new Stage();
        
        btnStart = new TextButton( "Start Game", Assets.skin, "default" );
        btnStart.setSize( 150, 70 );
        Constant.CenterInStage( btnStart, stage );
        stage.addActor( btnStart );
        
        btnStart.addListener(new InputListener(){
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                // TODO Auto-generated method stub
                return true;
            }
            @Override
            public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
                // TODO Auto-generated method stub
                game.setScreen( game.gameScreen );
                Constant.state = Constant.GameState.game_on;
            }            
        });
        
    }

    @Override
    public void show() {
        // TODO Auto-generated method stub
        Gdx.input.setInputProcessor( stage );
    }
    
    @Override
    public void render(float delta) {
        // TODO Auto-generated method stub
        Gdx.gl.glClearColor( 0, 1, 1, 1 );
        Gdx.gl.glClear( GL10.GL_COLOR_BUFFER_BIT );
        
        stage.act();
        stage.draw();        
    }

    @Override
    public void dispose() {
        // TODO Auto-generated method stub
        stage.dispose();
        super.dispose();
    }
    
}

游戏场景:

package com.fxb.flappy;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL10;

public class GameScreen extends ScreenAdapter{
    
    FlappyBird game;
    GameStage gameStage;
    OverStage overStage;
    
    
    public GameScreen(FlappyBird game0){
        game = game0;
        gameStage = new GameStage(this);
        overStage = new OverStage(this);
    }

    @Override
    public void show() {
        // TODO Auto-generated method stub
        Gdx.input.setInputProcessor( gameStage );
    }
    
    @Override
    public void render(float delta) {
        // TODO Auto-generated method stub
        Gdx.gl.glClearColor( 0, 1, 1, 1 );
        Gdx.gl.glClear( GL10.GL_COLOR_BUFFER_BIT );
        
        if( Constant.state == Constant.GameState.game_on ){
            gameStage.act();
            gameStage.draw();    
            Gdx.input.setInputProcessor( gameStage );
        }
        else if( Constant.state == Constant.GameState.game_preover ){
            gameStage.draw();
            overStage.act();
            overStage.draw();
            Gdx.input.setInputProcessor( overStage );
        }

    }

    @Override
    public void dispose() {
        // TODO Auto-generated method stub
        super.dispose();
    }
    
}

游戏舞台:

package com.fxb.flappy;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.Pools;

public class GameStage extends Stage{

    GameScreen gameScreen;
    Group groupUp, groupDown;
    Pool<PipeUp> poolUp;
    Pool<PipeDown> poolDown;
    Bird bird;
    Image imgBack;
    OrthographicCamera camera;
    
    int xIndex;
    float leftX = 0;
    float speed;
    float currentTime;
    float lastTouchTime;
    
    public GameStage( GameScreen gameScreen0 ){
        gameScreen = gameScreen0;
        
        groupUp = new Group();
        groupDown = new Group();
        
        poolUp = Pools.get( PipeUp.class );
        poolDown = Pools.get( PipeDown.class );
        
        bird = new Bird();
        imgBack = Constant.CreateImage( Assets.regionBack );
        camera = new OrthographicCamera( Assets.regionBack.getRegionWidth(), Assets.regionBack.getRegionHeight() );
        camera.position.set( Assets.regionBack.getRegionWidth()/2, Assets.regionBack.getRegionHeight()/2, 0 );
        
        //addActor( imgBack );
        addActor( bird );
        addActor( groupUp );
        addActor( groupDown );
                        
        this.addListener(new InputListener(){
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                // TODO Auto-generated method stub
                if( currentTime - lastTouchTime > 0.15f ){
                    if( bird.speedY < 0 ){
                        bird.speedY += 15;                        
                    }
                    else if( bird.speedY < 5 ){
                        bird.speedY += 15;
                    }
                    else if( bird.speedY < 8 ){
                        bird.speedY += 8;
                    }
                    else{
                        bird.speedY += 4;
                    }

                    if( bird.speedY > 25 ){
                        bird.speedY = 25;
                    }
                    lastTouchTime = currentTime;
                }
                return true;
            }        
        });
        
        
        Clear();        
    }
    
    public void Clear(){
        xIndex = 3;
        //this.camera.translate( -leftX, 0 );
        //this.camera.position.set( getWidth()/2, getHeight()/2, 0 );
        this.getCamera().translate( -leftX, 0, 0 );
        leftX = 0;
        
        speed = 2f;
        currentTime = 0;
        lastTouchTime = -1f;
        
        bird.Clear();
        
        groupUp.clear();
        groupDown.clear();
        poolUp.clear();
        poolDown.clear();
        
        for( int i=0; i<xIndex; ++i ){
            AddPipe(i);            
        }
            
        Gdx.input.setInputProcessor(this);
    }
    
    public void AddPipe( int xIndex0 ){
        PipeUp pipeup = poolUp.obtain();        
        PipeDown pipedown = poolDown.obtain();        
                
        groupUp.addActor( pipeup );
        groupDown.addActor( pipedown );
        
        float rate1 = MathUtils.random( 0.2f, 1.3f );
        float rate2 = 1.5f - rate1;
        
        //rate1 = 0.8f;
        //rate2 = 0.2f;
        
        int height1 = (int)( Assets.regionPipeUp.getRegionHeight()*rate1 );
        int height2 = (int)( Assets.regionPipeDown.getRegionHeight()*rate2 );
        
        if( rate1 < 1 ){
            pipeup.region = new TextureRegion( Assets.regionPipeUp, 0, Assets.regionPipeUp.getRegionHeight()-height1, Assets.regionPipeUp.getRegionWidth(), height1 );
            pipeup.setHeight( height1 );
        }
        else{
            pipeup.region = Assets.regionPipeUp;
            pipeup.setHeight( height1 );
        }
        
        if( rate2 < 1 ){
            pipedown.region = new TextureRegion( Assets.regionPipeDown, 0, 0, Assets.regionPipeDown.getRegionWidth(), height2 );
            pipedown.setHeight( height2 );
        }
        else{
            pipedown.region = Assets.regionPipeDown;
            pipedown.setHeight( height2 );
        }
        
        //pipedown        
        pipeup.setPosition( getWidth()+xIndex0*300, getHeight()-pipeup.getHeight() );
        pipedown.setPosition( getWidth()+xIndex0*300, 0 );
    }
        
    @Override
    public void act() {
        // TODO Auto-generated method stub
        
        leftX += speed;
        this.getCamera().translate( speed, 0, 0 );
        bird.translate( speed, bird.speedY );
        
        currentTime += Gdx.graphics.getDeltaTime();
        
        boolean isDis = false;
        for( int i=0; i<groupUp.getChildren().size; ++i ){
            PipeUp pipeup = (PipeUp)groupUp.getChildren().items[i];
            if( pipeup.getRight() < leftX ){
                groupUp.removeActor( pipeup );
                poolUp.free( pipeup );
                if( !isDis ){
                    isDis = true;                    
                }
            }
            
            if( Constant.IsCollsion( pipeup, bird )  ){
                //Gdx.app.exit();
                Constant.state = Constant.GameState.game_preover;
            }
        }

        for( int i=0; i<groupDown.getChildren().size; ++i ){
            PipeDown pipedown = (PipeDown)groupDown.getChildren().items[i];
            if( pipedown.getRight() < leftX ){
                groupDown.removeActor( pipedown );
                poolDown.free( pipedown );
                if( !isDis ){
                    isDis = true;                    
                }
            }
            
            if( Constant.IsCollsion( pipedown, bird )  ){
                //Gdx.app.exit();
                Constant.state = Constant.GameState.game_preover;
            }
        }
        
        if( isDis ){
            AddPipe( xIndex );
            xIndex++;
        }
        
        if( bird.getY() < 0 ){
            Constant.state = Constant.GameState.game_preover;
        }
        
        
        super.act();
    }
    
    @Override
    public void draw() {
        // TODO Auto-generated method stub    
        camera.update();
        
        SpriteBatch batch = this.getSpriteBatch();
        batch.setProjectionMatrix(camera.combined);
        batch.begin();
        batch.draw( Assets.regionBack, 0, 0 );
        batch.end();
            
        super.draw();
    }

    @Override
    public void dispose() {
        // TODO Auto-generated method stub
        super.dispose();
    }
    
}

游戏结束舞台:

package com.fxb.flappy;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.Window;

public class OverStage extends Stage{
    
    Window window;
    TextButton button1;
    TextButton button2;
    Label label;
    GameScreen gameScreen;
    
    public OverStage(GameScreen gameScreen0){
        gameScreen = gameScreen0;
        
        window = new Window( "", Assets.skin );
        //window.add( "GAME OVER\n" );
        
        label = new Label( "GAME OVER", Assets.skin );
        //label.setFontScale( 1.0f );
        
        button1 = new TextButton( "Again", Assets.skin );
        button2 = new TextButton( "Exit", Assets.skin );
        
        
        button1.addListener( new InputListener(){
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                // TODO Auto-generated method stub
                return true;
            }

            @Override
            public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
                // TODO Auto-generated method stub
                Constant.state = Constant.GameState.game_on;
                gameScreen.gameStage.Clear();
            }        
        });
        
        button2.addListener( new InputListener(){
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                // TODO Auto-generated method stub
                return true;
            }

            @Override
            public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
                // TODO Auto-generated method stub
                //Constant.state = Constant.GameState.game_on;
                Gdx.app.exit();
            }        
        });
        
        window.setSize( getWidth()/2, getHeight()/6 );
        window.addActor( button1 );
        window.addActor( button2 );
        window.addActor( label );
        
        button1.setSize( 60, 30 );
        button2.setSize( 60, 30 );
        label.setSize( 100, 50 );
        button1.setPosition( window.getWidth()/8, 10 );
        button2.setPosition( window.getWidth()*7/8-button2.getWidth(), 10 );
        label.setPosition( window.getWidth()/2-label.getWidth()/2, button2.getTop()+20 );
        
        this.addActor( window );
        Constant.CenterInStage( window, this );
    }
    
    

}

游戏主角类,即那只鸟

package com.fxb.flappy;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;

public class Bird extends Actor{

    Animation animation;
    float currentTime;
    float speedY;
    
    public Bird(){
        animation = new Animation( 0.15f, Assets.sRegionBird );
        setSize( Assets.sRegionBird[0].getRegionWidth(), Assets.sRegionBird[0].getRegionHeight() );
        setOrigin( getWidth()/2, getHeight()/2 );
        //setPosition(  );
        Clear();
    }
    
    public void Clear(){
        currentTime = 0;
        setPosition( 100, Gdx.graphics.getHeight()/2-getHeight()/2 );
        speedY = Constant.speedY;
    }
    
    @Override
    public void act(float delta) {
        // TODO Auto-generated method stub        
        currentTime += delta;
        
        //this.translate( 0, speedY  );
        //speedY -= 0.5f;
        
        if( speedY > -10f ){
            speedY += Constant.speedInc;
        }
        
        super.act(delta);
    }
    
    @Override
    public void draw(SpriteBatch batch, float parentAlpha) {
        // TODO Auto-generated method stub
        
        TextureRegion keyFrame = animation.getKeyFrame( currentTime, true );
        
        batch.draw( keyFrame, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation() );
        
        super.draw(batch, parentAlpha);
    }

}

上面管道类

package com.fxb.flappy;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;

public class PipeUp extends Actor{
    
    TextureRegion region;
    
    public PipeUp(){
        region = new TextureRegion( Assets.regionPipeUp );
        setSize( region.getRegionWidth(), region.getRegionHeight() );
    }
    
    /*
    public UpdateRegion(){
        region = new TextureRegion( Assets.regionPipeUp );
    }
    */
    @Override
    public void act(float delta) {
        // TODO Auto-generated method stub
        super.act(delta);
    }
    
    
    @Override
    public void draw(SpriteBatch batch, float parentAlpha) {
        // TODO Auto-generated method stub
        
        //batch.draw( region, region.getRegionX(), region.getRegionY(), region.getRegionWidth(), region.getRegionHeight() );
        batch.draw( region, getX(), getY(), getWidth(), getHeight() );
        
        super.draw(batch, parentAlpha);
    }
}

下面管道类:

 1 package com.fxb.flappy;
 2 
 3 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 4 import com.badlogic.gdx.graphics.g2d.TextureRegion;
 5 import com.badlogic.gdx.scenes.scene2d.Actor;
 6 
 7 public class PipeDown extends Actor{
 8 
 9     TextureRegion region;
10     
11     public PipeDown(){
12         region = new TextureRegion( Assets.regionPipeDown );
13         setSize( region.getRegionWidth(), region.getRegionHeight() );
14     }
15     
16     @Override
17     public void act(float delta) {
18         // TODO Auto-generated method stub
19         super.act(delta);
20     }
21     
22     
23     @Override
24     public void draw(SpriteBatch batch, float parentAlpha) {
25         // TODO Auto-generated method stub
26         //batch.draw( region, region.getRegionX(), region.getRegionY(), region.getRegionWidth(), region.getRegionHeight() );
27         batch.draw( region, getX(), getY(), getWidth(), getHeight() );
28         super.draw(batch, parentAlpha);
29     }
30 }
View Code

 资源类:

 1 package com.fxb.flappy;
 2 
 3 import com.badlogic.gdx.Gdx;
 4 import com.badlogic.gdx.assets.AssetManager;
 5 import com.badlogic.gdx.graphics.g2d.TextureAtlas;
 6 import com.badlogic.gdx.graphics.g2d.TextureRegion;
 7 import com.badlogic.gdx.scenes.scene2d.ui.Skin;
 8 
 9 public class Assets {
10     public static Skin skin;
11     public static AssetManager manager;
12     public static TextureAtlas atlas; 
13     public static TextureRegion[] sRegionBird;
14     
15     public static TextureRegion regionPipeUp;
16     public static TextureRegion regionPipeDown;
17     public static TextureRegion regionBack;
18     
19     public static void init(){
20         skin = new Skin( Gdx.files.internal( "skin/uiskin.json" ) );
21         atlas = new TextureAtlas( Gdx.files.internal( "data/bird.pack" ) );
22         
23         sRegionBird = new TextureRegion[3]; 
24         for( int i=0; i<sRegionBird.length; ++i ){
25             sRegionBird[i] = atlas.findRegion( "bird_blue", i );            
26         }
27         
28         regionPipeUp = atlas.findRegion( "bound_up" );
29         regionPipeDown = atlas.findRegion( "bound_down" );
30         regionBack = atlas.findRegion( "background" );
31 
32     }
33     
34     public static void clean(){
35         if( skin != null ) skin.dispose();
36         if( atlas != null ) atlas.dispose();
37         
38     }
39 }
View Code

常量及功能函数类:

 1 package com.fxb.flappy;
 2 
 3 import com.badlogic.gdx.graphics.g2d.TextureRegion;
 4 import com.badlogic.gdx.scenes.scene2d.Actor;
 5 import com.badlogic.gdx.scenes.scene2d.Stage;
 6 import com.badlogic.gdx.scenes.scene2d.ui.Image;
 7 
 8 public class Constant {
 9     
10     enum GameState{ game_ready, game_on, game_preover, game_over };
11     public static GameState state = GameState.game_ready;
12     
13     public static float speedY = -1f;
14     public static float speedInc = -0.5f;
15     
16     public static void CenterInStage( Actor actor, Stage stage ){
17         actor.setPosition( stage.getWidth()/2-actor.getWidth()/2, stage.getHeight()/2-actor.getHeight()/2 );
18     }
19     
20     public static Image CreateImage( TextureRegion region0 ){
21         Image img = new Image( region0 );
22         img.setSize( region0.getRegionWidth(), region0.getRegionHeight() );
23         return img;
24     }
25     
26     public static boolean IsCollsion( Actor actor1, Actor actor2 ){
27         float x1 = actor1.getX(), y1 = actor1.getY(), w1 = actor1.getWidth(), h1 = actor1.getHeight();
28         float x2 = actor2.getX(), y2 = actor2.getY(), w2 = actor2.getWidth(), h2 = actor2.getHeight();
29         if( x1<x2 && x1+w1<x2 ){
30             return false;
31         }
32         else if( x2<x1 && x2+w2<x1 ){
33             return false;
34         }
35         else if( y1<y2 && y1+h1<y2 ){
36             return false;
37         }
38         else if( y2<y1 && y2+h2<y1 ){
39             return false;
40         }
41         
42         return true;
43     }
44 
45 }
View Code

大概就这样了,也是比较粗糙。自己试验了下,在android平台下感觉操作不太顺手,可能里面一些参数还是没有调整好,提升的空间挺大的!!