Kilobolt
  • 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
  • Forum
  • About Us
    • Contact Us
  • Our Games
    • TUMBL: FallDown
  • Facebook
  • Twitter

Game Development Reference Sheet

This page will show you how to go about implementing certain features.

Table of Contents:

1. Saving, Loading Game Values
2. Taking Accelerometer Input
3. Multiple Levels (using save files)
4. Embedding Applet in HTML

Suggest new references! (support@kilobolt.com)

1. Saving, Loading Game Values

There are multiple ways of storing data. The example class provided below uses a file reading/writing system to load/save data.

To summarize what it does: it creates a file and writes text to it (saving). When loading, it opens this file and reads the text from it. 

Create a Settings class in your project. Mine will be placed in com.kilobolt.robotgame.

Settings Class

package com.kilobolt.robotgame;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import com.kilobolt.framework.FileIO;

public class Settings {
   
    // Create variables that will hold the values you want to save in your game.
    // Default values:
   
    public static boolean soundEnabled = true;
    public static int[] highscores = new int[] { 1951, 800, 120, 75, 3};
    public static int currentLevel = 0;


   
    public static void save(FileIO files) {
        BufferedWriter out = null;
        try {
           
            // Writes a file called .savedata to the SD Card
            out = new BufferedWriter(new OutputStreamWriter(
                    files.writeFile(".savedata")));
           
            // Line by line ("\n" creates a new line), write the value of each variable to the file.
            out.write(Boolean.toString(soundEnabled));
            out.write("\n");
           
            out.write(Integer.toString(currentLevel));
            out.write("\n");
       
            // Uses a for loop to save 5 numbers to the save file.
            for (int i = 0; i < 5; i++) {
                out.write(Integer.toString(highscores[i]));
                out.write("\n");
            }
           
           // This section handles errors in file management!
           
        } catch (IOException e) {
        } finally {
            try {
                if (out != null)
                    out.close();
            } catch (IOException e) {
            }
        }
    }
   
    public static void load(FileIO files) {
        BufferedReader in = null;
        try {
            // Reads file called Save Data
            in = new BufferedReader(new InputStreamReader(
                    files.readFile(".savedata")));

            // Loads values from the file and replaces default values.
            soundEnabled = Boolean.parseBoolean(in.readLine());
            currentLevel = Integer.parseInt(in.readLine());

            // Uses for loop to load 5 numbers as high score.
            for (int i = 0; i < 5; i++) {
                highscores[i] = Integer.parseInt(in.readLine());
            }
           
        } catch (IOException e) {
            // Catches errors. Default values are used.
        } catch (NumberFormatException e) {
            // Catches errors. Default values are used.
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (IOException e) {
            }
        }
    }
   
   
    // Use this method to add 5 numbers to the high score.
    public static void addScore(int score) {
        for (int i = 0; i < 5; i++) {
            if (highscores[i] < score) {
                for (int j = 4; j > i; j--)
                    highscores[j] = highscores[j - 1];
                highscores[i] = score;
                break;
            }
        }
    }
}
 

Loading and Saving using Settings Class

Loading:
Upon starting the game (or whenever you deem necessary), call:

Settings.load(getFileIO());

This will change the default values of the Setting class with the values saved in your .savedata file.
After that, you simply use if statements to change the control flow of your game to the desired result.

For example, before playing a sound, I would check:

if (Settings.soundEnabled){
   sound.play();
}

If not, then I would not play the sound.

Saving:
You can change the values in the Settings class similarly:

If the player beats a level for example, then call:
Settings.currentLevel = 5;

And when the game starts, use an if statement to start the appropriate level.

Example Applications

1. Toggle Buttons
If you want to create button that mutes and unmutes sound, you could approach it like this:

...
if (Settings.soundEnabled){
    g.drawPixmap(Assets.sound, 0, 0);
} else {
    g.drawPixmap(Assets.nosound, 0, 0);
    Assets.theme.stop();
}
...
----------------------------------------------
// On Button Press:
...
Settings.soundEnabled = !Settings.soundEnabled;
...

2. Loading Maps
Scroll down to (3: Multiple Levels (using save files)) to see this in action.

Good Practices

If you remember the Android activity life cycle, the pause() method will always be called when the application is exiting. Therefore this is a good place to save all your data by calling:

Settings.save(game.getFileIO());


However, if you want to be safe, every time that you change a value in Settings, call 
Settings.save(game.getFileIO()); so that data is never lost.

2. Accelerometer Input in Android

Picture
For this, you must be using the Android Game Development Framework (located in Step 1).
Add the following in bold to your Input interface:  

public float getAccelX();
public float getAccelY();
public float getAccelZ();



The method described below will automatically swap the XYZ axes as the screen rotates, so experiment a little bit and make sure you achieve the desired effects!

