• Home
  • Tutorials
    • Game Development Tutorial>
      • Unit 1: Beginning Java>
        • Before you begin...
        • Day 1: Setting Up
        • Day 2: Java Basics
        • Day 3: More Basics
        • Day 4: Java Math
        • Day 5: More Math
        • Day 6: If... else...
        • Day 7: More Control Flow
        • Day 8: Looping
        • Day 9: More on Looping
        • Day 10: Inheritance, Interface
        • Day 11: Threads and Graphics
      • Unit 2: Creating a Game I>
        • Day 1: Foundations
        • Day 2: Basic Framework
        • Day 3: Taking User Input
        • Day 4: Enter the Robot
        • Day 5: Background and Sprites
        • Day 6: Adding Enemies
        • Day 7: Shooting Bullets
        • Day 8: Animations
        • Day 9: 2D-Arrays
        • Day 10: Painting the Tilemap
      • Unit 3: Creating a Game II>
        • Day 1: Level Creation - Part 1
        • Day 2: Level Creation - Part 2
        • Day 3: Level Creation - Part 3
        • Collision Detection Basics
        • Day 4: Collision Detection Part 1
        • Day 5: Collision Detection Part 2
        • Day 6: Collision Detection Part 3
        • Day 7: Health System & Death
        • Day 8: Basic AI & Final Touches
      • Unit 4: Android Game Development>
        • Day 1: Introduction to Android
        • Day 2: Setting up for Development
        • Day 3: Creating our First Android Application
        • Day 4: Parts of an Android Application
        • Day 5: The Android Game Framework: Part I
        • Day 6: The Android Game Framework: Part II
        • Create an Android Game From Scratch (or port your existing game)
        • Day 7: Creating an Android Game (From Start to Finish)
      • Reference Sheet
    • Zombie Bird Tutorial (Flappy Bird Remake)>
      • Unit 1: Building the Game>
        • Introduction
        • Day 1: Flappy Bird - An In-depth Analysis
        • Day 2: Setting up libGDX
        • Day 3: Understanding the libGDX Framework
        • Day 4: GameWorld and GameRenderer and the Orthographic Camera
        • Day 5: The Flight of the Dead - Adding the Bird
        • Day 6: Adding Graphics - Welcome to the Necropolis
        • Day 7: The Grass, the Bird and the Skull Pipe
        • Day 8: Collision Detection and Sound Effects
        • Day 9: Finishing Gameplay and Basic UI
        • Day 10: GameStates and High Score
        • Day 11: Supporting iOS/Android + SplashScreen, Menus and Tweening
        • Day 12: Completed UI & Source Code
    • Android Application Development Tutorial>
      • Unit 1: Writing Basic Android Apps>
        • Before you begin...
        • Day 1: Android 101
        • Day 2: Getting to Know the Android Project
        • Day 3: The Development Machine
        • Day 4: Building a Music App - Part 1: Building Blocks
        • Day 5: Building a Music App - Part 2: Intents
        • Day 6: Building a Music App - Part 3: Activity Lifecycles
  • New Forum
  • About Us
    • Contact Us
  • Our Games
    • TUMBL: FallDown
  • Facebook
  • Twitter

Day 7 - Creating an Android Game (From Start to Finish)

Picture
Welcome to Day 7 of Unit 4. In this lesson, I will be taking you through the 5 Steps to Android Game Development using the Robot game we created in Java. 

Although in this example I am porting a previously created Java game, you could just as well follow the steps here to create a new game from scratch.

Porting can be easy!
Luckily for us, most of the classes that we created in Units 2 and 3 translate directly to Android. This means that we can copy and paste most of our code from our Java game project. Some notable exceptions include the Rectangle class and the SWT which are both used extensively in the Java game, but that's OK! We have other new classes that can do the job just as well.

Note: As mentioned in Day 5, the framework below has been created by Mario Zechner. 
Check out his book here!

Step 1. DOWNLOAD THE ANDROID GAME FRAMEWORK

kiloboltandroidframework.zip
File Size: 411 kb
File Type: zip
Download File

This is the same file that has been provided in the previous lesson.
I will download this, unzip it, and import it into Eclipse (right click inside Package Explorer >> Import >> Existing Projects into Workspace >> Browse root directory >> Finish).


Download the License: http://www.apache.org/licenses/LICENSE-2.0.txt

Step 2: Change the Name of the Packages

Since com.kilobolt.framework and com.kilobolt.framework.implementation are the names I want, I will leave those two alone. 
I will rename the entire Project (Right Click >> Refactor >> Rename) as KiloboltRobotGame.

I will then create a new package called com.kilobolt.robotgame. This third package will contain our game!
Picture
Again, we now have the following:

1. The interface (com.kilobolt.framework)
2. The implementation of the interface (com.kilobolt.framework.implementation)
3. The game code (com.kilobolt.robotgame)

Step 3: Develop the Game

Initial Setup

Open up your AndroidGame class and change the screen resolution by editing the following:
int frameBufferWidth = isPortrait ? 480: 800;
int frameBufferHeight = isPortrait ? 800: 480;

Recall that the ? operator evaluates isPortrait, and if it's true, selects the first value (480 in first statement) and if it's false, selects the latter value (800).
This is where we do the actual coding. Recall that we will only have one Activity in our game. Whereas in regular apps, each time you go to a new screen you open a new Activity, our game will simply change the existing Activity. This allows us to handle things like music, file management with much more ease.

Let's begin by discussing all the classes we will need:

Game Independent Classes (Required by most games created with this framework)

