Funded with Kickstarter

Sound Off

And we’re back! Last time we introduced pits. Now we’re going to add some sound effects and background music. For a little more about our (well, Matt’s) audio workflow, check out the MML & NSF section of the Tools Primer.

Matt created custom death and collection sounds for this Primer and a short but unsurprisingly excellent background track.

Download the mp3s and add them to your project’s assets folder:

Stereo Hook-up

Launch FDT and open and add the following:

[Embed(source="assets/collect.mp3")] static public var Mp3Collect : Class;
[Embed(source="assets/death.mp3")] static public var Mp3Death : Class;
[Embed(source="assets/genki.mp3")] static public var Mp3Genki : Class; should now look like this:

package {
    public class ROM {
        [Embed(source="assets/bg.png")] static public var ImgBg : Class;
        [Embed(source="assets/pits.png")] static public var ImgPits : Class;
        [Embed(source="assets/ball.png")] static public var ImgBall : Class;
        [Embed(source="assets/pod.png")] static public var ImgPod : Class;

        [Embed(source="assets/collect.mp3")] static public var Mp3Collect : Class;
        [Embed(source="assets/death.mp3")] static public var Mp3Death : Class;
        [Embed(source="assets/genki.mp3")] static public var Mp3Genki : Class;

Save and open We start our background music looping in the create() function by adding:

FlxG.playMusic(ROM.Mp3Genki, 0.75);

The above code sets the music’s volume to 75% so the sound effects don’t get drowned out. Speaking of sound effects, in the collectPod() function, add the following:;

With these minor additions should now look like this:

package {
    import org.flixel.*;
    // TMX Loading
    import net.pixelpracht.tmx.*;

    public class PlayState extends FlxState {
        private var hasLoaded:Boolean = false;
        private var background:FlxTilemap = new FlxTilemap();
        private var pits:FlxTilemap = new FlxTilemap();
        private var ball:Ball;
        private var podGroup:FlxGroup = new FlxGroup();

        override public function create():void {
            // initiate variables, add sprites, tilemaps, etc
            FlxG.playMusic(ROM.Mp3Genki, 0.75);

        // Level loading
        private function loadTmxFile():void {
            var loader:URLLoader = new URLLoader();
            loader.addEventListener(Event.COMPLETE, onTmxLoaded);
            loader.load(new URLRequest('level.tmx'));

        private function onTmxLoaded(e:Event) : void {
            var loader:URLLoader = as URLLoader;
            var xml:XML = new XML(;
            var tmx:TmxMap = new TmxMap(xml);
            hasLoaded = true; // allow updates

        private function loadStateFromTmx(tmx:TmxMap) : void {
            var csvBg:String = tmx.getLayer('bg').toCsv(tmx.getTileSet('bg'));
            background.loadMap(csvBg, ROM.ImgBg, 16, 16, FlxTilemap.OFF,0,0,2);

            var csvPits:String = tmx.getLayer('pits').toCsv(tmx.getTileSet('pits'));
            pits.loadMap(csvPits, ROM.ImgPits, 16, 16, FlxTilemap.AUTO);

            var group : TmxObjectGroup = tmx.getObjectGroup('entities');
            for each (var object:TmxObject in group.objects) {

  , 0, background.width, background.height, true);

        private function spawnObject(obj : TmxObject) : void {
            switch(obj.type) {
                case "Ball":
                    ball = new Ball(obj.x, obj.y);

                case "Pod":
                    podGroup.add(new Pod(obj.x, obj.y));

        override public function update():void {
            // listen for user input, move sprites, etc

            if (!hasLoaded) return;

            if (FlxG.keys.justPressed("ENTER")) {
                FlxG.switchState(new PlayState); // reload to test map changes

            // handle user input
            var ballVelocity:Number = 64;

            ball.velocity.x = 0;
            ball.velocity.y = 0;

            if (ball.alive) {
                if (FlxG.keys.RIGHT) {
                    ball.velocity.x = ballVelocity;
                else if (FlxG.keys.LEFT) {
                    ball.velocity.x = -ballVelocity;

                if (FlxG.keys.UP) {
                    ball.velocity.y = -ballVelocity;
                else if (FlxG.keys.DOWN) {
                    ball.velocity.y = ballVelocity;

            // update after user input

            // collision logic
            FlxG.collide(background, ball);
            FlxG.overlap(podGroup, ball, collectPod);

            // it's the pits!
            pits.overlapsWithCallback(ball, Actor.overlapPit);

            if (ball.didFallIntoPit && ball.finished) {
                FlxG.switchState(new PlayState);

        private function collectPod(aPod:Pod, aBall:Ball):void {
            aBall; // silence FDT warning about unused arg

Save and open First, replace:

import org.flixel.FlxSprite;


import org.flixel.*;

Then, inside the fallIntoPit() function, add:


This stops the background music and plays the ball’s death squawk. should now look like this:

package {
    import org.flixel.*;

    public class Ball extends Actor {

        public function Ball(X : Number = 0, Y : Number = 0) {
            super(X, Y);
            loadGraphic(ROM.ImgBall, true, false, 16, 16);
            addAnimation("bounce", [0,1,2,3], 10);
            addAnimation("fall", [4,5,6,7,7,7,7,7,7,7,7,7,7,7,7], 10, false);
            offset.y = 4;

        override public function fallIntoPit():void {

Save and run your game. Music and sound effects go a long way towards making a fledging game feel more real.

And that concludes this series of Primers. This weekend is our first Crunch! Backers can continue to follow our development process in the Journal and everyone can check back once a month for a new game!

Copyright © 2012–2018 Retro Game Crunch.