Input Interface

package com.kilobolt.framework;

import java.util.List;

public interface Input {
   
    public static class TouchEvent {
        public static final int TOUCH_DOWN = 0;
        public static final int TOUCH_UP = 1;
        public static final int TOUCH_DRAGGED = 2;
        public static final int TOUCH_HOLD = 3;

        public int type;
        public int x, y;
        public int pointer;


    }

    public boolean isTouchDown(int pointer);

    public int getTouchX(int pointer);

    public int getTouchY(int pointer);

    public float getAccelX();

    public float getAccelY();

    public float getAccelZ();

    public List<TouchEvent> getTouchEvents();
}
Before we implement these, we must create an AccelerometerHandler class as shown:
Create this in the com.kilobolt.framework.implementation package.
NOTE: YOU MUST FIX THE ONSENSORCHANGED() METHOD TO MATCH YOUR GAME ACTIVITY'S CLASS NAME.
The code below will handle rotation of screens and screens that have a landscape perspective as default, so it should be nearly universally effective.

AccelerometerHandler Class

package com.kilobolt.framework.implementation;


import com.kilobolt.robotgame.SampleGame;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;




public class AccelerometerHandler implements SensorEventListener {
    private static float screenX;
    static float screenY;
    static float screenZ;

   
    public AccelerometerHandler(Context context) {
        SensorManager manager = (SensorManager) context
                .getSystemService(Context.SENSOR_SERVICE);
        if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) {
            Sensor accelerometer = manager.getSensorList(
                    Sensor.TYPE_ACCELEROMETER).get(0);
            manager.registerListener(this, accelerometer,
                    SensorManager.SENSOR_DELAY_GAME);
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // nothing to do here
    }
    static final int ACCELEROMETER_AXIS_SWAP[][] = {
        {1, -1, 0, 1},
            {-1, -1, 1, 0},
            {-1, 1, 0, 1},
            {1, 1, 1, 0}};
       
   
    // THIS NEEDS TO BE FIXED!
    // Replace SampleGame with the game class Activity (make sure you import it).
   
    @Override
    public void onSensorChanged(SensorEvent event) {
        final int[] as = ACCELEROMETER_AXIS_SWAP[SampleGame.screenRotation];
        screenX = (float) as[0]*event.values[as[2]];
        screenY = (float) as[1]*event.values[as[3]];
        screenZ = event.values[2];     
       
       
   
    }

    public static float getAccelX() {
        return screenX;
    }

    public static float getAccelY() {
        return screenY;
    }

    public static float getAccelZ() {
        return screenZ;
    }



    public void setScreenX(float screenX) {
        this.screenX = screenX;
    }
}
 
Finally, we make the changes to AndroidInput Class, implementing the getAccelX, getAccelY, and getAccelZ methods, initializing accelHandler:

AndroidInput CLass

package com.kilobolt.framework.implementation;

import java.util.List;

import android.content.Context;
import android.os.Build.VERSION;
import android.view.View;

import com.kilobolt.framework.implementation.AccelerometerHandler;
import com.kilobolt.framework.Input;

public class AndroidInput implements Input {
    TouchHandler touchHandler;
    AccelerometerHandler accelHandler;

    public AndroidInput(Context context, View view, float scaleX, float scaleY) {
        accelHandler = new AccelerometerHandler(context);

        if (Integer.parseInt(VERSION.SDK) < 5)
            touchHandler = new SingleTouchHandler(view, scaleX, scaleY);
        else
            touchHandler = new MultiTouchHandler(view, scaleX, scaleY);
    }

    @Override
    public boolean isTouchDown(int pointer) {
        return touchHandler.isTouchDown(pointer);
    }

    @Override
    public int getTouchX(int pointer) {
        return touchHandler.getTouchX(pointer);
    }

    @Override
    public int getTouchY(int pointer) {
        return touchHandler.getTouchY(pointer);
    }

    @Override
    public List<TouchEvent> getTouchEvents() {
        return touchHandler.getTouchEvents();
    }

    @Override
    public float getAccelX() {
        return accelHandler.getAccelX();
    }

    @Override
    public float getAccelY() {
        return accelHandler.getAccelY();
    }

    @Override
    public float getAccelZ() {
        return accelHandler.getAccelZ();
    }

}
 

Handling ScreenRotation

In your Game Activity (SampleGame class for this example), you must create: 

public static int screenRotation;


In the onResume() method, you must also add:
WindowManager windowMgr = (WindowManager) this
.getSystemService(Activity.WINDOW_SERVICE);
screenRotation = windowMgr.getDefaultDisplay().getRotation();



Here's what it looks like:
package com.kilobolt.robotgame;

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

import android.app.Activity;
import android.util.Log;
import android.view.WindowManager;

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

