Android: Asteroids Game

For my android class game project I decided to create a simplified version of the Asteroids game using Android Studio and Java. I had never created any game before so not only this was my first android game it was my first game ever! I had about 3-4 week of part-time weeks to develop this app so my main focus was to get the requirements in on time. In this post I will go through the main parts of the code.

The app is available in Google Play Store:
Asteroids – Apps on Google Play

The code is available on GitHub at:
saadis01/Asteroids: A simplified version of the classic Asteroid game (github.com)

At a high level,

  • MainActivity.java – contains initialization code and also the GameView class that extends the SurfaceView
  • GameController.java
    • manages background music and image
    • creates instances of asteroids
    • provides methods to start/stop the game
    • keeps track of game being active/inactive
  • SpaceShip.java
    • manages the spaceship and bullet resources
    • the firing of the bullet sound and the ship explosion sound
    • the ship explosion animation
  • Asteroid.java
    • manages speed and movements of an asteroid
    • manages the sound resource used when an asteroid is hit
    • keeps the current count of hits before the asteroid explodes

The drawMe method of the GameView class is where all the drawing on the canvas happens. When the game is launched a game over screen is displayed with the option to start a new game as shown below

The drawMe method is being called constantly as soon as the app is started. Once different elements of the game are created they are not destroyed, they are simply hidden from the screen, for e.g. when the game is not active the spaceship is not rendered. When the start button is clicked, the startGame() method of the GameController class is called that sets the isActive flag to true. The drawMe method checks for gameController.isActive() and draw appropriate items.

When the game is active, the spaceship is visible and fires a bullet while asteroids moves according to the move direction they were assigned at the time of creation. When asteroids are created, the are assigned a move speed and a move direction at random and when an asteroid is hit, it disappears from the screen and reenters the screen after a fixed time with a randomly chosen speed and direction.

Collision Detection

Two types of collision are of concern here

  • Asteroid hit by the bullet
  • Asteroid hit the spaceship

In order to detect collision, each of these three elements (spaceship, bullet and asteroid) carry a rectangle area as they move. While drawing the asteroid, intersect() method of the asteroid is call to check if the rectangle around the asteroid intersected with either the rectangle around the bullet or with the rectangle around the spaceship. This intersection indicates a collision. If an asteroid is hit by the bullet and the number of hit an asteroid could take before exploding has reached, there is a bang sound played and an image is displayed. If an asteroid hit the spaceship, a different bang sound is played and an animation is ran to show the explosion.

The Bullet

Space ship class has the code to create the bullet and also fire the bullet. The fireLaserBullet() method is called by the drawMe method constantly as long as the game is active. A bullet is created if there isn’t one on the screen already. The bullet moves up until it leave the screen or if it hits an asteroid. In both cases that bullet is recreated.

Recreation in this case doesn’t not mean the bullet object is destroyed and recreated. It simply mean making it reappear at the top of the spaceship to give an illusion that the spaceship has fired another bullet.

    public void fireLaserBullet(){
        // if asteroid was hit, remove the bullet from the screen
        if (asteroidHit){
            bulletTop = Constants.SPACE_SHIP_BULLET_OFF_SCREEN_Y_OFFSET;
            bulletCreated = false;
            asteroidHit = false;
            return;
        }
        if (bulletCreated) {
            // move bullet up until it's out of the screen
            bulletTop -= Constants.SPACE_SHIP_BULLET_MOVE_UP_SPEED;
            if (bulletTop < 0){
                bulletCreated = false;
            }
        }
        else {
            // create bullet
            if (this.displayShip) fireSound.start();
            bulletLeft = left+Constants.SPACE_SHIP_BULLET_CREATE_X_OFFSET;
            bulletTop = top-Constants.SPACE_SHIP_BULLET_CREATE_Y_OFFSET;
            bulletCreated = true;
        }

        rectBulletF.top = bulletTop - Constants.SPACE_SHIP_BULLET_RECT_Y_OFFSET;
        rectBulletF.left = bulletLeft;
        rectBulletF.right = this.bulletLeft + this.laserBullet.getWidth();
        rectBulletF.bottom = this.bulletTop - Constants.SPACE_SHIP_BULLET_RECT_Y_OFFSET + this.laserBullet.getHeight();
    }