Assets class - assigns reference variables to all assets (images/audio) that will be used in our game.

SplashLoadingScreen & LoadingScreen - The former will be used to load the LoadingScreen's background image, and in the LoadingScreen, we will display this loaded image while we load every other image/audio file used in the game.

Menu Screen - Used to start the game.

GameScreen - All the gameplay will take place in this class. This class is similar to our StartingPoint class.

SampleGame - The Activity class that is launched when the game starts. It opens our application.

RobotGame classes (Copied from our Unit 2 and 3 Game)
Animation
Background
Enemy
Heliboy
Projectile
Robot
Tile
Now that we have outlined the Classes we need, we will begin our game, starting with the most important class. But first, we must download all the files used in the game:

downloading the Files

There are two important folders in your Android project: assets and res. Download the following .zip file and place the files in the appropriate locations.

1. Everything inside the downloaded assets folder should be taken out and placed into the assets folder in your project.
2. The raw folder should be placed DIRECTLY into the res folder.

Make sure you follow these steps carefully so that your game will not crash.
robotgame_assets_and_res.zip
File Size: 1930 kb
File Type: zip
Download File

Now that you have all the files we need, we can start coding.
PLEASE IGNORE ALL ERRORS. Most of the times, we will be using variables that have not been defined yet. We go on to fix these errors later on.

Note: Speaking of Music

Our extremely talented music director provided the theme song for this game. If you guys are interested in awesome music, Subscribe to his new YouTube channel!

Also, starting next week, he will be uploading free video game music that you can use in your games. Stay tuned :)

1. SampleGame Class


package com.kilobolt.robotgame;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import android.util.Log;

import com.kilobolt.framework.Screen;
import com.kilobolt.framework.implementation.AndroidGame;

public class SampleGame extends AndroidGame {

    public static String map;
    boolean firstTimeCreate = true;

    @Override
    public Screen getInitScreen() {

        if (firstTimeCreate) {
            Assets.load(this);
            firstTimeCreate = false;
        }

        InputStream is = getResources().openRawResource(R.raw.map1);
        map = convertStreamToString(is);

        return new SplashLoadingScreen(this);

    }

    @Override
    public void onBackPressed() {
        getCurrentScreen().backButton();
    }

    private static String convertStreamToString(InputStream is) {

        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append((line + "\n"));
            }
        } catch (IOException e) {
            Log.w("LOG", e.getMessage());
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                Log.w("LOG", e.getMessage());
            }
        }
        return sb.toString();
    }

    @Override
    public void onResume() {
        super.onResume();

        Assets.theme.play();

    }

    @Override
    public void onPause() {
        super.onPause();
        Assets.theme.pause();

    }
}
 
This SampleGame class extends AndroidGame class (which extends Activity), and therefore is an Activity. 

1. getInitScreen() method checks whether this is the first time that SampleGame has been opened. If it is the first time, we call the load method inside the Assets class, which loads our music. This allows us to manage music without having multiple instances of background music. Notice that we also define a String called map, which contains the information from the map1.txt file. 

We then return a SplashLoadingScreen which is the first screen that we will see in our game. More on this after.

2. We must override the onBackPressed() method to handle back button presses. We have set it so that when the user presses the back button, whatever is defined in the current screen's backButton() method is called. You will see this in action.

3. convertStreamToString() is intimidating at first, but it is just a method that takes the .txt file and returns a String.

4. onResume() and onPause() are both called during the Android Activity Lifecycle. If you forgot what that is, it is a good time to review.

Now that's move on to the Assets class.

2. Assets Class

package com.kilobolt.robotgame;

import com.kilobolt.framework.Image;
import com.kilobolt.framework.Music;
import com.kilobolt.framework.Sound;

public class Assets {
   
    public static Image menu, splash, background, character, character2, character3, heliboy, heliboy2, heliboy3, heliboy4, heliboy5;
    public static Image tiledirt, tilegrassTop, tilegrassBot, tilegrassLeft, tilegrassRight, characterJump, characterDown;
    public static Image button;
    public static Sound click;
    public static Music theme;
   
    public static void load(SampleGame sampleGame) {
        // TODO Auto-generated method stub
        theme = sampleGame.getAudio().createMusic("menutheme.mp3");
        theme.setLooping(true);
        theme.setVolume(0.85f);
        theme.play();
    }
   
}
 
This is a fairly straightforward class. We define all the assets that will be used in our game, and also load & play the background music at 85% volume.

Next is the SplashLoadingScreen class.

3. SplashLoadingScreen

package com.kilobolt.robotgame;

import com.kilobolt.framework.Game;
import com.kilobolt.framework.Graphics;
import com.kilobolt.framework.Screen;
import com.kilobolt.framework.Graphics.ImageFormat;

public class SplashLoadingScreen extends Screen {
    public SplashLoadingScreen(Game game) {
        super(game);
    }

    @Override
    public void update(float deltaTime) {
        Graphics g = game.getGraphics();
        Assets.splash= g.newImage("splash.jpg", ImageFormat.RGB565);

       
        game.setScreen(new LoadingScreen(game));

    }

    @Override
    public void paint(float deltaTime) {

    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {

    }

    @Override
    public void backButton() {

    }
}
Since this is a subclass of the Screen superclass, we must call all of Screen's methods. We have the update method, in which we load our first Image as an RGB565 (which does not support transparency but takes up the least amount of memory). We do not paint anything. As soon as the loading of the splash.jpg is complete, we go straight to the...

4. LoadingScreen class

This screen is almost identical to the SplashLoadingScreen, except we load many more assets. As long as it takes to load these assets, our game will call the paint() method, in which we draw the splash screen image we loaded in the previous screen.
package com.kilobolt.robotgame;

import com.kilobolt.framework.Game;
import com.kilobolt.framework.Graphics;
import com.kilobolt.framework.Graphics.ImageFormat;
import com.kilobolt.framework.Screen;

public class LoadingScreen extends Screen {
    public LoadingScreen(Game game) {
       
        super(game);
    }

