Kha Shmup Tutorial Part 8

A week or so ago we finished up with some death animation when we shot our enemies, so in this tutorial we are going to be tracking how many enemies we’ve destroyed.

We will start by defining a UIManager to help us keep track of the various UI we’ll be doing in this game, which for this tutorial will be just displaying a score label and amount.

UIManager.hx

package;

import kha.Font;
import kha.graphics2.Graphics;

class UIManager {

  public var font: Font;
  public var x: Int;
  public var y: Int;
  public var fontSize: Int;

  private var score: Int;

  public function new(x: Int, y: Int, font: Font, fontSize: Int) {
    this.x = x;
    this.y = y;
    this.font = font;
    this.fontSize = fontSize;
  }

  public function scoreUp(): Void {
    score += 1;
  }

  public function render(g: Graphics): Void {
    g.font = font;
    g.fontSize = fontSize;
    g.drawString("score: " + score, x, y);
  }
}

This class receives a font upon creation, as well as a position for where to display the label, and a font size for how large we want the display.

This has a render method that sets the font used by our graphics2 instance, the fontSize, and then proceeds to draw the label and the core at our defined position.

There is also a function called scoreUp, which just increments the score. We will see how we’ll be using this as a callback to our collision method.

Let’s change CollisionHandler.hx a bit:

CollisionHandler.hx

package;

class CollisionHandler {

  // checks if 2 entities collide, and if so 'hits' them both, and calls a callback
  public static function handleBiCollision(h1: Hitboxed,     
                                           h2: Hitboxed,     
                                           ?callback: Void->Void = null): Void {
    if (testCollision(h1, h2)) {
      h1.hit();
      h2.hit();
      if (callback != null) {
        callback();
      }
    }
  }

  // compare every entity from leftGroup with everyone in rightGroup
  public static function handleGroupCollisions(leftGroup: Array<Hitboxed>,     
                                               rightGroup: Array<Hitboxed>,     
                                               ?callback: Void->Void = null): Void {
    for (left in leftGroup) {
      for (right in rightGroup) {
        handleBiCollision(left, right, callback);
      }
    }
  }

  public static function testCollision(h1: Hitboxed, h2: Hitboxed): Bool {
    return h1.hitbox.overlaps(h2.hitbox);
  }
}

The big change here is that we are providing an optional callback method to our “handle” methods. When we have a collision we check if this callback is null, and if it is not - we call it back. There is an obvious limitation to the fact that we can only set one callback per method, and if you want to supply more methods in your own game you obviously can pass a collection or methods.

Let’s wire this all up in KhaShmup.hx:

KhaShmup.hx

package;

// ...

class KhaSmup {

  // ...
  private var uiManager: UIManager;
  // ...

  private function loadingFinished(): Void {
    // ...
    uiManager = new UIManager(10, 10,
      Assets.fonts.kenpixel_mini_square, 20);
  }

  private function handleCollisions() {
    var bullets: Array<Hitboxed> = cast ship.gun.getActiveBullets();
    var enemies: Array<Hitboxed> = cast enemySpawner.getActiveEnemies();

    CollisionHandler.handleGroupCollisions(bullets, enemies, uiManager.scoreUp);
  }

  // ...
}

This should be clear - we just instantiate our ui manager and we pass the uiManager’s scoreUp method as the callback for the collisions between bullets and enemies.

I am using the KenPixel Mini Square font from Kenny.nl’s Font Pack. You can use your own font if you wish, just make sure you change the referenced assets when instantiating the uiManager.

Running the Application

Now run the game and you should see score ticking up when you shoot enemies:

KhaShmup8

Check my part 8 branch if you don’t see this: https://github.com/jamiltron/KhaShmup/tree/part-8

We’re getting close to finishing our game. Next time let’s add a main menu.