public class SampleGame extends AndroidGame {

    public static String map;
    boolean firstTimeCreate = true;
    public static int screenRotation;

    @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();
        WindowManager windowMgr = (WindowManager) this
                .getSystemService(Activity.WINDOW_SERVICE);
        screenRotation = windowMgr.getDefaultDisplay().getRotation();

    }

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

    }

}

Adding Motion-Based Controls to your Game

Click to set custom HTML
1. In your game loop (usually the update method of the GameScreen), call the following method:
updateAccelRunning(deltaTime);

You would call this method when accelerometer is enabled.

2. Import the following:
import com.kilobolt.androidgames.framework.impl.AccelerometerHandler;

3. Define updateAccelRunning(deltaTime); method in the GameScreen class. An example is below:
This version only handles X axis accelerometer.
private void updateAccelRunning(float deltaTime) {
        float accelX = AccelerometerHandler.getAccelX();

        if (accelX > .5) {
            ball.moveLeft(deltaTime);

        }

        if (accelX < -.5) {
            ball.moveRight(deltaTime);

        }

        if (accelX <= .5 && accelX >= -.5) {
            ball.dX = 0;
            ball.animationIndicator = 1;

        }

    }

Working Sample Project

kiloboltacceldemo.zip
File Size: 6653 kb
File Type: zip
Download File

3. Multiple Levels (using Save files)

Here's a fully working example (Android) of the Robot game with randomized map selection. 

It will offer some insight on one simple way you might go about implementing multiple levels:
kiloboltleveldemo.zip
File Size: 6739 kb
File Type: zip
Download File

The basic idea is this:

1. Load the "currentLevel" value of the Settings file (this currentLevel value is randomized upon pressing "Play" in the MainMenuScreen).
2. Use an if statement inside the loadMap() method to generate the proper map.
3. Use the same conditions inside the paint() method to render the correct background.

Below are some key places you should examine in detail:

MainMenuScreen (snippet)


    @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)) {
                    Random r = new Random();
                    if (r.nextInt(2) == 0){
                        System.out.println("THIS");
                        Settings.currentLevel = 0;
                        Settings.save(game.getFileIO());
                    } else {
                        System.out.println("THAT");
                        Settings.currentLevel = 1;
                        Settings.save(game.getFileIO());
                    }
                   
                    game.setScreen(new GameScreen(game));
                }

            }
        }
    }
Usually, you do not want to load a random level, but this illustrates how you might change the currentLevel and save it to the Settings file.

IMPORTANT STEP:

You must call Settings.load(game.getFileIO()); when the game loads (or any time before you check the value of Settings.currentLevel) or you will just get default values from your Settings class.

GameScreen (Snippet)

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

        if (Settings.currentLevel == 0) {
            scanner = new Scanner(SampleGame.map);
            System.out.println("THIS");
        }
        else if (Settings.currentLevel == 1) {
            scanner = new Scanner(SampleGame.map2);
            System.out.println("THAT");
        } else {
            // DEFAULT VALUE IN CASE OF ERROR.
            scanner = new Scanner(SampleGame.map);
            System.out.println("OTHER");

       
        }
       
        System.out.println(Settings.currentLevel);

        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 < height; 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);
                }

            }
        }

    }
Notice how I load the currentLevel values from Settings, and then determine which map file to load. The same technique is applied in the paint method.

4. Embedding Applet in HTML

Thank you to Nitin Ram for getting this working!

To embed your applet to a website, you must first make some changes to your code.

1. Embedded Applets cannot modify the Frame. 
Remove these lines from your code:

   Frame frame = (Frame) this.getParent().getParent();
   frame.setTitle("Q-Bot Alpha");

2. You must access the .txt file in a different manner (the method used in our code will not work on a browser).

Change the following:
     BufferedReader reader = new BufferedReader(new FileReader(filename));

To:
     BufferedReader reader = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream(filename)));

(Make sure you Import new classes)

3. Filenames must start with a "/"

Change "data/map1.txt" to "/data/map1.txt" 
Those are all the changes you need to make to your code work when embedded.

4. Exporting is easy.

Right-click on your project, click Export, choose the Java > Jar File option (not Runnable Jar), and save it to a directory.

5. Embedding to an HTML.
To embed to an HTML page, you must use the following code:
<html>
    <head>
        <title>Kilobolt Q-Bot Applet</title>
    </head>
    <body>
        <Applet ARCHIVE = "Q-bot.jar" Code="kiloboltgame.StartingClass.class" width=800 Height=480>
        </Applet>
    </body>

<html>

Here's an example of it in action:
q-bot_html-jar.zip
File Size: 162 kb
File Type: zip
Download File

comments powered by Disqus
© 2014 Kilobolt, LLC. All rights reserved.