Kha Shmup Tutorial Part 2
Last time we started up a Kha program and got a nice purple screen rendering. This time we will be displaying a sprite representing the player.
Ship.hx
Create a new file named Ship.hx in the Sources directory and fill it up like so:
package;
import kha.Image;
import kha.graphics2.Graphics;
class Ship {
private var image: Image;
public var x: Int;
public var y: Int;
public function new(x: Int, y: Int, image: Image) {
this.x = x;
this.y = y;
this.image = image;
}
public function render(g: Graphics) {
g.drawImage(image, x, y);
}
}
This should be fairly straight-forward - its a class containing an image, and a 2d position. It also contains a render function - taking in a graphics2 instance and draws the Ship’s image using it. Note that we do not call begin or end on g, we will be calling this outside of this class. Now we have to load the ship’s image asset.
Assets and khafile.js
Go into the khafile.js project file and make sure it looks like this:
let project = new Project('KhaSmup');
project.addAssets('Assets/**');
project.addSources('Sources');
resolve(project);
The only change is adding an assets directory. Now make sure to create this directory in the root of the project, and add an image to it name playerShip.png. I am using one of the ships from Kenny.nl’s game asset pack, which is a great collection of assets for several genres of games. I am using the following ship:
KhaShmup.hx
We need to make a few changes to KhaShmup.hx to load the assets and to initialize our ship:
package;
import kha.Assets;
import kha.Color;
import kha.Framebuffer;
import kha.Image;
import kha.Scaler;
import kha.System;
class KhaShmup {
private static var bgColor = Color.fromValue(0x26004d);
public static inline var screenWidth = 800;
public static inline var screenHeight = 600;
private var backbuffer: Image;
private var initialized = false;
private var ship: Ship;
public function new() {
Assets.loadEverything(loadingFinished);
}
private function loadingFinished(): Void {
initialized = true;
// create a buffer to draw to
backbuffer = Image.createRenderTarget(screenWidth, screenHeight);
// create our player
var shipImg = Assets.images.playerShip;
ship = new Ship(Std.int(screenWidth / 2) - Std.int(shipImg.width / 2),
Std.int(screenHeight / 2) - Std.int(shipImg.height / 2),
shipImg);
}
public function render(framebuffer: Framebuffer): Void {
if (!initialized) {
return;
}
var g = backbuffer.g2;
// clear and draw to our backbuffer
g.begin(bgColor);
ship.render(g);
g.end();
// draw our backbuffer onto the active framebuffer
framebuffer.g2.begin();
Scaler.scale(backbuffer, framebuffer, System.screenRotation);
framebuffer.g2.end();
}
}
Let’s go over the changes.
public function new() {
Assets.loadEverything(loadingFinished);
}
When we initialize our game, we now tell Kha’s Asset system to load everything (which is only one image at the moment), and once everything is loaded we call our loadingFinished method.
private function loadingFinished(): Void {
initialized = true;
// create a buffer to draw to
backbuffer = Image.createRenderTarget(screenWidth, screenHeight);
// create our player
var shipImg = Assets.images.playerShip;
ship = new Ship(Std.int(screenWidth / 2) - Std.int(shipImg.width / 2),
Std.int(screenHeight / 2) - Std.int(shipImg.height / 2),
shipImg);
}
This loadingFinished callback sets an initialized flag to true, we load the ship’s image, and instantiate the ship.
You may be wondering about the Std.int calls in the ship’s initialization. Std.int(x / y) will perform integer division on platforms that support that. In Kha we are don’t have to align images to whole integer positions, but when an image is not on whole images a different filtering method may be used. In my case I found that this often resulted in visual artifacts of blurriness, so whenever I can I keep images on whole integer positions.
The call to Assets.images.playerShip may seem odd, but essentially Kha is running a macro to generate this accessor based on the image in our Assets directory, so once Kha has loaded all of the assets, we can grab any image (or sound, etc) using this format.
public function render(framebuffer: Framebuffer): Void {
if (!initialized) {
return;
}
var g = backbuffer.g2;
// clear and draw to our backbuffer
g.begin(bgColor);
ship.render(g);
g.end();
// draw our backbuffer onto the active framebuffer
framebuffer.g2.begin();
Scaler.scale(backbuffer, framebuffer, System.screenRotation);
framebuffer.g2.end();
}
The major difference here is that we are checking our initialized flag to make sure we have loaded the assets we are intending to render. The other change is that we make the call to the ship’s render method, providing it a graphics instance.
Running the application
Now when you run the application you should be seeing this:
If you do not, check out part-2 of my branch.
https://github.com/jamiltron/KhaShmup/tree/part-2
In Summary
We loaded up some assets and rendered an image representing the player. In part 3 we will learn how to use keyboard as input and we will get the player moving around the screen.