Browse through more Android tutorials. If you'd like to see a tutorial on any particular topic, do leave a comment in the wishlist page. We frequently post new tutorials along with app releases. You may subscribe to our newsletter to get all updates in your inbox.
Now you can get the latest Java source bundled with each app update. Install the app from Google Play and go to Settings > Extras.

«  Create a SMS app Create a reminder/alarm app  »

Create a game like Flappy Bird in Android using AndEngine

DownloadDownload

Keywords: AndEngine AndEnginePhysicsBox2DExtension SimpleBaseGameActivity ResourceManager SceneManager MenuScene CameraScene AutoParallaxBackground AnimatedSprite DynamicSpriteBatch TiledSprite Sound Music HUD Font PhysicsHandler GenericPool PhysicsWorld Fixture Body ContactListener

Contents

7 « Prev Page

21. Pipes

Flappy Pipes The pipes are created using TiledSprite but they need to be paired (top & bottom) so we use DynamicSpriteBatch to combine them together.
The advantage of combining sprites into a batch is that they can be positioned or transformed as a single entity.
Let's create a new class for pipe in package com.appsrox.flappychick.entity.
	public class Pipe extends DynamicSpriteBatch {

		private static final float DEMO_VELOCITY = 150.0f;
		private static final float DEMO_GAP = 75.0f;
		private static final float DEMO_HEIGHT = 150.0f;
		private static final float DEMO_POSITION = 1.1f*GameActivity.CAMERA_WIDTH;
		
		private TiledSprite mPipe1a;
		private TiledSprite mPipe1b;
		
		private float mGroundY;
		private float mPipeWidth;
		private float mPipeHeight;
		
		private final PhysicsHandler mPhysicsHandler;
		
		public Pipe(TiledTextureRegion pTiledTextureRegion, VertexBufferObjectManager pVertexBufferObjectManager, float pGroundY, float pDeltaHeight) {
			super(pTiledTextureRegion.getTexture(), 2, pVertexBufferObjectManager);
			
			mGroundY = pGroundY;
			mPipeWidth = pTiledTextureRegion.getWidth();
			mPipeHeight = DEMO_HEIGHT + pDeltaHeight;
			
			mPhysicsHandler = new PhysicsHandler(this);
			registerUpdateHandler(mPhysicsHandler);
			mPhysicsHandler.setVelocity(-DEMO_VELOCITY, 0);
			
			//top
			final float pipe1aX = 0;
			final float pipe1aY = 0;
			
			mPipe1a = new TiledSprite(pipe1aX, pipe1aY, pTiledTextureRegion, pVertexBufferObjectManager);
			mPipe1a.setCurrentTileIndex(0);
			mPipe1a.setHeight(mPipeHeight);
			
			//bottom
			final float pipe1bX = 0;
			final float pipe1bY = mPipeHeight + Pipe.DEMO_GAP;
			
			mPipe1b = new TiledSprite(pipe1bX, pipe1bY, pTiledTextureRegion, pVertexBufferObjectManager);
			mPipe1b.setCurrentTileIndex(1);
			mPipe1b.setHeight(mGroundY - pipe1bY);
			
			setPosition(DEMO_POSITION, 0);
		}
		
		@Override
		protected boolean onUpdateSpriteBatch() {
			this.draw(mPipe1a);
			this.draw(mPipe1b);

			return true;
		}

		@Override
		public boolean collidesWith(IShape pOtherShape) {
			Sprite sprite = (Sprite) pOtherShape;
			if (sprite.getX()+sprite.getWidth() > this.mX && (sprite.getY() < mPipe1a.getHeight() || sprite.getY()+sprite.getHeight() > mPipe1b.getY()) && sprite.getX() < this.mX+mPipeWidth) {
				return true;
			}
			return super.collidesWith(pOtherShape);
		}
		
		public void die() {
			unregisterUpdateHandler(mPhysicsHandler);
		}
		
		public void alive() {
			registerUpdateHandler(mPhysicsHandler);
		}	

		@Override
		public void reset() {
			super.reset();
			setX(DEMO_POSITION);
		}

	}
					
Basically, in the constructor we create the top and bottom pipe from tiled texture region and set different tile index to each. We then set initial position and velocity to the sprite batch.
Notice the use of PhysicsHandler as an update handler to the batch for imparting velocity. We additionally override collidesWith() method to implement our own logic to check collision of an entity with the pipes.

22. Generic Pool

