HaxePunk shooting game tutorial: Part 6

posted on Oct 01, 2014 in Haxe, OpenFL, Game design, HaxePunk
HaxePunk shooting game tutorial sound

In this part we'll talk about audio in HaxePunk.

HaxePunk employs a simple to use sound system makes it easy to play sounds and customize their playback.

In this case, I am going to add 3 sounds to the game - a shooting sound, a hitting sound and an explosion sound.

I'm also going to take advantage of HaxePunk's Sfx class to pan the sounds for a stereo effect.

To get started, we first need to get some sound resources.

For this tutorial I created 3 sounds which you can download here.

Put all the audio files into your assets/audio/ directory.

Adding and playing the sounds is easy. Here's the code of EnemyShip.hx, which will play the explosion and hit sounds:

package ;
import com.haxepunk.Entity;
import com.haxepunk.HXP;
import com.haxepunk.graphics.Emitter;
import com.haxepunk.Sfx;

/**
 * Enemy ship entity.
 * @author Kirill Poletaev
 */
class EnemyShip extends Entity
{
	private var speed:Int;
	private var health:Int;
	private var explosion:Explosion;
	private var score:Score;
	private var sfx_explosion:Sfx;
	private var sfx_hit:Sfx;

	public function new(g:Dynamic, explosion:Explosion, score:Score) 
	{
		super();
		graphic = g;
		this.explosion = explosion;
		this.score = score;
		speed = Math.ceil(Math.random() * 3);
		setHitbox(64, 48, 0, 0);
		health = 5;
		
		sfx_explosion = new Sfx("audio/explosion.wav");
		sfx_hit = new Sfx("audio/hit.wav");
	}
	
	override public function update() {
		this.y += speed;
		if (this.y > HXP.height) {
			scene.remove(this);
		}
		var collidedEntity = collide("bullet", x, y);
		if (collidedEntity != null) {
			health--;
			scene.remove(collidedEntity);
			explosion.explode(x, y);
			sfx_hit.play(1, (x/HXP.width)*1.6 - 0.8);
		}
		if (health == 0) {
			var i:Int = 0;
			while(i<3){
				explosion.explode(x, y);
				i++;
			}
			score.add(1);
			sfx_explosion.play(1, (x/HXP.width)*1.6 - 0.8);
			scene.remove(this);
		}
	}
	
}

As you can see, I create a separate Sfx class instance for each sound type. The path to the sound is declared in the constructor of the Sfx class.

Whenever a sound needs to be played, its play() method is called. The play method has 2 parameters - volume (ranged 0 - 1) and pan (ranged -1 - 1).

Take a look at the following line:

sfx_explosion.play(1, (x/HXP.width)*1.6 - 0.8);

Notice the expression I've put as the panning parameter. This way I reset the panning value of the sound every time it is played, and it is now ranged from -0.8 to 0.8.

The reason I do this is because I want the sound to give an effect of being played at a certain location, for example, if a ship explodes on the far right side of the screen - the sound has to be played from the right speaker louder than from the left. This creates a believable stereo effect.

Similarly to the hit and explosion sounds, I play the shooting sound when the player shoots a bullet in PlayerShip.hx:

package ;
import com.haxepunk.Entity;
import com.haxepunk.graphics.atlas.AtlasRegion;
import com.haxepunk.graphics.atlas.TextureAtlas;
import com.haxepunk.graphics.Image;
import com.haxepunk.HXP;
import com.haxepunk.Sfx;
import com.haxepunk.utils.Input;
import com.haxepunk.utils.Key;

/**
 * Player ship.
 * @author Kirill Poletaev
 */
class PlayerShip extends Entity
{
	private var movespeed:Int;
	private var bulletDelay:Int;
	private var currentDelay:Int;
	private var atlas:TextureAtlas;
	private var alternateCannon:Bool;
	private var bulletImage:Image;
	private var sfx_shoot:Sfx;

	public function new(atlas:TextureAtlas) 
	{
		super();
		graphic = new Image(atlas.getRegion("playerShip"));
		movespeed = 8;
		width = 64;
		height = 48;
		x = HXP.width/2 - width/2;
		y = HXP.height - 80;
		bulletDelay = 5;
		currentDelay = 0;
		this.atlas = atlas;
		alternateCannon = true;
		bulletImage = new Image(atlas.getRegion("bullet"));
		
		sfx_shoot = new Sfx("audio/shoot.wav");
	}
	
	override public function update() {
		if (Input.check("down")) {
			moveBy(0, movespeed);
		}
		if (Input.check("up")) {
			moveBy(0, -movespeed);
		}
		if (Input.check("right")) {
			moveBy(movespeed, 0);
		}
		if (Input.check("left")) {
			moveBy(-movespeed, 0);
		}
		
		if (this.x < 0) this.x = 0;
		if (this.y < 0) this.y = 0;
		if (this.x > HXP.width - width) this.x = HXP.width - width;
		if (this.y > HXP.height - height) this.y = HXP.height - height;
		
		if (Input.check(Key.SPACE)) {
			if (currentDelay == 0) {
				currentDelay = bulletDelay;
				var b:Bullet = new Bullet(bulletImage);
				scene.add(b);
				if(alternateCannon){
					b.x = x;
				}else {
					b.x = x + 50;
				}
				alternateCannon = !alternateCannon;
				sfx_shoot.play(0.7, (x/HXP.width)*1.6 - 0.8);
				b.y = y;
			}
		}
		if (currentDelay > 0) {
			currentDelay--;
		}
	}
	
}

Notice that this time I changed the volume of the sound from 1 to 0.7.

And this is how you handle sounds in HaxePunk! Easy.

Note that you can also change the volume and panning of all the audio in the game using HXP.volume and HXP.pan properties.

We'll add the ability to pause the game in the next part!

8876