    @Override
    public void update(float deltaTime) {
        Graphics g = game.getGraphics();
        Assets.menu = g.newImage("menu.png", ImageFormat.RGB565);
        Assets.background = g.newImage("background.png", ImageFormat.RGB565);
        Assets.character = g.newImage("character.png", ImageFormat.ARGB4444);
        Assets.character2 = g.newImage("character2.png", ImageFormat.ARGB4444);
        Assets.character3  = g.newImage("character3.png", ImageFormat.ARGB4444);
        Assets.characterJump = g.newImage("jumped.png", ImageFormat.ARGB4444);
        Assets.characterDown = g.newImage("down.png", ImageFormat.ARGB4444);

       
        Assets.heliboy = g.newImage("heliboy.png", ImageFormat.ARGB4444);
        Assets.heliboy2 = g.newImage("heliboy2.png", ImageFormat.ARGB4444);
        Assets.heliboy3  = g.newImage("heliboy3.png", ImageFormat.ARGB4444);
        Assets.heliboy4  = g.newImage("heliboy4.png", ImageFormat.ARGB4444);
        Assets.heliboy5  = g.newImage("heliboy5.png", ImageFormat.ARGB4444);


       
        Assets.tiledirt = g.newImage("tiledirt.png", ImageFormat.RGB565);
        Assets.tilegrassTop = g.newImage("tilegrasstop.png", ImageFormat.RGB565);
        Assets.tilegrassBot = g.newImage("tilegrassbot.png", ImageFormat.RGB565);
        Assets.tilegrassLeft = g.newImage("tilegrassleft.png", ImageFormat.RGB565);
        Assets.tilegrassRight = g.newImage("tilegrassright.png", ImageFormat.RGB565);
       
        Assets.button = g.newImage("button.jpg", ImageFormat.RGB565);

        //This is how you would load a sound if you had one.
        //Assets.click = game.getAudio().createSound("explode.ogg");

       
        game.setScreen(new MainMenuScreen(game));

    }

    @Override
    public void paint(float deltaTime) {
        Graphics g = game.getGraphics();
        g.drawImage(Assets.splash, 0, 0);
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {

    }

    @Override
    public void backButton() {

    }
}
Once loading is complete, the MainMenuScreen opens. Notice that the previous two screens had empty backButton() methods. Since we do not want users pressing the backButton() in the middle of loading, we have intentionally left it blank.

5. MainMenuScreen Class

package com.kilobolt.robotgame;

import java.util.List;

import com.kilobolt.framework.Game;
import com.kilobolt.framework.Graphics;
import com.kilobolt.framework.Screen;
import com.kilobolt.framework.Input.TouchEvent;

public class MainMenuScreen extends Screen {
    public MainMenuScreen(Game game) {
        super(game);
    }

    @Override
    public void update(float deltaTime) {
        Graphics g = game.getGraphics();
        List<TouchEvent> touchEvents = game.getInput().getTouchEvents();

        int len = touchEvents.size();
        for (int i = 0; i < len; i++) {
            TouchEvent event = touchEvents.get(i);
            if (event.type == TouchEvent.TOUCH_UP) {

                if (inBounds(event, 50, 350, 250, 450)) {
                    game.setScreen(new GameScreen(game));
                }

            }
        }
    }

    private boolean inBounds(TouchEvent event, int x, int y, int width,
            int height) {
        if (event.x > x && event.x < x + width - 1 && event.y > y
                && event.y < y + height - 1)
            return true;
        else
            return false;
    }

    @Override
    public void paint(float deltaTime) {
        Graphics g = game.getGraphics();
        g.drawImage(Assets.menu, 0, 0);
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {

    }

    @Override
    public void backButton() {
        android.os.Process.killProcess(android.os.Process.myPid());

    }
}
 
In this method, we paint the menu Image. We have also defined an inbounds method that allows us to check if the user touches inside a rectangle. If the user touches the Play button, we open the GameScreen. If the user touches the back button, the game exits.
Picture

6. GameScreen Class

Most of the code here will be familiar (and will be called in the same order they are called in StartingClass from Units 2 & 3). 
Please ignore the errors for now! 
package com.kilobolt.robotgame;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import android.graphics.Color;
import android.graphics.Paint;

import com.kilobolt.framework.Game;
import com.kilobolt.framework.Graphics;
import com.kilobolt.framework.Image;
import com.kilobolt.framework.Input.TouchEvent;
import com.kilobolt.framework.Screen;

public class GameScreen extends Screen {
	enum GameState {
		Ready, Running, Paused, GameOver
	}

	GameState state = GameState.Ready;

	// Variable Setup

	private static Background bg1, bg2;
	private static Robot robot;
	public static Heliboy hb, hb2;

	private Image currentSprite, character, character2, character3, heliboy,
			heliboy2, heliboy3, heliboy4, heliboy5;
	private Animation anim, hanim;

	private ArrayList tilearray = new ArrayList();

	int livesLeft = 1;
	Paint paint, paint2;