As you might have guessed, we need a pool of pipes (of different heights) so that we can recycle them as the game progresses. AndEngine provides GenericPool for easily creating pool of objects.
Let's create a new class in package com.appsrox.flappychick.entity.
	public class PipePool extends GenericPool<Pipe> {
		
		private TiledTextureRegion mPipeTextureRegion;
		private VertexBufferObjectManager mVertexBufferObjectManager;
		private float mGroundY;
		private int mPipeIndex;
		
		public PipePool(TiledTextureRegion pPipeTextureRegion, VertexBufferObjectManager pVertexBufferObjectManager, float pGroundY) {
			super();
			this.mPipeTextureRegion = pPipeTextureRegion;
			this.mVertexBufferObjectManager = pVertexBufferObjectManager;
			this.mGroundY = pGroundY;
		}

		@Override
		protected Pipe onAllocatePoolItem() {
			return new Pipe(mPipeTextureRegion, mVertexBufferObjectManager, mGroundY, 50*MathUtils.random(-2, +2));
		}

		@Override
		protected void onHandleRecycleItem(Pipe pItem) {
	//		  pItem.setIgnoreUpdate(true);
	//		  pItem.setVisible(false);
		}

		@Override
		protected void onHandleObtainItem(Pipe pItem) {
			pItem.reset();
		}

		@Override
		public synchronized Pipe obtainPoolItem() {
			mPipeIndex++;
			return super.obtainPoolItem();
		}

		public int getPipeIndex() {
			return mPipeIndex;
		}

	}
					
GenericPool is an abstract class so we've to implement few methods. In onAllocatePoolItem() we create a new instance of Pipe with random height.
The other thing to note is in obtainPoolItem() method we increment mPipeIndex which is used as the score.
Now that we have Pipe and PipePool ready, let's revisit GameScene class and implement the game play.
	private Pipe mPipe;
	private PipePool mPipePool;
	
	private boolean mGameOver;
	private float mPipeWidth;	
	
	@Override
	public void createScene() {
		//...
		
		mPipeWidth = mResourceManager.mPipeTextureRegion.getWidth();
		
		//create entities
		final Rectangle ground = new Rectangle(0, SCREEN_HEIGHT - mResourceManager.mParallaxLayerFront.getHeight(), SCREEN_WIDTH, mResourceManager.mParallaxLayerFront.getHeight(), mVertexBufferObjectManager);
		ground.setColor(Color.TRANSPARENT);
		final Rectangle roof = new Rectangle(0, 0, SCREEN_WIDTH, 1, mVertexBufferObjectManager);
		roof.setColor(Color.TRANSPARENT);
		
		mPipePool = new PipePool(mResourceManager.mPipeTextureRegion, mVertexBufferObjectManager, ground.getY());
		mPipePool.batchAllocatePoolItems(10);
		mPipe = mPipePool.obtainPoolItem();

		attachChild(ground);
		attachChild(roof);
		attachChild(mPipe);		
		
		//...
		
		/* The actual collision-checking. */
		registerUpdateHandler(new IUpdateHandler() {
			
			@Override
			public void reset() {}
			
			@Override
			public void onUpdate(float pSecondsElapsed) {
				
				if (!mGameOver && mPipe.collidesWith(mBird)) {
					mGameOver = true;
					mResourceManager.mSound.play();
					mBird.stopAnimation(0);
					mPipe.die();
					mAutoParallaxBackground.setParallaxChangePerSecond(0);
					return;
				}
				
				if (mPipe.getX() < -mPipeWidth) {
					detachChild(mPipe);
					mPipePool.recyclePoolItem(mPipe);
					mPipePool.shufflePoolItems();
					
					mPipe = mPipePool.obtainPoolItem();
					attachChild(mPipe);
					sortChildren();
				}
				
				if (score != mPipePool.getPipeIndex() && mBird.getX() > (mPipe.getX()+mPipeWidth)) {
					score = mPipePool.getPipeIndex();
					mHudText.setText(String.valueOf(score));
				}				

			}
		});		
		
	}
					
In brief, we create new entities for ground and roof, and obtain pipe from the pool. The other modification is in update handler. When pipe collides with the bird we end the game and play a sound. Otherwise, we update the score if the bird is able to pass through the pipes.
Also, if the pipe goes out of screen then we recycle it and obtain a new one from the pool after shuffling it.
Share the love:  

Next Page » 7

App Gen
App Name:
Project Name:
Package:
Screens:
Splash
Login
Help
Main
List  Grid  Pager
Detail
Settings
Options:
Action Bar
Navigation Drawer
Dummy Data
Generate
Free Apps