You are on page 1of 8

The ABCs of Android game development:

Prepare the canvas


By William J. Francis
February 2, 2013, 9:52 AM PST

Takeaway: 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. Its
not that writing games is more difficult than developing other types of applications, its
just different. Game programming has its own techniques, paradigms, and vocabulary
youll 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 wont 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 saucers 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 havent 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 Androids 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 developers 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 TechRepublics Android App Builder blog, we have a
newbies guide to Android development. Youll also want to take a look at Googles
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 werent 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 youve 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 didnt 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);
}
};
}

You might also like