	public GameScreen(Game game) {
		super(game);

		// Initialize game objects here

		bg1 = new Background(0, 0);
		bg2 = new Background(2160, 0);
		robot = new Robot();
		hb = new Heliboy(340, 360);
		hb2 = new Heliboy(700, 360);

		character = Assets.character;
		character2 = Assets.character2;
		character3 = Assets.character3;

		heliboy = Assets.heliboy;
		heliboy2 = Assets.heliboy2;
		heliboy3 = Assets.heliboy3;
		heliboy4 = Assets.heliboy4;
		heliboy5 = Assets.heliboy5;

		anim = new Animation();
		anim.addFrame(character, 1250);
		anim.addFrame(character2, 50);
		anim.addFrame(character3, 50);
		anim.addFrame(character2, 50);

		hanim = new Animation();
		hanim.addFrame(heliboy, 100);
		hanim.addFrame(heliboy2, 100);
		hanim.addFrame(heliboy3, 100);
		hanim.addFrame(heliboy4, 100);
		hanim.addFrame(heliboy5, 100);
		hanim.addFrame(heliboy4, 100);
		hanim.addFrame(heliboy3, 100);
		hanim.addFrame(heliboy2, 100);

		currentSprite = anim.getImage();

		loadMap();

		// Defining a paint object
		paint = new Paint();
		paint.setTextSize(30);
		paint.setTextAlign(Paint.Align.CENTER);
		paint.setAntiAlias(true);
		paint.setColor(Color.WHITE);

		paint2 = new Paint();
		paint2.setTextSize(100);
		paint2.setTextAlign(Paint.Align.CENTER);
		paint2.setAntiAlias(true);
		paint2.setColor(Color.WHITE);

	}

	private void loadMap() {
		ArrayList lines = new ArrayList();
		int width = 0;
		int height = 0;

		Scanner scanner = new Scanner(SampleGame.map);
		while (scanner.hasNextLine()) {
			String line = scanner.nextLine();

			// no more lines to read
			if (line == null) {
				break;
			}

			if (!line.startsWith("!")) {
				lines.add(line);
				width = Math.max(width, line.length());

			}
		}
		height = lines.size();

		for (int j = 0; j < 12; j++) {
			String line = (String) lines.get(j);
			for (int i = 0; i < width; i++) {

				if (i < line.length()) {
					char ch = line.charAt(i);
					Tile t = new Tile(i, j, Character.getNumericValue(ch));
					tilearray.add(t);
				}

			}
		}

	}

	@Override
	public void update(float deltaTime) {
		List touchEvents = game.getInput().getTouchEvents();

		// We have four separate update methods in this example.
		// Depending on the state of the game, we call different update methods.
		// Refer to Unit 3's code. We did a similar thing without separating the
		// update methods.

		if (state == GameState.Ready)
			updateReady(touchEvents);
		if (state == GameState.Running)
			updateRunning(touchEvents, deltaTime);
		if (state == GameState.Paused)
			updatePaused(touchEvents);
		if (state == GameState.GameOver)
			updateGameOver(touchEvents);
	}

	private void updateReady(List touchEvents) {

		// This example starts with a "Ready" screen.
		// When the user touches the screen, the game begins.
		// state now becomes GameState.Running.
		// Now the updateRunning() method will be called!

		if (touchEvents.size() > 0)
			state = GameState.Running;
	}

	private void updateRunning(List touchEvents, float deltaTime) {

		// This is identical to the update() method from our Unit 2/3 game.

		// 1. All touch input is handled here:
		int len = touchEvents.size();
		for (int i = 0; i < len; i++) {
			TouchEvent event = touchEvents.get(i);
			if (event.type == TouchEvent.TOUCH_DOWN) {

				if (inBounds(event, 0, 285, 65, 65)) {
					robot.jump();
					currentSprite = anim.getImage();
					robot.setDucked(false);
				}

				else if (inBounds(event, 0, 350, 65, 65)) {

					if (robot.isDucked() == false && robot.isJumped() == false
							&& robot.isReadyToFire()) {
						robot.shoot();
					}
				}

				else if (inBounds(event, 0, 415, 65, 65)
						&& robot.isJumped() == false) {
					currentSprite = Assets.characterDown;
					robot.setDucked(true);
					robot.setSpeedX(0);

				}

				if (event.x > 400) {
					// Move right.
					robot.moveRight();
					robot.setMovingRight(true);

				}

			}

			if (event.type == TouchEvent.TOUCH_UP) {

				if (inBounds(event, 0, 415, 65, 65)) {
					currentSprite = anim.getImage();
					robot.setDucked(false);

				}

				if (inBounds(event, 0, 0, 35, 35)) {
					pause();

				}

				if (event.x > 400) {
					// Move right.
					robot.stopRight();
				}
			}

		}

		// 2. Check miscellaneous events like death:

		if (livesLeft == 0) {
			state = GameState.GameOver;
		}

		// 3. Call individual update() methods here.
		// This is where all the game updates happen.
		// For example, robot.update();
		robot.update();
		if (robot.isJumped()) {
			currentSprite = Assets.characterJump;
		} else if (robot.isJumped() == false && robot.isDucked() == false) {
			currentSprite = anim.getImage();
		}

		ArrayList projectiles = robot.getProjectiles();
		for (int i = 0; i < projectiles.size(); i++) {
			Projectile p = (Projectile) projectiles.get(i);
			if (p.isVisible() == true) {
				p.update();
			} else {
				projectiles.remove(i);
			}
		}

		updateTiles();
		hb.update();
		hb2.update();
		bg1.update();
		bg2.update();
		animate();

		if (robot.getCenterY() > 500) {
			state = GameState.GameOver;
		}
	}

