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 ROM.as 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;

ROM.as 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 ROM.as and open PlayState.as. 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:

FlxG.play(ROM.Mp3Collect);

With these minor additions PlayState.as should now look like this:

package {
    import org.flixel.*;
    // TMX Loading
    import net.pixelpracht.tmx.*;
    import flash.events.Event;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    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);
            loadTmxFile();
        }

        // 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 = e.target as URLLoader;
            var xml:XML = new XML(loader.data);
            var tmx:TmxMap = new TmxMap(xml);
            loadStateFromTmx(tmx);
            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);
            add(background);

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

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

            FlxG.camera.setBounds(0, 0, background.width, background.height, true);
            FlxG.camera.follow(ball, FlxCamera.STYLE_TOPDOWN_TIGHT);
        }

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

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

        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
            super.update();

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

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

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

        private function collectPod(aPod:Pod, aBall:Ball):void {
            aBall; // silence FDT warning about unused arg
            aPod.kill();
            FlxG.play(ROM.Mp3Collect);
        }
    }
}

Save PlayState.as and open Ball.as. First, replace:

import org.flixel.FlxSprite;

with:

import org.flixel.*;

Then, inside the fallIntoPit() function, add:

FlxG.pauseSounds();
FlxG.play(ROM.Mp3Death);

This stops the background music and plays the ball’s death squawk.

Ball.as 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);
            play("bounce");
            offset.y = 4;
        }

        override public function fallIntoPit():void {
            play("fall");
            FlxG.pauseSounds();
            FlxG.play(ROM.Mp3Death);
        }
    }
}

Save Ball.as 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.