The Spaceship

Besides handling the bullet, the spaceship class has code to handle spaceship hit by an asteroid and also display the animation on collision. The animation is composed of explosion images that are stored in explosionBitmaps array.

    public void shipHit(){
        shipExplodedSound.start();
        startExplosionAnimation();
        renderExplosion = true;
        displayShip = false;
    }

    private void startExplosionAnimation() {
        Timer explosionAnimationTimer = new Timer();
        explosionAnimationTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if (explosionRenderedIndex >= explosionBitmaps.size()) {
                    renderExplosion = false;
                    explosionRenderedIndex = 0;
                    explosionAnimationTimer.cancel();
                }
                else {
                    explosionBitmap = explosionBitmaps.get(explosionRenderedIndex);
                }
                explosionRenderedIndex++;

            }
        }, Constants.SPACE_SHIP_EXPLOSION_ANIMATION_TIMER_DELAY, Constants.SPACE_SHIP_EXPLOSION_ANIMATION_TIMER_PERIOD);

    }

The shipHit() method is called when the collision is detected that starts the sound and the animation. It also sets the render explosion flag to true and display ship to false. The startExplosionAnimation() method runs a timer task at fixed intervals to swap the explosion image (explosionBitmap) being rendered.

The Asteroid

Besides asteroid image, the Asteroid class has these specific variables that makes each asteroid behave a little differently from each other.

  • Speed – A randomly assigned value that makes asteroid moves at different speed
  • Number of hits to explode – Number of hits a asteroid could take before exploding
  • Movement – A randomly assigned move direction upon asteroid creation that determines how the asteroid would move on the screen. There are three supported move directions defined by the enum MoveDirection
    • VerticalDown – asteroid moves vertically from top to bottom either from left to right or right to left. Once it leaves the screen the direction is reversed and it moves vertically in the opposite direction. This continues until the asteroid is hit.
    • HorizontalDown – asteroid moves horizontally from left to right or right to left while moving down every time it leaves the screen. Once it leaves the screen either from the top or the bottom, the direction is reversed.
    • Diagonal – asteroid moves diagonally on the screen. When it leaves the screen the direction is reversed.
  • Entry location – A randomly assigned location where an asteroid enters the screen.

Besides asteroids movement, there is code to that handles what happens when an asteroid is hit.

    public void hit(){
        if (hitCount >= numHitsBeforeExploding) {
            renderExplosion = true;
            bangLarge.start();
            startExplosionAnimationTimer();

            // hide asteroid
            this.displayAsteroid = false;

            // Re-enter asteroid in 5 seconds
            enableDisplayTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    hitCount = 0;
                    setRandomEntryLocation();
                    setRectF();
                    moveDirection = MoveDirection.getRandomDirection();
                    setMoveSpeed();
                    displayAsteroid = true;

                }
            }, Constants.ASTEROID_REENTER_TIMER_DELAY);
        }
        else  {
            hitCount++;
        }
    }

    private void startExplosionAnimationTimer() {
        Timer explosionAnimationTimer = new Timer();
        explosionAnimationTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                renderExplosion = false;
            }
        }, Constants.ASTEROID_EXPLOSION_HIDE_TIMER_DELAY);

    }

When the asteroid is hit, a bang sound is play and renderExplosion is set to true to display an explosion image. In order to keep the UI continue to display the explosion image for a little while, a timer is used that starts after a certain delay and sets the renderExplosion to false which makes the images to no longer be rendered by the UI. The asteroid object is not destroyed, it’s simply hidden. Another timer is used to reenter the asteroid on the screen but with different parameters. When the asteroid reenters the screen, it’s assigned a reentry location at random, move speed at random and move direction at random.

That was it for the main parts of the application. To see the game in action and the code in a little more details watch the following video.