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); } }
下面管道类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
资源类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
常量及功能函数类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
大概就这样了,也是比较粗糙。自己试验了下,在android平台下感觉操作不太顺手,可能里面一些参数还是没有调整好,提升的空间挺大的!!