You are here

Chapter 19: The birth of an AsteroidHandler

After the huge success regarding the effectiveness of rotating the whole canvas lead me to a few conclusions:

  1. We do not need 1 bitmap per asteroid, scaled to the current size (whole, half, quarter) and rotated individually. It's better to create three different sizes from the beginning (in some common place) and let each asteroid just keep a pointer to one of them.
  2. The GameEngine will soon be cluttered with special code for the asteroid. And then comes the ship and the missiles also. What we need is an AsteroidHandler.
  3. I might as well create the three asteroid sizes from the beginning instead of in the app, to be able to have different images if I want to later on.

Said and done! 

I started with renaming the image "asteroid_100" to "asteroid_whole" and created two smaller versions called "asteroid_half" and "asteroid_quarter".

Then is was time to implement the AsteroidHandler: 

package com.ajomannen.justroids;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;

public class AsteroidHandler {
  private Bitmap[] asteroidBitmaps;
  private List<Asteroid> asteroids;
  private Random random;
  private Paint paint;

  public AsteroidHandler(Resources resources, int level) {
   paint = new Paint();
   random = new Random();

   asteroidBitmaps = new Bitmap[3];
   asteroidBitmaps[Asteroid.WHOLE] = BitmapFactory.decodeResource(
     resources, R.drawable.asteroid_whole);
   asteroidBitmaps[Asteroid.HALF] = BitmapFactory.decodeResource(
     resources, R.drawable.asteroid_half);
   asteroidBitmaps[Asteroid.QUARTER] = BitmapFactory.decodeResource(
     resources, R.drawable.asteroid_quarter);

   asteroids = new ArrayList<Asteroid>();
   float x;
   float y;
   double velocity;
   double direction;
   int rotation;

   for (int i = 0; i < level; i++) {
    x = 160 * random.nextFloat();
    y = 1;
    velocity = 0.2 + 1 * random.nextFloat();
    direction = 360 * random.nextFloat();
    rotation = -1 - random.nextInt(3);
    asteroids.add(new Asteroid(asteroidBitmaps[Asteroid.WHOLE], x, y,
      velocity, direction, rotation));
   }
  }

  public void update(float screenWidth, float screenHeight) {
   for (Asteroid asteroid : asteroids)
    asteroid.move(screenWidth, screenHeight);
  }

  public void draw(Canvas canvas) {
   for (Asteroid asteroid : asteroids)
    asteroid.draw(canvas, asteroid.x, asteroid.y, paint);
  }

}

What this handler does is simply to handle the creation of the asteroids and keep track of them in an ArrayList in order to handle the updating and drawing of them.

 

We then have to make a few rearrangements in our GameEngine, remove the handling of our initial test-asteroid and invoke the AsteroidHandler instead:

package com.ajomannen.justroids;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;

public class GameEngine {

  Context context;
  Resources resources;

  public float screenWidth;
  public float screenHeight;
  private Paint blackPaint;

  private int level;
  private AsteroidHandler asteroidHandler;

  public void Init(Context context) {
   this.context = context;
   resources = context.getResources();

   blackPaint = new Paint();
   blackPaint.setColor(Color.BLACK);
   blackPaint.setStyle(Style.FILL);

   level = 5;
   asteroidHandler = new AsteroidHandler(resources, level);

   setSurfaceDimensions(240, 160);

  }

  public void onDestroy() {
   try {
   } catch (Exception e) {
   }
  }

  public void setSurfaceDimensions(int width, int height) {
   screenWidth = width;
   screenHeight = height;
  }

  public void update() {
   asteroidHandler.update(screenWidth, screenHeight);
  }

  public void draw(Canvas canvas) {
   canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), blackPaint);
   asteroidHandler.draw(canvas);
  }

}

A few minor changes to GfxObject (like removal of the terrible rotate method):

package com.ajomannen.justroids;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;

public class GfxObject {

  protected Bitmap bitmap;
  protected float x;
  protected float y;
  private double velocity = 0;
  private double direction = 0;
  private double dX = 0;
  private double dY = 0;
  protected int rotation = 1;
  private int angle = 0;

  public void setVelocity(double velocity) {
   this.velocity = velocity;
   calculateDXDY();
  }

  public void setDirection(double direction) {
   this.direction = direction;
   calculateDXDY();
  }

  private void calculateDXDY() {
   double radians = Math.toRadians(direction);
   dX = Math.cos(radians) * velocity;
   dY = Math.sin(radians) * velocity;
  }

  public void move(float screenWidth, float screenHeight) {
   float minX = 0 - bitmap.getWidth() / 2;
   float maxX = screenWidth + bitmap.getWidth() / 2;
   float minY = 0 - bitmap.getHeight() / 2;
   float maxY = screenHeight + bitmap.getHeight() / 2;

   x += dX;
   y += dY;
   if (x > maxX)
    x = minX;
   if (x < minX)
    x = maxX;
   if (y > maxY)
    y = minY;
   if (y < minY)
    y = maxY;

   angle += rotation;
   if (Math.abs(angle) >= 360)
    angle = 0;
  }

  public void draw(Canvas canvas, float x, float y, Paint paint) {
   canvas.save(Canvas.MATRIX_SAVE_FLAG);
   canvas.rotate(angle, x, y);
   canvas.drawBitmap(bitmap, x - bitmap.getWidth() / 2,
     y - bitmap.getHeight() / 2, paint);
   canvas.restore();
  }

}

An finally, the latest version of Asteroid:

package com.ajomannen.justroids;

import android.graphics.Bitmap;

public class Asteroid extends GfxObject {
  static final int WHOLE = 2;
  static final int HALF = 1;
  static final int QUARTER = 0;

  public Asteroid(Bitmap bitmap, float x, float y, double velocity,
    double direction, int rotation) {
   this.bitmap = bitmap;
   this.x = x;
   this.y = y;
   setVelocity(velocity);
   setDirection(direction);
   this.rotation = rotation;
  }

}

The result of all this? Five beautifully rotating asteroids:

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer