Create a game like Flappy Bird in Android using AndEngine
DownloadKeywords: AndEngine AndEnginePhysicsBox2DExtension SimpleBaseGameActivity ResourceManager SceneManager MenuScene CameraScene AutoParallaxBackground AnimatedSprite DynamicSpriteBatch TiledSprite Sound Music HUD Font PhysicsHandler GenericPool PhysicsWorld Fixture Body ContactListener
Contents- Overview
- Create a new Eclipse Android project
- Add AndEngine library
- Add AndEngine Physics Box2D extension
- Android manifest
- AndEngine concepts
- Game Activity
- Resource Manager
- TextureAtlas & TextureRegion
- Font
- Sound & Music
- Scene Manager
- Base Scene
- Splash Scene
- Main Menu Scene
- Sub Menu Scene
- Game Scene
- Auto Parallax Background
- HUD
- Bird
- Pipes
- Generic Pool
- Physics World
- Body & Fixture
- Contact Listener
- Camera Scene
- What's next?
8. Resource Manager
The ResourceManager class is designed to be a singleton so that it can manage all resources used in the game. Here is a basic outline of the class.public class ResourceManager { private static final ResourceManager INSTANCE = new ResourceManager(); public GameActivity mActivity; private ResourceManager() {} public static ResourceManager getInstance() { return INSTANCE; } public void prepare(GameActivity activity) { INSTANCE.mActivity = activity; } public void loadSplashResources() { //TODO implement } public void unloadSplashResources() { //TODO implement } public void loadGameResources() { //TODO implement } public void unloadGameResources() { //TODO implement } }Usually, there is a load and corresponding unload method for each screen. Since our menu scene is lot similar to game scene so we have combined into a single method.
It is not recommended to load and unload resources frequently in a game. Only unload resources that are no longer needed.
In next few sections we'll discuss some common types of resources used in a game.9. TextureAtlas & TextureRegion
TextureAtlas or just texture is kind of map that holds all images in the memory whereas TextureRegion is a portion of the map occupied by a specific image.Let's now implement the empty methods by creating textures relevant to a scene. Add the below code to ResourceManager for loading textures used by splash scene.
private BitmapTextureAtlas mSplashTextureAtlas; public ITextureRegion mSplashTextureRegion; public void loadSplashResources() { BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/splash/"); mSplashTextureAtlas = new BitmapTextureAtlas(mActivity.getTextureManager(), 512, 512, TextureOptions.BILINEAR); mSplashTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mSplashTextureAtlas, mActivity, "logo.png", 0, 0); mSplashTextureAtlas.load(); }We need to create gfx folder under assets directory of the project for storing all graphics used in the game. Further, we create sub-folders under gfx for each scene.
First, we set the base path for all assets.
Next, we create an atlas with dimension that can hold all images and create regions within the atlas.
Keep in mind that the atlas dimension must be a power of 2 for efficient use of memory.
For splash scene we just have a single logo.png present in gfx/splash folder.Corresponding to a load method we have unload method for unloading resources. Here is the code for unloading splash textures.
public void unloadSplashResources() { mSplashTextureAtlas.unload(); mSplashTextureRegion = null; }We'll next do the same exercise for game scene.
private BitmapTextureAtlas mAutoParallaxBackgroundTexture; public ITextureRegion mParallaxLayerBack; public ITextureRegion mParallaxLayerFront; private BitmapTextureAtlas mBitmapTextureAtlas; public TiledTextureRegion mBirdTextureRegion; public TiledTextureRegion mPipeTextureRegion; private BitmapTextureAtlas mSubBitmapTextureAtlas; public TiledTextureRegion mStateTextureRegion; public ITextureRegion mPausedTextureRegion; public ITextureRegion mResumedTextureRegion; public TiledTextureRegion mButtonTextureRegion; public TiledTextureRegion mMedalTextureRegion; public void loadGameResources() { BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/game/"); mAutoParallaxBackgroundTexture = new BitmapTextureAtlas(mActivity.getTextureManager(), 512, 1024); mParallaxLayerFront = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mAutoParallaxBackgroundTexture, mActivity, "Flappy_Ground.png", 0, 0); mParallaxLayerBack = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mAutoParallaxBackgroundTexture, mActivity, "Flappy_Background.png", 0, 150); mAutoParallaxBackgroundTexture.load(); mBitmapTextureAtlas = new BitmapTextureAtlas(mActivity.getTextureManager(), 128, 512, TextureOptions.BILINEAR); mBirdTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(mBitmapTextureAtlas, mActivity, "Flappy_Birdies.png", 0, 0, 1, 3); mPipeTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(mBitmapTextureAtlas, mActivity, "Flappy_Pipe.png", 0, 125, 2, 1); mBitmapTextureAtlas.load(); mSubBitmapTextureAtlas = new BitmapTextureAtlas(mActivity.getTextureManager(), 512, 512, TextureOptions.BILINEAR); mStateTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(mSubBitmapTextureAtlas, mActivity, "ready_over.png", 0, 0, 2, 1); mPausedTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mSubBitmapTextureAtlas, mActivity, "board.png", 0, 60); mResumedTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mSubBitmapTextureAtlas, mActivity, "help.png", 0, 200); mButtonTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(mSubBitmapTextureAtlas, mActivity, "play_pos.png", 0, 350, 2, 1); mMedalTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(mSubBitmapTextureAtlas, mActivity, "medal.png", 0, 450, 4, 1); mSubBitmapTextureAtlas.load(); } public void unloadGameResources() { mAutoParallaxBackgroundTexture.unload(); mBitmapTextureAtlas.unload(); mSubBitmapTextureAtlas.unload(); mAutoParallaxBackgroundTexture = null; mParallaxLayerFront = null; mParallaxLayerBack = null; mBitmapTextureAtlas = null; mBirdTextureRegion = null; mPipeTextureRegion = null; mSubBitmapTextureAtlas = null; mStateTextureRegion = null; mPausedTextureRegion = null; mResumedTextureRegion = null; mButtonTextureRegion = null; mMedalTextureRegion = null; }Game scene uses much more textures but the basic concept is the same. The important thing to keep in mind is the atlas dimension and correct positioning of regions within it.
You may use BlackPawnTextureAtlasBuilder and BuildableBitmapTextureAtlas for automated positioning of regions in an atlas.
If you are wondering why we used multiple atlases, its just the way the game evolved. The idea should be to utilize maximum area within an atlas since its consuming memory.10. Font
AndEngine allows us to use default fonts (monospace, serif, sans-serif, etc.) as well as custom fonts (.ttf) in a game.Here is a code snippet to load and unload default font to be used in splash scene.
public Font mFont1; public void loadSplashResources() { //... mFont1 = FontFactory.create(mActivity.getFontManager(), mActivity.getTextureManager(), 256, 256, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL), 10, Color.GRAY); mFont1.load(); } public void unloadSplashResources() { //... mFont1.unload(); mFont1 = null; }We can specify the typeface, style, size, and color while creating the font.
Next, we'll see how to create custom font needed in the game scene.
public Font mFont2; public Font mFont3; public Font mFont4; public Font mFont5; public void loadGameResources() { //... mFont4 = FontFactory.create(mActivity.getFontManager(), mActivity.getTextureManager(), 256, 256, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL), 16, Color.BLACK); mFont4.load(); FontFactory.setAssetBasePath("font/"); ITexture fontTexture2 = new BitmapTextureAtlas(mActivity.getTextureManager(), 256, 256, TextureOptions.BILINEAR); mFont2 = FontFactory.createStrokeFromAsset(mActivity.getFontManager(), fontTexture2, mActivity.getAssets(), "Font1.ttf", 40, true, Color.YELLOW, 2, Color.DKGRAY); mFont2.load(); ITexture fontTexture3 = new BitmapTextureAtlas(mActivity.getTextureManager(), 256, 256, TextureOptions.BILINEAR); mFont3 = FontFactory.createFromAsset(mActivity.getFontManager(), fontTexture3, mActivity.getAssets(), "Font2.ttf", 24, true, Color.WHITE); mFont3.load(); ITexture fontTexture5 = new BitmapTextureAtlas(mActivity.getTextureManager(), 256, 256, TextureOptions.BILINEAR); mFont5 = FontFactory.createStrokeFromAsset(mActivity.getFontManager(), fontTexture5, mActivity.getAssets(), "Font1.ttf", 36, true, Color.WHITE, 2, Color.DKGRAY); mFont5.load(); } public void unloadGameResources() { //... mFont4.unload(); mFont4 = null; mFont2.unload(); mFont2 = null; mFont3.unload(); mFont3 = null; mFont5.unload(); mFont5 = null; }We need to create font folder under assets directory of the project for storing all .ttf files used in the game.
Note that we created stroke font (with outline) using createStrokeFromAsset() and normal font using createFromAsset().
11. Sound & Music
Recall that earlier we had enabled sound and music while creating engine options in onCreateEngineOptions() method of GameActivity.Here is the code snippet to load/unload sound and music. We will discuss how to play them in a later section.
public Sound mSound; public Music mMusic; public void loadGameResources() { //... SoundFactory.setAssetBasePath("mfx/"); try { mSound = SoundFactory.createSoundFromAsset(mActivity.getEngine().getSoundManager(), mActivity, "metal_hit.ogg"); } catch (final IOException e) { Debug.e(e); } MusicFactory.setAssetBasePath("mfx/"); try { mMusic = MusicFactory.createMusicFromAsset(mActivity.getEngine().getMusicManager(), mActivity, "bird_sound.ogg"); mMusic.setLooping(true); } catch (final IOException e) { Debug.e(e); } } public void unloadGameResources() { //... mSound.release(); mSound = null; mMusic.stop(); mMusic.release(); mMusic = null; }We need to create mfx folder under assets directory of the project for storing all audio files used in the game.