Monday, April 11, 2011

Android 2D Graphics



This is a continuation of my previous article on my attempts with a simple Android game.

After some more reading of the Android developer guide and some googl'ing, I finally managed to draw a ball on a background and make it move around. :)

To begin with I decided to only display a background with a ball on it and get this ball to bounce around within the boundaries of the screen.

So I first had to create a custom view by extending the View class and defining my own onDraw() method. As explained in the Developer guide, the Android framework would call my onDraw() method to request this View to be drawn and this is where I should perform all drawing to my Canvas.

I created a View named 'MyView' (not a very original name :)) and defined the default constructor in it. Within this constructor I created the Ball and Background image bitmaps from the resources (which I added under res/drawable-hdpi) using BitmapFactory.decodeResource() and initialized a few other variables initial placement of the ball and direction of bounce.

I also found that apart from the Constructor and onDraw() I had to override onMeasure() and invoke setMeasuredDimension() with the correct width and height for my view to correctly determine its boundaries. I also initialized a few more variables within this to help identify the maximum X and Y boundaries for the ball, initial placement and direction of bounce.

Within the onDraw() I just display the background, ball images and update the x and y co-ordinates for the next location the ball needs to be drawn at. Also quickly determine if a collision is going to occur with the boundaries and reverse directions accordingly.

I think the actual code would help explain this a lot better,

public class MyView extends View {

 private Context mContext;
 private Bitmap mBack;
 private Bitmap mBall;
 private int ballRadius;
 private int ballXMax, ballYMax;
 private int x,y;
 private int xInc, yInc;
  
 // c-tor for in code construction
 public MyView(Context context) {
  super(context);
  
  mContext = context;
  
  mBack = BitmapFactory.decodeResource(getResources(), R.drawable.wood);        
  mBall = BitmapFactory.decodeResource(getResources(), R.drawable.ball2);
  ballRadius = mBall.getWidth();
 }

 @Override
 protected void onMeasure(int width, int height){
  int measuredWidth = MeasureSpec.getSize(width);
  int measuredHeight = MeasureSpec.getSize(height);
  
  xInc = 1;
  yInc = 1;
  
  ballXMax = measuredWidth - ballRadius;
  ballYMax = measuredHeight - ballRadius;
  
  x = ballXMax/2;
  y = ballYMax/2;
 }

 @Override
 protected void onDraw(Canvas canvas) {
  canvas.drawBitmap(mBack, 0, 0, null);

  x += xInc;
  y += yInc;
       
  canvas.drawBitmap(mBall, x, y, null);

  // boundary collision detection and reversal
  if (x > ballXMax) xInc = -1;
  if (x < 0) xInc = 1;
  if (y > ballYMax) yInc = -1;
  if (y < 0) yInc = 1;

  //and redraw now
  invalidate();
 }
}
Further optimization:
I did however notice that with this code the simulation of the ball moving seemed "jumpy" on the emulator. i .e. it did not seem to be moving across the screen at a uniform speed. At some points it seemed to move slowly and then suddenly a little faster.
Also the onDraw() method where the static background image is redrawn every iteration seemed like something that could be avoided. With some searching I found that it is possible to create a Theme (theme.xml within the res/values directory) with the desired background image and use this in the AndroidManifest.xml for the Activity. This avoids having to redraw the background each time in our onDraw() method.
theme.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="Theme.StaticBackground" parent="android:Theme.NoTitleBar.Fullscreen">
        <item name="android:windowBackground">@drawable/wood</item>
    </style>
</resources>
With this change the animation appeared much smoother on the emulator than before (I'm not aware of how this could be measured as yet though).

View construction from XML:
Also as you may have noticed, I only have one constructor in my above code. This constructor only allows in-code creation on my View. If you want to create the View from XML then you also need to add a constructor with the additional AttributeSet argument (as shown below).
// c-tor for construction from XML Layout
 public MyView(Context context, AttributeSet attr) {
  super(context, attr);

  mContext = context;
  
  mBall = BitmapFactory.decodeResource(getResources(), R.drawable.ball2);
  ballRadius = mBall.getWidth();
 }
In this case you can have something like this in your layout XML (res/layout/main.xml) to display the custom view,
<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" >

  <com.meyn.android.mygame.MyView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

</FrameLayout>
BTW, this is the onCreate() from the activity,
@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // No Title bar
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        //MyView mMyView = new MyView(this);
        //setContentView(mMyView);

        setContentView(R.layout.main);
    }

Monday, April 4, 2011

Beginning with Android 2D Graphics

I am planning to develop a simple Android game with the main intention of learning Android game programming while doing so. I plan to start out with a rather simple board game. The user controls a ball and has to direct it to a specific position (the target) in a maze. On reaching the target the game is complete and points will be awarded to the user based on the time taken to reach the target (i.e. lesser time higher score).

This post is intended as a reference guide (mostly for myself but if it does help anyone else that would be cool).

To get started, I first looked up options Android offers and found several ways of implementing 2D graphics. I had to dig a little deeper to know which was best suited for the task I had in mind.

The first option Android offers is drawing into a View object (with the android.graphics.drawable and the android.view.animation packages). However (from the developers guide) this option seems to be best suited for display of static graphics OR pre-defined animation within an otherwise static application. As this does not suit my needs I decided to check out the other options.

The next option is drawing to a Canvas, which from the developer guide is a better choice when the application needs to regularly re-draw itself (as in my case).
There are however 2 ways to go about this. Either have the game draw to the Canvas in the same thread as the UI Activity or handle this in a separate thread. Doing this in the same thread is the recommended approach for games that do not require a significant amount of processing or frame-rate speed (such as board games or any slowly animated game), while using a separate thread for updating the drawing surface is recommended for faster animations and gives the game/app more control on the drawing pace.

To summarize these are the options,
  1. Drawing to a View object - Suited for Display of Static graphics / pre-defined animations
  2. Drawing to a Canvas - For apps that need to re-draw regularly
    • In the same thread as the UI activity
    • In a separate thread from UI activity (more control on drawing pace)
With my first attempt I've decided to try drawing in the same thread, and if performance does not seem satisfactory, I will try the separate thread approach (or may as well try it anyway once done with this:)). In my next post I'll share whatever I've learned trying to implement this. The Snake game is given as a references for this kind of an app.

Updated: Continued here