	private boolean inBounds(TouchEvent event, int x, int y, int width,
			int height) {
		if (event.x > x && event.x < x + width - 1 && event.y > y
				&& event.y < y + height - 1)
			return true;
		else
			return false;
	}

	private void updatePaused(List touchEvents) {
		int len = touchEvents.size();
		for (int i = 0; i < len; i++) {
			TouchEvent event = touchEvents.get(i);
			if (event.type == TouchEvent.TOUCH_UP) {
				if (inBounds(event, 0, 0, 800, 240)) {

					if (!inBounds(event, 0, 0, 35, 35)) {
						resume();
					}
				}

				if (inBounds(event, 0, 240, 800, 240)) {
					nullify();
					goToMenu();
				}
			}
		}
	}

	private void updateGameOver(List touchEvents) {
		int len = touchEvents.size();
		for (int i = 0; i < len; i++) {
			TouchEvent event = touchEvents.get(i);
			if (event.type == TouchEvent.TOUCH_DOWN) {
				if (inBounds(event, 0, 0, 800, 480)) {
					nullify();
					game.setScreen(new MainMenuScreen(game));
					return;
				}
			}
		}

	}

	private void updateTiles() {

		for (int i = 0; i < tilearray.size(); i++) {
			Tile t = (Tile) tilearray.get(i);
			t.update();
		}

	}

	@Override
	public void paint(float deltaTime) {
		Graphics g = game.getGraphics();

		g.drawImage(Assets.background, bg1.getBgX(), bg1.getBgY());
		g.drawImage(Assets.background, bg2.getBgX(), bg2.getBgY());
		paintTiles(g);

		ArrayList projectiles = robot.getProjectiles();
		for (int i = 0; i < projectiles.size(); i++) {
			Projectile p = (Projectile) projectiles.get(i);
			g.drawRect(p.getX(), p.getY(), 10, 5, Color.YELLOW);
		}
		// First draw the game elements.

		g.drawImage(currentSprite, robot.getCenterX() - 61,
				robot.getCenterY() - 63);
		g.drawImage(hanim.getImage(), hb.getCenterX() - 48,
				hb.getCenterY() - 48);
		g.drawImage(hanim.getImage(), hb2.getCenterX() - 48,
				hb2.getCenterY() - 48);

		// Example:
		// g.drawImage(Assets.background, 0, 0);
		// g.drawImage(Assets.character, characterX, characterY);

		// Secondly, draw the UI above the game elements.
		if (state == GameState.Ready)
			drawReadyUI();
		if (state == GameState.Running)
			drawRunningUI();
		if (state == GameState.Paused)
			drawPausedUI();
		if (state == GameState.GameOver)
			drawGameOverUI();

	}

	private void paintTiles(Graphics g) {
		for (int i = 0; i < tilearray.size(); i++) {
			Tile t = (Tile) tilearray.get(i);
			if (t.type != 0) {
				g.drawImage(t.getTileImage(), t.getTileX(), t.getTileY());
			}
		}
	}

	public void animate() {
		anim.update(10);
		hanim.update(50);
	}

	private void nullify() {

		// Set all variables to null. You will be recreating them in the
		// constructor.
		paint = null;
		bg1 = null;
		bg2 = null;
		robot = null;
		hb = null;
		hb2 = null;
		currentSprite = null;
		character = null;
		character2 = null;
		character3 = null;
		heliboy = null;
		heliboy2 = null;
		heliboy3 = null;
		heliboy4 = null;
		heliboy5 = null;
		anim = null;
		hanim = null;

		// Call garbage collector to clean up memory.
		System.gc();

	}

	private void drawReadyUI() {
		Graphics g = game.getGraphics();

		g.drawARGB(155, 0, 0, 0);
		g.drawString("Tap to Start.", 400, 240, paint);

	}

	private void drawRunningUI() {
		Graphics g = game.getGraphics();
		g.drawImage(Assets.button, 0, 285, 0, 0, 65, 65);
		g.drawImage(Assets.button, 0, 350, 0, 65, 65, 65);
		g.drawImage(Assets.button, 0, 415, 0, 130, 65, 65);
		g.drawImage(Assets.button, 0, 0, 0, 195, 35, 35);

	}

	private void drawPausedUI() {
		Graphics g = game.getGraphics();
		// Darken the entire screen so you can display the Paused screen.
		g.drawARGB(155, 0, 0, 0);
		g.drawString("Resume", 400, 165, paint2);
		g.drawString("Menu", 400, 360, paint2);

	}

	private void drawGameOverUI() {
		Graphics g = game.getGraphics();
		g.drawRect(0, 0, 1281, 801, Color.BLACK);
		g.drawString("GAME OVER.", 400, 240, paint2);
		g.drawString("Tap to return.", 400, 290, paint);

	}

	@Override
	public void pause() {
		if (state == GameState.Running)
			state = GameState.Paused;

	}

	@Override
	public void resume() {
		if (state == GameState.Paused)
			state = GameState.Running;
	}

	@Override
	public void dispose() {

	}

	@Override
	public void backButton() {
		pause();
	}

	private void goToMenu() {
		// TODO Auto-generated method stub
		game.setScreen(new MainMenuScreen(game));

	}

	public static Background getBg1() {
		// TODO Auto-generated method stub
		return bg1;
	}

	public static Background getBg2() {
		// TODO Auto-generated method stub
		return bg2;
	}

