You are here

Chapter 16: Lesson learned - or How to NOT implement rotation

After a pretty friction-less journey up to here, it's easy to get a growing feeling of megalomania, and just assume that every design decision you take is the right one.

I just came to realize that it is not always the case...

Every (or almost every) resource has it's limits, and even the coolest device has it's boiling point.

 

One of the requirements in my wish list, was that the asteroids should rotate to give the game a more "living" feeling, and I had a pretty thorough idea on how I would implement that. So I just wrote the necessary code changes our the generic GfxObject .java and made a new build.

This is how it looked when I ran it on my emulator:

As you can see, there are two major issues:

  1. The asteroid is moving to slow in general - we're not keeping the 25 fps
  2. There are "hiccups" approximately once per second

My GfxObject currently looks like this:

package com.ajomannen.justroids;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;

public class GfxObject {

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

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

  public void setDirection(float 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) {
   rotate(angle);
   canvas.drawBitmap(rotatedAndCroppedBitmap,
     x - rotatedAndCroppedBitmap.getWidth() / 2, y
       - rotatedAndCroppedBitmap.getHeight() / 2, paint);
  }

  private void rotate(int degrees) {

   int width = bitmap.getWidth();
   int height = bitmap.getHeight();

   float midX = (float) width / 2;
   float midY = (float) height / 2;

   Matrix matrix = new Matrix();
   matrix.setRotate(degrees, midX, midY);

   try {
    Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
      height, matrix, true);

    rotatedAndCroppedBitmap = Bitmap.createBitmap(rotatedBitmap,
      (rotatedBitmap.getWidth() - width) / 2,
      (rotatedBitmap.getHeight() - height) / 2, width, height);
   } catch (Exception e) {
    Log.e("JustRoids", "Failed to rotate an image " + degrees
      + " degrees:");
    e.printStackTrace();
   }
  }

}

After reading the article on: http://developer.android.com/guide/practices/design/performance.html I realize that I have made two embarrassing mistakes, which I marked in red color above:

  1. I create three expensive objects (two Bitmaps and one Matrix) 25 times per second, causing periodic Garbage Collections - the hiccups.
  2. I rotate the asteroid 25 times per second. It was too expensive. End of statement.

I will have to throw the changes above away and choose another strategy.

 

See you in next chapter!

 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer