Funded with Kickstarter

Players Gonna Play

Welcome back! Where were we? Well, we started by downloading and installing some free game development tools, we set up our first project, then we built our first level and loaded it into our game. Today we’ll be tackling sprites, the camera, and user input. On with the show!

Sprites

Time to add the player. Download the bouncing ball spritesheet:

ball.png

Move the file into your project’s assets folder.

Open up FDT and open ROM.as and embed the ball spritesheet by adding:

[Embed(source="assets/ball.png")] static public var ImgBall : 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/ball.png")] static public var ImgBall : Class;
    }
}

Now create a new Ball ActionScript Class in your project (File > New > ActionScript Class). Set the Class name to Ball, the Superclass to org.flixel.FlxSprite, check “Explicitly invoke superclass constructor”, and click the Finish button.

In Ball.as, we need to remove the unnecessary SimpleGraphic argument from the constructor and super calls. Replace:

public function Ball(X : Number = 0, Y : Number = 0, SimpleGraphic : Class = null) {

with:

public function Ball(X : Number = 0, Y : Number = 0) {

And:

super(X, Y, SimpleGraphic);

with:

super(X, Y);

Let’s load our spritesheet! Add the following inside the Ball constructor:

loadGraphic(ROM.ImgBall, true, false, 16, 16);
addAnimation("bounce", [0,1,2,3], 10);
play("bounce");
offset.y = 4;

This loads our spritesheet, let’s Flixel know that this is an animated graphic, tells Flixel not to reverse the graphic, and defines an individual frame size as 16 by 16 pixels. Next we define and play the bounce animation. Then we set the y offset to 4 to visually center the bouncing ball in the middle of our tilemap’s tiles.

With these additions Ball.as should look like this:

package {
    import org.flixel.FlxSprite;

    public class Ball extends FlxSprite {
        public function Ball(X:Number, Y:Number) {
            super(X, Y);
            loadGraphic(ROM.ImgBall, true, true, 16, 16);
            addAnimation("bounce", [0,1,2,3], 10);
            play("bounce");
            offset.y = 4;
        }
    }
}

Close Ball.as and open up PlayState.as.

Let’s Bounce

In PlayState.as find:

private var background:FlxTilemap = new FlxTilemap();

and beneath that add:

private var ball:Ball;

Then, inside the loadStateFromTmx() function, beneath:

add(background);

add:

ball = new Ball(16,16);
add(ball);

Now run your game. The ball should be in the upper left corner! Let’s respond to user input. Back in PlayState.as scroll to the update() function. Beneath:

if (!hasLoaded) return;

add:

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

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

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);

That’s a lot of code. Let’s walk through it. First, we define a ballVelocity variable so as we’re building and playtesting we don’t have to change the number in multiple places. Next, we zero the ball’s current velocity, cancelling any movement started on a previous frame, before updating the velocity based on new user input. super.update(); effectively applies our velocity changes and updates the ball’s animation automatically. Finally, we collide the ball with our background’s solid tiles so it can’t bounce through walls.

Before running the game we have one last thing to do: hook up the camera. Inside the loadStateFromTmx() function, beneath:

add(ball);

add:

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

This locks the camera to the dimensions of our tilemap and tells it to follow the ball.

With all the new 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 ball:Ball;

        override public function create():void {
            // initiate variables, add sprites, tilemaps, etc

            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);

            ball = new Ball(16,16);
            add(ball);

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

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

            if (!hasLoaded) return;

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

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

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

Now run your game. Use the arrow keys to bounce around your map! Next time we’ll add collectibles and share a trick to streamline your level design process.

Copyright © 2012–2018 Retro Game Crunch.