Apps

The ABCs of Android game development: Prepare the canvas

In the first installment of his five-part app developer series, William J. Francis creates a drawing surface and a framework for controlling and updating the contents of an Android game.

In Google Play, 23 of the top 25 grossing applications for Android phones and tablets are games (at the time of this writing). So it only makes sense that if you are in or are thinking about getting into the mobile space that you might consider trying your hand at writing entertainment software.

Even for an experienced software engineer, coding a game can be a little daunting. It's not that writing games is more difficult than developing other types of applications, it's just different. Game programming has its own techniques, paradigms, and vocabulary -- you'll hear game developers talking about sprites, frame-rates, world-coordinates, and pixel-perfect collision detection.

Last year, when along with my son I wrote my first game, I found myself combing the Internet and forums for simple tutorials that covered the basics. There is an overwhelming amount of information out there, but game programming is often specific to a platform and/or tool. I decided to write a series for TechRepublic readers that introduces the key concepts of coding games on the Android platform, and results in a nearly complete game that demonstrates the techniques discussed.

This is the first post in this five-part series. The source code in each installment will build upon that of the previous post. You will be able to follow along with each tutorial or download the project and import it directly into Eclipse. As with learning anything new, we must walk before we can fly. Therefore, while the code at the end of part one of this tutorial will execute, it won't be terribly entertaining. Never fear, it will get there. In the meantime, below is a sneak peek of the completed game.

Figure A

This is a screen shot of our finished game. The goal is to adjust the saucer's momentum in order to avoid getting smashed to bits by the asteroid. While simplistic, the project incorporates all the essential aspects of a video game: a canvas, sprites, animation, collision detection, and user input.

If you haven't done any Android programming, that is okay too. There are lots of tools available for writing mobile games, and in a later post, I will discuss some of the alternatives. My main reasons for choosing Android's canvas and the standard SDK are two-fold. First, the techniques are not so complicated as to scare away a novice. (While OPENGL is more performant, just getting something to appear on the screen is often enough to crush a budding game developer's soul.) Second, all of the tools are free and can be downloaded and set up under Mac OS, Linux, and Microsoft Windows. I am a big fan of Corona SDK and Unity, but both cost money to get started and only run under certain operating systems.

If this is your first time reading TechRepublic's Android App Builder blog, we have a newbies guide to Android development. You'll also want to take a look at Google's official documentation for installing and setting up your development environment.

Preparing the canvas

1. Create a new Android project in Eclipse. Because later on we will be using some of the gesture detector inputs that weren't part of the 1.0 SDK, we want to make sure the minimum version of Android targeted is 2.1 (Eclair) or better. Remember to rename your startup activity to Main.java and the corresponding layout to main.xml.

2. Modify the AndroidManifest.XML file. In particular, we are going to add a property to our activity that will cause the device to ignore orientation changes and always force the screen into portrait mode.

AndroidManifest.XML

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.authorwjf.gamedevtut"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:screenOrientation="portrait" android:configChanges="orientation|keyboardHidden"
            android:name=".Main"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

3. Define the layout for our application. In the /res/layout folder, locate main.xml. For the most part, if you've done any Android programming, you should recognize the layout components. I created a standard linear layout using common Android UI widgets with one exception. Note that the very last element in the layout is defined as a GameBoard; this is our own custom view and the heart of drawing our game to the screen. For now, you should make sure if you didn't use the same package name that I did (com.authorwjf.drawing) that you point it to your package.

main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center"
        android:text="ABC's of Android Game Dev" />
   <Button
                android:id="@+id/the_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="center"
                android:enabled="false"
                android:text="Reset" />
   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center"
       android:text="Sprite Speed (?,?)"
       android:id="@+id/the_label" />
   <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:text="Last Collision XY (?,?)"
      android:id="@+id/the_other_label" />
    <com.authorwjf.drawing.GameBoard
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_margin="20dip"
                android:id="@+id/the_canvas"/>
</LinearLayout>

4. Move to our /src directory and create a new .drawing package. In my case, the package is com.authorwjf.drawing. Within this package add a new class file and call it GameBoard.java; this new class file will handle all the drawing to our canvas. In its first incarnation, GameBoard.java simply paints a black background and populates a star field with random pulsing stars.

GameBoard.java

package com.authorwjf.drawing;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;
public class GameBoard extends View{
        private Paint p;
        private List<Point> starField = null;
        private int starAlpha = 80;
        private int starFade = 2;
        private static final int NUM_OF_STARS = 25;
        synchronized public void resetStarField() {
                starField = null;
        }

public GameBoard(Context context, AttributeSet aSet) {
                super(context, aSet);
                //it's best not to create any new objects in the on draw
                //initialize them as class variables here
                p = new Paint();
         }

private void initializeStars(int maxX, int maxY) {
                 starField = new ArrayList<Point>();
                 for (int i=0; i<NUM_OF_STARS; i++) {
                          Random r = new Random();
                          int x = r.nextInt(maxX-5+1)+5;
                          int y = r.nextInt(maxY-5+1)+5;
                          starField.add(new Point (x,y));
                  }
          }
          @Override
          synchronized public void onDraw(Canvas canvas) {
                   //create a black canvas
                   p.setColor(Color.BLACK);
                   p.setAlpha(255);
               p.setStrokeWidth(1);
                   canvas.drawRect(0, 0, getWidth(), getHeight(), p);
                   //initialize the starfield if needed
                   if (starField==null) {
                           initializeStars(canvas.getWidth(), canvas.getHeight());
                   }
                   //draw the stars
                   p.setColor(Color.CYAN);
                   p.setAlpha(starAlpha+=starFade);
                   //fade them in and out
                  if (starAlpha>=252 || starAlpha <=80) starFade=starFade*-1;
                  p.setStrokeWidth(5);
                  for (int i=0; i<NUM_OF_STARS; i++) {
                           canvas.drawPoint(starField.get(i).x, starField.get(i).y, p);
                  }
        }
}

5. Modify the /src/Main.java file. The Main class file is charged with updating the coordinates of any user objects that will get drawn to the screen by the GameBoard class, handling user input, and the frame rate. By periodically posting an invalidate to the canvas, Main.java forces the canvas to redraw its contents to reflect any updates in the action. Think of Main.java as the brains behind our game.

Main.java

package com.authorwjf.gamedevtut;

import com.authorwjf.drawing.GameBoard;
import com.authorwjf.gamedevtut.R;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class Main extends Activity implements OnClickListener{

private Handler frame = new Handler();
        //Divide the frame by 1000 to calculate how many times per second the screen will update.
        private static final int FRAME_RATE = 20; //50 frames per second
        @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Handler h = new Handler();
        ((Button)findViewById(R.id.the_button)).setOnClickListener(this);
        //We can't initialize the graphics immediately because the layout manager
        //needs to run first, thus we call back in a sec.
        h.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                                initGfx();
                        }
         }, 1000);
    }