	public static Robot getRobot() {
		// TODO Auto-generated method stub
		return robot;
	}

}
A majority of these methods and statements should be familiar, and most of the rest should be self explanatory. We make use of GameStates to call different sets of update/paint methods, rather than creating multiple Classes for these different states of the game. 

We call nullify to remove every variable used before we go to the menu (to avoid duplicates that leak memory).

Now that we have completed the 6 basic classes that will support the gameplay, we just port our 7 game specific classes, with a few changes.
The biggest changes are the following:

Image class - rather than use the Image from the Java library, we will be importing the image class we created in the framework.
Rectangle class - we will have to replace the Java Rectangle with the Android Rect class.

We will call the following methods using the Rect class.


//Create a new Rectangle
public static Rect rect = new Rect(0, 0, 0, 0);

// Set new boundaries:
rect.set(centerX - 34, centerY - 63, centerX + 34, centerY);

//Check for intersection between two rectangles
if (Rect.intersects(rect1, rect2)){ ...

Pretty straightforward, right? Other than that, a vast majority of the code will stay the same, so here are all 7 classes:

1. Animation Class

package com.kilobolt.robotgame;

import java.util.ArrayList;

import com.kilobolt.framework.Image;


public class Animation {

    private ArrayList frames;
    private int currentFrame;
    private long animTime;
    private long totalDuration;

    public Animation() {
        frames = new ArrayList();
        totalDuration = 0;

        synchronized (this) {
            animTime = 0;
            currentFrame = 0;
        }
    }

    public synchronized void addFrame(Image image, long duration) {
        totalDuration += duration;
        frames.add(new AnimFrame(image, totalDuration));
    }

    public synchronized void update(long elapsedTime) {
        if (frames.size() > 1) {
            animTime += elapsedTime;
            if (animTime >= totalDuration) {
                animTime = animTime % totalDuration;
                currentFrame = 0;

            }

            while (animTime > getFrame(currentFrame).endTime) {
                currentFrame++;

            }
        }
    }

    public synchronized Image getImage() {
        if (frames.size() == 0) {
            return null;
        } else {
            return getFrame(currentFrame).image;
        }
    }

    private AnimFrame getFrame(int i) {
        return (AnimFrame) frames.get(i);
    }

    private class AnimFrame {

        Image image;
        long endTime;

        public AnimFrame(Image image, long endTime) {
            this.image = image;
            this.endTime = endTime;
        }
    }
}
 

2. Background Class

package com.kilobolt.robotgame;


public class Background {
   
    private int bgX, bgY, speedX;
   
    public Background(int x, int y){
        bgX = x;
        bgY = y;
        speedX = 0;
    }
   
    public void update() {
        bgX += speedX;

        if (bgX <= -2160){
            bgX += 4320;
        }
    }

    public int getBgX() {
        return bgX;
    }

    public int getBgY() {
        return bgY;
    }

    public int getSpeedX() {
        return speedX;
    }

    public void setBgX(int bgX) {
        this.bgX = bgX;
    }

    public void setBgY(int bgY) {
        this.bgY = bgY;
    }

    public void setSpeedX(int speedX) {
        this.speedX = speedX;
    }

   
   
   
}
 

3. Enemy Class

package com.kilobolt.robotgame;

import android.graphics.Rect;


public class Enemy {

    private int power, centerX, speedX, centerY;
    private Background bg = GameScreen.getBg1();
    private Robot robot = GameScreen.getRobot();

    public Rect r = new Rect(0, 0, 0, 0);
    public int health = 5;

    private int movementSpeed;

    // Behavioral Methods
    public void update() {
        follow();
        centerX += speedX;
        speedX = bg.getSpeedX() * 5 + movementSpeed;
        r.set(centerX - 25, centerY - 25, centerX + 25, centerY + 35);

        if (Rect.intersects(r, Robot.yellowRed)) {
            checkCollision();
        }
       

    }

    private void checkCollision() {
        if (Rect.intersects(r, Robot.rect)|| Rect.intersects(r, Robot.rect2)
                || Rect.intersects(r, Robot.rect3) || Rect.intersects(r, Robot.rect4)) {

        }
    }

    public void follow() {
       
        if (centerX < -95 || centerX > 810){
            movementSpeed = 0;
        }

        else if (Math.abs(robot.getCenterX() - centerX) < 5) {
            movementSpeed = 0;
        }

        else {

            if (robot.getCenterX() >= centerX) {
                movementSpeed = 1;
            } else {
                movementSpeed = -1;
            }
        }

    }

    public void die() {

    }

    public void attack() {

    }

    public int getPower() {
        return power;
    }

    public int getSpeedX() {
        return speedX;
    }

    public int getCenterX() {
        return centerX;
    }

    public int getCenterY() {
        return centerY;
    }

    public Background getBg() {
        return bg;
    }

    public void setPower(int power) {
        this.power = power;
    }

    public void setSpeedX(int speedX) {
        this.speedX = speedX;
    }

    public void setCenterX(int centerX) {
        this.centerX = centerX;
    }

    public void setCenterY(int centerY) {
        this.centerY = centerY;
    }

    public void setBg(Background bg) {
        this.bg = bg;
    }

}

4. Heliboy Class

package com.kilobolt.robotgame;

public class Heliboy extends Enemy {

    public Heliboy(int centerX, int centerY) {

        setCenterX(centerX);
        setCenterY(centerY);

    }

}
 

5. robot Class

package com.kilobolt.robotgame;


import java.util.ArrayList;

import android.graphics.Rect;

public class Robot {

    // Constants are Here
    final int JUMPSPEED = -15;
    final int MOVESPEED = 5;

    private int centerX = 100;
    private int centerY = 377;
    private boolean jumped = false;
    private boolean movingLeft = false;
    private boolean movingRight = false;
    private boolean ducked = false;
    private boolean readyToFire = true;

    private int speedX = 0;
    private int speedY = 0;
    public static Rect rect = new Rect(0, 0, 0, 0);
    public static Rect rect2 = new Rect(0, 0, 0, 0);
    public static Rect rect3 = new Rect(0, 0, 0, 0);
    public static Rect rect4 = new Rect(0, 0, 0, 0);
    public static Rect yellowRed = new Rect(0, 0, 0, 0);
   
    public static Rect footleft = new Rect(0,0,0,0);
    public static Rect footright = new Rect(0,0,0,0);
   
   
    private Background bg1 = GameScreen.getBg1();
    private Background bg2 = GameScreen.getBg2();

    private ArrayList<Projectile> projectiles = new ArrayList<Projectile>();

    public void update() {
        // Moves Character or Scrolls Background accordingly.

        if (speedX < 0) {
            centerX += speedX;
        }
        if (speedX == 0 || speedX < 0) {
            bg1.setSpeedX(0);
            bg2.setSpeedX(0);

        }
        if (centerX <= 200 && speedX > 0) {
            centerX += speedX;
        }
        if (speedX > 0 && centerX > 200) {
            bg1.setSpeedX(-MOVESPEED / 5);
            bg2.setSpeedX(-MOVESPEED / 5);
        }

        // Updates Y Position
        centerY += speedY;

        // Handles Jumping

            speedY += 1;

        if (speedY > 3){
            jumped = true;
        }

        // Prevents going beyond X coordinate of 0
        if (centerX + speedX <= 60) {
            centerX = 61;
        }

        rect.set(centerX - 34, centerY - 63, centerX + 34, centerY);
        rect2.set(rect.left, rect.top + 63, rect.left+68, rect.top + 128);
        rect3.set(rect.left - 26, rect.top+32, rect.left, rect.top+52);
        rect4.set(rect.left + 68, rect.top+32, rect.left+94, rect.top+52);
        yellowRed.set(centerX - 110, centerY - 110, centerX + 70, centerY + 70);
        footleft.set(centerX - 50, centerY + 20, centerX, centerY + 35);
        footright.set(centerX, centerY + 20, centerX+50, centerY+35);


    }

    public void moveRight() {
        if (ducked == false) {
            speedX = MOVESPEED;
        }
    }

    public void moveLeft() {
        if (ducked == false) {
            speedX = -MOVESPEED;
        }
    }

    public void stopRight() {
        setMovingRight(false);
        stop();
    }

    public void stopLeft() {
        setMovingLeft(false);
        stop();
    }

    private void stop() {
        if (isMovingRight() == false && isMovingLeft() == false) {
            speedX = 0;
        }

        if (isMovingRight() == false && isMovingLeft() == true) {
            moveLeft();
        }

        if (isMovingRight() == true && isMovingLeft() == false) {
            moveRight();
        }

    }

    public void jump() {
        if (jumped == false) {
            speedY = JUMPSPEED;
            jumped = true;
        }

    }

    public void shoot() {
        if (readyToFire) {
            Projectile p = new Projectile(centerX + 50, centerY - 25);
            projectiles.add(p);
        }
    }

    public int getCenterX() {
        return centerX;
    }

    public int getCenterY() {
        return centerY;
    }

    public boolean isJumped() {
        return jumped;
    }

    public int getSpeedX() {
        return speedX;
    }

    public int getSpeedY() {
        return speedY;
    }

    public void setCenterX(int centerX) {
        this.centerX = centerX;
    }

    public void setCenterY(int centerY) {
        this.centerY = centerY;
    }

    public void setJumped(boolean jumped) {
        this.jumped = jumped;
    }

    public void setSpeedX(int speedX) {
        this.speedX = speedX;
    }

    public void setSpeedY(int speedY) {
        this.speedY = speedY;
    }

    public boolean isDucked() {
        return ducked;
    }

    public void setDucked(boolean ducked) {
        this.ducked = ducked;
    }

    public boolean isMovingRight() {
        return movingRight;
    }

    public void setMovingRight(boolean movingRight) {
        this.movingRight = movingRight;
    }

    public boolean isMovingLeft() {
        return movingLeft;
    }

    public void setMovingLeft(boolean movingLeft) {
        this.movingLeft = movingLeft;
    }

    public ArrayList getProjectiles() {
        return projectiles;
    }

    public boolean isReadyToFire() {
        return readyToFire;
    }

    public void setReadyToFire(boolean readyToFire) {
        this.readyToFire = readyToFire;
    }

}
 

6. Projectile Class

package com.kilobolt.robotgame;

import android.graphics.Rect;


public class Projectile {

    private int x, y, speedX;
    private boolean visible;
   
    private Rect r;
   
    public Projectile(int startX, int startY){
        x = startX;
        y = startY;
        speedX = 7;
        visible = true;
       
        r = new Rect(0, 0, 0, 0);
    }
   
    public void update(){
        x += speedX;
        r.set(x, y, x+10, y+5);
        if (x > 800){
            visible = false;
            r = null;
        }
        if (x < 800){
            checkCollision();
        }
    }

    private void checkCollision() {
        if(Rect.intersects(r, GameScreen.hb.r)){
            visible = false;
       
            if (GameScreen.hb.health > 0) {
                GameScreen.hb.health -= 1;
            }
            if (GameScreen.hb.health == 0) {
                GameScreen.hb.setCenterX(-100);

            }

        }
       
        if (Rect.intersects(r, GameScreen.hb2.r)){
            visible = false;

            if (GameScreen.hb2.health > 0) {
                GameScreen.hb2.health -= 1;
            }
            if (GameScreen.hb2.health == 0) {
                GameScreen.hb2.setCenterX(-100);
               

            }

        }
    }


    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getSpeedX() {
        return speedX;
    }

    public boolean isVisible() {
        return visible;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void setSpeedX(int speedX) {
        this.speedX = speedX;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }
   
   
}
 

7. Tile Class

package com.kilobolt.robotgame;

import android.graphics.Rect;

import com.kilobolt.framework.Image;

public class Tile {

    private int tileX, tileY, speedX;
    public int type;
    public Image tileImage;

    private Robot robot = GameScreen.getRobot();
    private Background bg = GameScreen.getBg1();

    private Rect r;

    public Tile(int x, int y, int typeInt) {
        tileX = x * 40;
        tileY = y * 40;

        type = typeInt;

        r = new Rect();

        if (type == 5) {
            tileImage = Assets.tiledirt;
        } else if (type == 8) {
            tileImage = Assets.tilegrassTop;
        } else if (type == 4) {
            tileImage = Assets.tilegrassLeft;

        } else if (type == 6) {
            tileImage = Assets.tilegrassRight;

        } else if (type == 2) {
            tileImage = Assets.tilegrassBot;
        } else {
            type = 0;
        }

    }

        public void update() {
            speedX = bg.getSpeedX() * 5;
            tileX += speedX;
            r.set(tileX, tileY, tileX+40, tileY+40);
   
           
           
            if (Rect.intersects(r, Robot.yellowRed) && type != 0) {
                checkVerticalCollision(Robot.rect, Robot.rect2);
                checkSideCollision(Robot.rect3, Robot.rect4, Robot.footleft, Robot.footright);
            }
   
        }

    public int getTileX() {
        return tileX;
    }

    public void setTileX(int tileX) {
        this.tileX = tileX;
    }

    public int getTileY() {
        return tileY;
    }

    public void setTileY(int tileY) {
        this.tileY = tileY;
    }

    public Image getTileImage() {
        return tileImage;
    }

    public void setTileImage(Image tileImage) {
        this.tileImage = tileImage;
    }

    public void checkVerticalCollision(Rect rtop, Rect rbot) {
        if (Rect.intersects(rtop, r)) {
           
        }

        if (Rect.intersects(rbot, r) && type == 8) {
            robot.setJumped(false);
            robot.setSpeedY(0);
            robot.setCenterY(tileY - 63);
        }
    }

    public void checkSideCollision(Rect rleft, Rect rright, Rect leftfoot, Rect rightfoot) {
        if (type != 5 && type != 2 && type != 0){
            if (Rect.intersects(rleft, r)) {
                robot.setCenterX(tileX + 102);
   
                robot.setSpeedX(0);
   
            }else if (Rect.intersects(leftfoot, r)) {
               
                robot.setCenterX(tileX + 85);
                robot.setSpeedX(0);
            }
           
            if (Rect.intersects(rright, r)) {
                robot.setCenterX(tileX - 62);
   
                robot.setSpeedX(0);
            }
           
            else if (Rect.intersects(rightfoot, r)) {
                robot.setCenterX(tileX - 45);
                robot.setSpeedX(0);
            }
        }
    }

}
Our completed project will look something like this:
Picture
That's it for the game! We now need to adjust the AndroidManifest to define permissions and our starting Activity (kind of like a main method, an entry point).

4. EDIT THE ANDROIDMANIFEST

To add your own icon, download the image below and place it inside the drawable-hdpi folder.
Picture
icon.png
File Size: 6 kb
File Type: png
Download File

We add three permissions that may be used in the game (Wake Lock, External Storage, and Vibrate).
In the <activity> tag, we add every activity used by our application (for our game, there's only .SampleGame). 

We can set the name of the application here, hide the keyboard, and change the screen orientation.

Using the intent filter, we can set the .SampleGame activity as the main activity (the starting point).
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.kilobolt.robotgame"
   android:versionCode="1"
   android:versionName="1.0" >

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <uses-sdk
       android:minSdkVersion="8"
       android:targetSdkVersion="17" />

    <application
       android:icon="@drawable/icon"
       android:label="RobotGame" >
        <activity
           android:name=".SampleGame"
           android:configChanges="keyboard|keyboardHidden|orientation"
           android:label="RobotGame"
           android:screenOrientation="landscape" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

5. EXPORT/PUBLISH THE GAME

Refer to the instructions here for step 5.

Source Code

kiloboltrobotgamecomplete.zip
File Size: 6491 kb
File Type: zip
Download File

Common Errors

If you get an error with the (R.raw.map1), check if you have this import statement:

import android.R;
This needs to be:
import com.kilobolt.robotgame.R;

Thank you for reading! Now let's take things to the next level.

Picture
We're not quite done! Join us for our latest tutorial series where we will teach you how to create Zombie Bird, a clone of the highly successfully Flappy Bird. 

We will use libGDX - a powerful cross platform game development framework that will allow you to create high performance OpenGL games for all major devices (iPhone, Android, Mac, PC, HTML).

Want to build a high performance game for all major platforms?


                        Yes, Let's do it!



  Comments below are NOT actively monitored. If you require assistance, please post in our Forums.
Help Kilobolt
I'm going to learn libGDX now!
comments powered by Disqus
© 2014 Kilobolt, LLC. All rights reserved.