Eager to get going, I just started coding three new "features" into the code:
It didn't work quite the way I hoped it to:
First, asteroids collide way before they actually meet.
Secondly, they get stuck on each other.
It looks really terrible. Worse than the bad rotation.
This is what I did in AsteroidHandler.java. (Bold parts are to handle the concept of "size", red parts are the collision detection and bouncing.):
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 float[] asteroidRadiuses; 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); asteroidRadiuses = new float[3]; asteroidRadiuses[Asteroid.WHOLE] = 50; asteroidRadiuses[Asteroid.HALF] = 30; asteroidRadiuses[Asteroid.QUARTER] = 15; 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 = 320 * random.nextFloat(); // No random velocity anymore. Each asteroid is faster than the // previous. velocity = 0.2 + 0.5 * i; direction = 360 * random.nextFloat(); rotation = -1 - random.nextInt(3); asteroids.add(new Asteroid(asteroidBitmaps[Asteroid.WHOLE], Asteroid.WHOLE, x, y, velocity, direction, rotation)); } } public void update(float screenWidth, float screenHeight) { int index = 0; Asteroid otherAsteroid; float dX; float dY; float distance; for (Asteroid thisAsteroid : asteroids) { thisAsteroid.move(screenWidth, screenHeight); for (int i = 0; i < index; i++) { otherAsteroid = asteroids.get(i); dX = Math.abs(thisAsteroid.x - otherAsteroid.x); dY = Math.abs(thisAsteroid.y - otherAsteroid.y); distance = (float) Math.sqrt(dX * dX + dY * dY); if (distance <= (asteroidRadiuses[thisAsteroid.size] + asteroidRadiuses[otherAsteroid.size])) { double newDirection = thisAsteroid.getDirection() + 160 + 40 * random.nextFloat(); if (newDirection >= 360) newDirection -= 360; thisAsteroid.setDirection(newDirection); break; } } index++; } } public void draw(Canvas canvas) { for (Asteroid asteroid : asteroids) asteroid.draw(canvas, asteroid.x, asteroid.y, paint); } }
As we call getDirection() and getVelocity() for an asteroid above, we have to add those two methods in GfxObject.java also:
public double getVelocity() { return velocity; } public double getDirection() { return direction; }
We also have to add "size" to Asteroid.java:
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; int size; public Asteroid(Bitmap bitmap, int size, float x, float y, double velocity, double direction, int rotation) { this.bitmap = bitmap; this.size = size; this.x = x; this.y = y; setVelocity(velocity); setDirection(direction); this.rotation = rotation; } }
My mistakes here are two;
I think I will have to solve the issues one at a time, and start with disabling "bouncing" and just highlight the collided asteroids in a separate color, while I fix the scaling issue.
Let's start with adding a "collided" property to GfxObject:
protected boolean collided = false; public boolean isCollided() { return collided; } public void setCollided(boolean collided) { this.collided = collided; }
Then we comment out the crappy bouncing code and instead set "collided" for both asteroids in AsteroidHandler update() method:
public void update(float screenWidth, float screenHeight) { int index = 0; Asteroid otherAsteroid; float dX; float dY; float distance; for (Asteroid thisAsteroid : asteroids) { thisAsteroid.move(screenWidth, screenHeight); thisAsteroid.setCollided(false); for (int i = 0; i < index; i++) { otherAsteroid = asteroids.get(i); dX = Math.abs(thisAsteroid.x - otherAsteroid.x); dY = Math.abs(thisAsteroid.y - otherAsteroid.y); distance = (float) Math.sqrt(dX * dX + dY * dY); if (distance <= (asteroidRadiuses[thisAsteroid.size] + asteroidRadiuses[otherAsteroid.size])) { thisAsteroid.setCollided(true); otherAsteroid.setCollided(true); // double newDirection = thisAsteroid.getDirection() + 160 // + 40 * random.nextFloat(); // if (newDirection >= 360) // newDirection -= 360; // thisAsteroid.setDirection(newDirection); // break; } } index++; } }
After that, we can decide how this collision should be illustrated.
I choose to make a very red copy of all three asteroid images and call them "asteroid_xxxx_highlight.png", add a second Bitmap in GfxObject:
protected Bitmap bitmapHighlighted;
and also add it in the constructor in Asteroid:
public Asteroid(Bitmap bitmap, Bitmap bitmapHilighted, int size, float x, float y, double velocity, double direction, int rotation) { this.bitmap = bitmap; this.bitmapHighlighted = bitmapHighlighted; this.size = size; this.x = x; this.y = y; setVelocity(velocity); setDirection(direction); this.rotation = rotation; }
Time to get another Bitmap array into AsteroidHandler:
private Bitmap[] asteroidBitmaps; private Bitmap[] asteroidBitmapsHighlighted;
Populate it with:
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); asteroidBitmapsHighlighted = new Bitmap[3]; asteroidBitmapsHighlighted[Asteroid.WHOLE] = BitmapFactory .decodeResource(resources, R.drawable.asteroid_whole_highlighted); asteroidBitmapsHighlighted[Asteroid.HALF] = BitmapFactory .decodeResource(resources, R.drawable.asteroid_half_highlighted); asteroidBitmapsHighlighted[Asteroid.QUARTER] = BitmapFactory .decodeResource(resources, R.drawable.asteroid_quarter_highlighted);
And include one of them when creating the asteroids:
asteroids.add(new Asteroid(asteroidBitmaps[Asteroid.WHOLE], asteroidBitmapsHighlighted[Asteroid.WHOLE], Asteroid.WHOLE, x, y, velocity, direction, rotation));
And finally, get the draw() method in GfxObject to choose between the two images:
public void draw(Canvas canvas, float x, float y, Paint paint) { canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.rotate(angle, x, y); if (collided) { canvas.drawBitmap(bitmapHighlighted, x - bitmapHighlighted.getWidth() / 2, y - bitmapHighlighted.getHeight() / 2, paint); } else { canvas.drawBitmap(bitmap, x - bitmap.getWidth() / 2, y - bitmap.getHeight() / 2, paint); } canvas.restore(); }
Well, this worked like a charm:
Now we have a good trouble-shooting scenario to fix the scaling issue.
Let's do that in next chapter.