synchronized public void initGfx() {
        ((GameBoard)findViewById(R.id.the_canvas)).resetStarField();
        ((Button)findViewById(R.id.the_button)).setEnabled(true);
        //It's a good idea to remove any existing callbacks to keep
        //them from inadvertently stacking up.
        frame.removeCallbacks(frameUpdate);
        frame.postDelayed(frameUpdate, FRAME_RATE);
    }
    @Override
    synchronized public void onClick(View v) {
             initGfx();
    }
    private Runnable frameUpdate = new Runnable() {
          @Override
          synchronized public void run() {
                  frame.removeCallbacks(frameUpdate);
                  //make any updates to on screen objects here
                  //then invoke the on draw by invalidating the canvas
                  ((GameBoard)findViewById(R.id.the_canvas)).invalidate();
                  frame.postDelayed(frameUpdate, FRAME_RATE);
          }
     };
}

The project should be ready to compile and load to your device or emulator. It will draw our star field and pulse the stars in and out. Pressing the reset button causes the app to choose new random coordinates for the stars. If you don't have a lot of experience with Android development, the simulator is painfully slow, so don't worry if the animated effect of the stars isn't as snappy as you'd like. Run it on an actual phone, and I think you will be pleased with the results.

Come back to the Android App Builder blog for part two in this series, which will focus on sprites.

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

7 comments
rilyan
rilyan

Hi, great guide but I can't locate the codes for the gamedevtut.R class, mind showing me where it is? thanks

nomad_tech
nomad_tech

Ima complete nub to mobile development and look forward to the upcoming articles on this.

authorwjf
authorwjf

Looks great! Did you use the Android Java environment or something else? Your graphics are first rate. I debated doing this series using Corona SDK as the development environment. I'm also curious what can be done in a native container using HTML 5 but haven't had a chance to give it a try. So many approaches so little time! Do you have any advice for readers trying their hand at Android game dev for the first time?

authorwjf
authorwjf

Glad to have you on board! As the series progresses I will be checking the discussion threads frequently so don't hesitate to speak up if something is confusing or isn't working as you expected.