Day 7 - Creating an Android Game (From Start to Finish)
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!
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
![]()
|
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!
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!
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)
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).
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
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.
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 |
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 :)
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.
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();
}
}
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.
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() {
}
}
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() {
}
}
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());
}
}
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.
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!
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:
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;
}
}
}
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;
}
}
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;
}
}
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);
}
}
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;
}
}
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;
}
}
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);
}
}
}
}
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:
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.
![]()
|
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).
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>
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 |
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;
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.

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!
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.
comments powered by Disqus