Browse Source
Download Source
Discuss Tutorial

Welcome to Axelite Basic, the tutorial to walk through the basics of creating a game using Axel. In this tutorial, you'll create an extremely simple game, but will learn all the essentials to expanding it into your own full game. If you haven't already set up your environment, be sure to read through one of the tutorials on setting up FlashDevelop or Flash Builder. Otherwise, we're going to jump right in!

Important: This tutorial is written using version 0.9.1. If you are using a newer version, you may need to adjust some things.

To begin, create a new project. I'm going to name and refer to it as Axelite Basic. Any screenshots I take will assume I'm using Flash Builder, but in steps that require something different to be done for FlashDevelop, I'll be sure to cover those too! Now that we've got a new project though, import Axel into your src folder, and your setup should look something like this:


Our initial project hierarchy

Now, there are many ways you can embed assets in your project. You can place them all into a compiled .swc file that you use in your project, or you can embed the assets directly. We're going to be using the direct route, and to keep things organized, we're going to make a separate source folder for our assets. This is dependent on your IDE, so follow the correct next section for the IDE you are using.

Embedding Assets

Flash Builder Instructions: Right click your project name, go to New -> Folder, type in res and hit OK. This will create a new folder called res in your project directory. Now, we need to tell Flash Builder to include this as a source directory. To do this, go to Project -> Properties, click ActionScript Build Path on the left, go to the Source path tab, click Add Folder..., type res, and hit OK. Click OK to close that, and you're done! Your resource folder will now be considered part of your source, so you can directly embed your assets without using relative or absolute paths.

FlashDevelop Instructions: Right click your project name in your project panel, and go to Add -> New Folder.... Name it res and hit enter. Now right click the res folder that was created and click Add Source Path. This is now part of your build path! Your resource folder will now be considered part of your source, so you can directly embed your assets without using relative or absolute paths.

Now that we got our resource folder set up, we're going to put the graphics in there that we'll use for this tutorial. These files are tiles.png, player.png, and gem.png and particle.png.


The tiles we'll build our world with

Our player, with walking animation frames

Our gem, we'll collect these

Our particle, for shiny effects

Our background image

Now that we've got all the graphics set up, let's create a single class where we embed them all. By doing this, we keep our code cleaner, and always know where to reference the graphics, especially if we use the same graphic in multiple places. While in a typical game you'd separate everything into various as3 packages, we're going to use only the default package here for simplicity. If you check out the Axelite Red or Axelite Star tutorial games, you'll find examples of organizing a more complicated project.

Create a new class in your default package, and call it Resource.as. Now, embed our graphics as follows:

Resource.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
package {
    public class Resource {
        [Embed(source = "/tiles.png")] public static const TILES:Class;
        [Embed(source = "/background.png")] public static const BACKGROUND:Class;
        [Embed(source = "/player.png")] public static const PLAYER:Class;
        [Embed(source = "/gem.png")] public static const GEM:Class;
        [Embed(source = "/particle.png")] public static const PARTICLE:Class;
    }
}

By doing this, you'll be able to reference them anywhere by doing Resource.PLAYER or Resource.TILES. Now let's create our main game state!

States

Before we create our first state, let's talk about how the state system works. In simple games, you're always going to be working in a single state at a time. For example, when you load up the game, perhaps the player is within the TitleState. Now they click a button to start the first level, and they go into the GameState. But as you do more complicated things, it starts to make more sense that instead of thinking of always being in a single state, you have an active state, with other possible states in the background. For example, what if the player is in the GameState and they open a menu? Do you really want to switch your game over to a MenuState and lose your main game state? Probably not. You could fake it by not actually switching states, and just displaying the menu as part of the GameState, with logic to freeze everything while you're working in the menu. However, what if we could say, "Switch into the menu state, but keep the game as it is, so we can switch back to it". This is where a game state stack comes in. In Axel, you represent your state as a stack of states.

So let's think about how we would model a menu with confirmation in Axel. First, you have the player working in a GameState. Now, they hit escape, and you want to show the menu. To do this, you'll push the MenuState on top of the stack. Now, you have the GameState at the bottom, with the Menu State on top of it. By default, every state on the stack will draw. So at this point, we would see the game in the background, with the menu on top of it, because each frame it draws the game state first (since it's at the bottom), and then draws the menu state on top of it (because it's on top of the game in the stack). Conversely, by default, only the top state on the stack will update. Because of this, once you've pushed the menu on top, the game will be frozen, and the player will only be able to interact with the menu.

Tip
In your states, you can set persistantDraw to true or false, indicating whether this state should draw when it isn't the top state, and persistantUpdate to true or false, indicating whether this state should update when it isn't the top state.

Now, let's say the player clicks EXIT in the menu and you want to show a confirmation dialog. However, only the confirmation dialog should be interactable, you shouldn't be able to play around with the menu when it is showing. This fits our model perfectly, just push a ConfirmationState on top the stack! Because the menu won't update because it's no longer on top, you'll only be able to interact with the confirmation box. However, the menu and game will still be drawn. Here is a picture showing exactly what we just described.


A stack of states

Now let's talk about the interface to implementing this all in Axel. If you want to simply switch to a new state, and destroy the old one (for example, if you are switching from the title state to the game state, and don't need to keep the title state living in the background), you use Ax.switchState(newstate). If you want to push a new state on top of the stack, you use Ax.pushState(newstate). If you want to remove the top state, and return to the one below (for example if the user clicks cancel in the ConfirmationState), use Ax.popState().

Now that we know how to work with the state system, let's create our GameState! We're going to start out with the absolute basics. Start by creating GameState.as in your default package, and filling it with the following:

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package {
    import org.axgl.AxState;
 
    public class GameState extends AxState {
        override public function create():void {
            super.create();
            trace("Gamestate Created");
        }
        
        override public function update():void {
            super.update();
        }
    }
}

The first thing is that all your states must extend AxState. This will give them all the properties they need to work as a game state. The next thing is that the two most important functions to override are create and update. The create function is called once when your state is created. You should put all your construction logic in an overriden create, rather than the constructor, to ensure that everything is set up and available for you to use when it is called. The other function, update, is called once every frame. Usually your game will be running at 60 FPS, so this function will be called 60 times a second. This is where you will put any game logic that happens in the state.

One thing of note is that unless you know what you're doing, you'll want to call super.create() and super.update() when you override these functions. This ensures that the logic in the class you have overriden still gets called.

Now, let's update our main file to tell the game to jump right into the GameState when the game begins. Open AxeliteBasic.as and fill it with the following code:

AxeliteBasic.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
package {
    import org.axgl.Ax;
    
    [SWF(width = "480", height = "360", backgroundColor = "#000000")]
 
    public class AxeliteBasic extends Ax {
        public function AxeliteBasic() {
            super(GameState);
        }
    }
}

Here we are telling it that the game should be 480x360, and that it should create a new GameState and jump into it when the game is loaded. Note that we don't pass the width and height to the super() function. We could, but since we leave it at the default of 0, it will figure that out from the SWF size. This is the only time you will detemine the current state without using Ax.switchState, Ax.pushState, or Ax.popState. Now test your game, and while you get a white blank window, you should see GameState Created in your debug console. If you've had any trouble up until this point, I'd suggest going back and checking out the setup tutorial for your IDE.

Now that we've got everything up and running, it's time to dive into creating the actual game. The first thing we are going to do is create a very simple 1 screen world using a Tilemap.

Tilemaps

When building levels, most often you'll find yourself using tilemaps. Tilemaps are grids of tiles, where each tile is part of the game image. For example, if you have an image of 4 different tiles, each one 16x16, you could create a 160x160 tilemap that would have 10 rows and 10 columns (each tile is 16x16), and you could paint those 4 tiles anywhere you want. For our game, this is the image we will use for our tiles:


The tiles we'll build our world with

Each tile is 24px by 24px, and our world is 480px by 360px, so we'll be able to fit 20 tiles by 15 tiles on the screen at a time. However, we're going to make the tilemap bigger than the screen, so that we can move around and explore while the camera follows us.

When building a tilemap, each tile in your image gets a number, starting with 1 (in the upper left). Here is what the numbers are for our tile image:


Our tile IDS, zoomed in

Now, when we build a tilemap, we build it using these numbers. Each value should be separated by commas, and each row should be separated by a newline. For example, here is a very simple 4x4 world, represented in the format we use to load tilemaps:

Tilemap
ActionScript 3 Code
1
2
3
4
0,0,4,4
1,2,3,3
4,4,3,3
5,3,3,6

Now, if we were to build a tilemap using that data, we map each number to the tile it represents in the image (a 0 means no tile), and place that tile in the position. Here is the resulting map from that data:


The resulting tilemap

Now typically you wouldn't write this data from hand. You can use any kind of map editor, such as Tiled or DAME, as long as you export the data to be in this format. For this tutorial, I am going to give you the data for our world. Feel free to play around with it to edit it as you like. You can also write logic for autotiling if you like, to make building worlds quick!

We're going to embed this into our Resource file as a string. In a real game, you may want to represent it in other ways, such as an embedded XML file, a .tmx file from Tiled, a compressed string, etc. Our Resource file should now look like this:

Resource.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package {
    public class Resource {
        [Embed(source = "/tiles.png")] public static const TILES:Class;
        [Embed(source = "/player.png")] public static const PLAYER:Class;
        [Embed(source = "/gem.png")] public static const GEM:Class;
        [Embed(source = "/particle.png")] public static const PARTICLE:Class;
        
        public static const MAP_DATA:String =   "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,1,1,1,0,2,0,1,0,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,3,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,1,1,0,0,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3\n" +
                                                "0,0,0,0,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3\n" +
                                                "1,1,0,0,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3\n" +
                                                "4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3\n" +
                                                "3,3,1,1,0,2,0,2,0,1,1,0,0,0,1,1,1,2,1,1,0,0,0,0,0,0,0,3,3,3\n" +
                                                "3,3,4,4,4,4,4,4,4,4,4,0,0,0,4,4,4,4,4,4,0,0,0,0,0,0,0,3,3,3\n" +
                                                "3,3,3,3,3,3,3,3,3,3,3,0,0,0,3,3,3,3,3,3,0,0,0,0,0,0,0,3,3,3\n" +
                                                "3,3,3,3,3,3,3,3,3,3,6,0,0,0,5,3,3,3,3,6,0,0,0,0,1,1,2,3,3,3\n" +
                                                "3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,3,3,3\n" +
                                                "3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3\n" +
                                                "3,3,3,3,3,3,6,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,1,1,3,3,3,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,3,3,3,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,4,4,4,4,4,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "1,1,1,0,2,0,1,1,0,3,3,3,3,3,0,1,1,0,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "4,4,4,4,4,4,4,4,4,3,3,3,3,3,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3\n" +
                                                "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3";
    }
}
Tip
Note that there needs to be a newline character (\n) between each row of data.

Now we're going to create our tilemap and add it to our game. To do that, you first create a Tilemap object and call build on it. Then add it to your state.

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
// Put this at the top of your class
private var tilemap:AxTilemap;
 
// This belongs in your create() method
tilemap = new AxTilemap().build(Resource.MAP_DATA, Resource.TILES, 24, 24, 3);
add(tilemap);

The first argument to the build function is the map data. The second is the image that will be used for the tiles. The third and fourth are how big each tile is, in pixels. The final parameter tells the tilemap the starting index of the solid tiles. To make things easier, you can put all your non-solid tiles first in the image (our grass and rock), and then tell it that everything before the X'th tile (3rd in this case) is not solid. Alternatively, after loading a tilemap, you can modify tiles separately to set which ones are solid.

Now if you run your game, you should get something similar to the following:


The upper left part of our tilemap

We only see the upper left because the tilemap is bigger than our window. Once we add a player, we'll tell the camera to follow the player so that he can explore the parts of the map that are off screen. Now, let's get rid of that white background, and add some blue sky. We'll do this by overriding the create() method in our main class (AxelBasic.as) and setting the background there. Any initialization you do at a game level should go in create() and NOT in the constructor. Add this to your AxelBasic.as file:

AxelBasic.as
ActionScript 3 Code
1
2
3
override public function create():void {
    Ax.background = AxColor.fromHex(0x95c7e8);
}

We set the background to a new AxColor. The AxColor constructor takes in the red, blue, and green components of the color, between 0 and 1. However, we can also construct one using AxColor.fromHex() as we do here, to use a hex color. Now, if you test your game once more, you'll see a blue sky in the background:


"Blue Skies" -Taffy

However, this background is quite boring! So now let's add in our background image. In our game state put this before your tilemap:

GameState.as
ActionScript 3 Code
1
2
3
var background:AxSprite = new AxSprite(0, 0, Resource.BACKGROUND);
background.scroll.x = background.scroll.y = 0;
add(background);

This creates a new sprite based on our background. We then set the scroll.x and scroll.y to 0, meaning that even though we move, the background is always going to stay in the same place (we'll talk about that more later in the tutorial). Then we add it to our game state. Test once more, and you should now see a wonderful background behind our tiles (make sure you added it before you added your tilemap!).


Darkness Falls
Sprites

Next up we're going to create the player sprite. The basic class you're going to use for creating sprites is AxSprite. One way to do it is to create a new AxSprite in your GameState, and put the logic into your update function. However, it's good practice to create a separate class for your game objects, so that you can keep all the game logic for each object separate. So let's create a Player.as class, and have it extend AxSprite. So for starters, we have:

Player.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
package {
    import org.axgl.AxSprite;
 
    public class Player extends AxSprite {
        public function Player() {
            
        }
        
        override public function update():void {
            super.update();
        }
    }
}

Now let's make the player take in the x and y coordinates you want to start him out at. Then we will call super to pass them up to the AxSprite constructor, which knows what to do with them. Next, we want to call load, the function to load the graphic for the sprite. Because it's an animated sprite, we need to pass in the size of each frame of our player. Then we'll add a few animations (standing, walking, jumping, and falling). We do that by calling addAnimation, which takes in name, frames (as an array), framerate, and looped. The name is what you'll use to identify the animation and play it later, the frames is an array of frames that you want it to play (the first frame in your sprite sheet is 0), the framerate is how fast it should play (how many times the frame should change per second), and the final parameter is whether or not to loop the animation, or stop when it finishes once. By default the framerate is 15, and looped is true, so if you want the defaults, you don't need to specify those parameters. I'll show you how to use these animations later. Once we've done this, we end up with the following:

Player.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package {
    import org.axgl.AxSprite;
 
    public class Player extends AxSprite {
        public function Player(x:Number, y:Number) {
            super(x, y);
            
            load(Resource.PLAYER, 32, 32);
            addAnimation("stand", [0, 4, 0, 5], 4, true);
            addAnimation("walk", [1,2,3,4, 5], 16, true);
            addAnimation("jump", [2], 1, false);
            addAnimation("fall", [3], 1, false);
        }
        
        override public function update():void {
            super.update();
        }
    }
}

Now we're going to add the player to our state and see the fruits of our labor! In your GameState.as, add the following:

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
// Put this at the top of your class
private var player:Player;
 
// This belongs in your create() method
player = new Player(10, 10);
add(player);

This will create a player sprite and place it at 10,10 on your map. Test your movie, and you should see the following:


Our player!

Now while we have a player, he doesn't really do much. So now we're going to add some movement!

Input and sprite movement

The main parameters that determine the movement of a player are x, y, velocity, acceleration. The x value tells you where it currently is on the x axis, and the y value tells you where it is on the y axis (0,0 is in the upper left). To move your sprite, you can change these values directly. However, even better is using the velocity and acceleration parameters. Velocity will tell your sprite how fast it should move every second. Velocity consists of x, y, and a (how fast it should increase it's x value, y value, and angle value, where angle is its rotation). If we set velocity.x to 100, that means it should move 100 pixels every 1 second. Acceleration tells your sprite how fast it should change its velocity every second, just like in real physics.

To demonstrate this, in your player's constructor, set velocity.x = 50;. Now test your game and you should see your player move slowly across the screen. Great! Now remove that, and let's add some input movement.

To test key presses, you'll want to use Ax.keys. The main methods you will use are Ax.keys.pressed (was a key just pressed this frame), Ax.keys.down (is a key being held down), and Ax.keys.released (was the key just released this frame). The list of keys you can use are constants in the AxKey. For example, Ax.keys.pressed(AxKey.SPACE) will return true only if you just pressed the space bar.

Lets add some movement to our player. First, in our constructor, let's set the terminal velocity. This is the maximum speed the player can move.

Player.as#Player
ActionScript 3 Code
1
maxVelocity = new AxVector(150, 500);

This limits our horizontal speed to 150, and our vertical speed to 500. Next, let's check the keyboard, and accelerate our player depending on what we're pressing. Add this to update before the super.update() call.

Player.as#update
ActionScript 3 Code
1
2
3
4
5
6
7
if (Ax.keys.down(AxKey.RIGHT)) {
    acceleration.x = 850;
} else if (Ax.keys.down(AxKey.LEFT)) {
    acceleration.x = -850;
} else {
    acceleration.x = 0;
}

Now if you test your game, you can use the left and right arrow keys to move left and right. However, if you let go, your player doesn't stop. He just keeps sliding in the direction he was moving. This is because while we changed his acceleration to 0, that just stops him from changing his speed, so he'll continue moving constantly at whatever speed he was at when you released the arrow. What we need is some kind of friction. You can access this in the drag variable. If you set drag.x to a positive number, each time you are not accelerating (when you release the keys), the player will slow down until he stops, like friction. The larger the number, the more he will slow. Let add some drag in the constructor:

Player.as#Player
ActionScript 3 Code
1
drag.x = 400;

Now let's also make it so that the player changes which way he is facing when you are moving left. Changing the facing to RIGHT when you hold right, and LEFT when you hold left:

Player.as#update
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
if (Ax.keys.down(AxKey.RIGHT)) {
    acceleration.x = 850;
    facing = RIGHT;
} else if (Ax.keys.down(AxKey.LEFT)) {
    acceleration.x = -850;
    facing = LEFT;
} else {
    acceleration.x = 0;
}
Tip
When facing is equal to flip, your sprite will be flipped horizontally. Be default, flip is LEFT, so your sprite flips when facing left. You can change flip to RIGHT or NONE to change this behavior.

Now if you test your game, you can move left and right, and your character faces the right direction. Also, when you left go, he will stop moving. Great! Now, let's add some gravity, so that the player isn't walking back and forth in the sky. In the constructor of your player, set the y acceleration to be a constant number. This is how you simulate gravity on your sprite:

Player.as#Player
ActionScript 3 Code
1
acceleration.y = 650;

Now test your game, and your player will fall off the screen! Not exactly what we wanted. There are two things at play here. First, the tilemap is not colliding with your player, so he falls right through it. Second, the camera isn't following the player, so the player just moves off the screen independently. Let's fix the collision first!

Collision

Collision in Axel ranges from being extremely simple, to being a bit more complex. The most basic form of collision is to put the following in your update function of your state, after super.update():

GameState.as#update
ActionScript 3 Code
1
Ax.collide(player, tilemap);

If you put this in there, and test your game, you'll see that everything works great! However, let's talk a little bit more about collision.

When you call collide, the game needs an algorithm to figure out what to collide with. One possible solution is to take everything in the first argument, and collide it against everything in the second argument (for our example, we only have a player and a tilemap, but we'll talk about groups soon). If we did this, it would be extremely inefficient. Imaging having 100 sprites, and 100 coins to collide. If we collide all the sprites against all the coins, that would require checking 100 * 100 = 10,000 things for collision! That's very inefficient when you have many objects.

One way to solve this inefficient is to partition the world into a grid. All objects in each cell of the world are placed in a bucket. Then when we collide, each object in the first argument is collided only against the objects in the same bucket(s) as that sprite. So unless all your sprites are stacked on top each other, this ends up being much more efficient. However, the downside to this is that each time you do it, you have to build all the buckets of objects. While this isn't huge, this does take a bit of time. The reason I bring this up, is that if you are colliding a player against 5 coins, you don't want to go through the hassle of building that grid every frame, and using the first inefficient version of the collision is actually much more efficient.

Because of these different scenarios, you can specify the algorithm used for colliding. You construct an object (either a collider or a grid), and can use that for the collision call. A great thing about this is that you can construct the object once, and reuse it. This ends up being much more efficient (you can create a grid of buckets, and each frame it can just clear each bucket, rather than having to create them from scratch).

Now, tilemaps are a bit different. If you collide with a tilemap, you can't use a grid. This is because a tilemap is essentially a grid, so its much more efficient to use special logic for colliding against a tilemap. For it to work properly, you must use collider version, and not the grid version.

Now, to create the everything vs everything version of collision, you create a new instance of an AxCollider object, and pass that as the fourth argument to the collide function (the third argument is the function that gets called every time something collides. To create the grid version, create an instance of AxGrid. When using an AxGrid, you must pass it the width of the world and the height of the world, so that it knows how to overlay the grid onto the world. You can also pass it the number of rows and columns to use in the grid (you can experiment with these values to get the best performance). If you don't pass them, it assumes you want 10 rows and 10 columns.

Rather than making the game create a new collision object every frame, let's improve our collision. Add a new private static variable called TILEMAP_COLLIDER, and initialize it to be a new AxCollider in your create function. Let's also look forward and create a new grid collider for colliding with the gems we will collect later. Our GameState should now look like:

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package {
    import org.axgl.Ax;
    import org.axgl.AxState;
    import org.axgl.collision.AxCollider;
    import org.axgl.collision.AxCollisionGroup;
    import org.axgl.collision.AxGrid;
    import org.axgl.tilemap.AxTilemap;
 
    public class GameState extends AxState {
        private static var TILEMAP_COLLIDER:AxCollisionGroup;
        private static var GEM_COLLIDER:AxCollisionGroup;
        
        private var tilemap:AxTilemap;
        private var player:Player;
        
        override public function create():void {
            super.create();
            
            var background:AxSprite = new AxSprite(0, 0, Resource.BACKGROUND);
            background.scroll.x = background.scroll.y = 0;
            add(background);
            
            tilemap = new AxTilemap().build(Resource.MAP_DATA, Resource.TILES, 24, 24, 3);
            add(tilemap);
            
            player = new Player(10, 10);
            add(player);
            
            TILEMAP_COLLIDER = new AxCollider;
            GEM_COLLIDER = new AxGrid(tilemap.width, tilemap.height);
        }
        
        override public function update():void {
            super.update();
            
            Ax.collide(player, tilemap, null, TILEMAP_COLLIDER);
        }
    }
}

Great! Now that we've got collision with the tilemap, we can solve the next problem: Getting the camera to follow the player.

Camera

Controlling the camera is very easy. You can control it manually by changing Ax.camera.x and Ax.camera.y, but you can also set it to always center on a single object. To do that, add the following to the end of your GameState's create function:

GameState.as#GameState
ActionScript 3 Code
1
Ax.camera.follow(player);

Test your game and you'll find that the camera now follows you around the world. The problem is that the camera can move outside the world, so if you walk off the edge, the camera will follow you. Let's fix that by setting how far the camera can move in your create function:

GameState.as#GameState
ActionScript 3 Code
1
Ax.camera.bounds = new AxRect(0, 0, tilemap.width, tilemap.height);

Perfect! Now we can move about our world. That was easy. Let's make some improvements to our player though. First, we'll change his spawn location to the bottom left, next we'll limit how far he can move (he can't move outside the world, so no more walking off the edge), and then we'll add the ability to jump.

Player Improvements

First, let's change our player's spawn location. Simply change the x and y you construct your player with in GameState to be 24 and 609, and you're done!

Next, we'll limit the player's movement to the world. To do this, our player needs to know how big the world is. From within the player class there's actually quite a few ways to do this. The first is we can hardcode it (our world is 30 tiles wide and 30 tiles tall, and each tile is 24 pixels, so we know exactly how big our world is). However, this would need to change if we change our map. Another way is to pass in tilemap.width and tilemap.height to our player class. Another way is that we can get the current state using Ax.state. Because when we create our player we know we are in a GameState, so we can cast it to a GameState. Then if we make our tilemap public, we could do (Ax.state as GameState).tilemap.width. All of these solutions work, but the one we'll choose is to pass the world size to our player object. So modify the player to take in worldWidth and worldHeight in its contructor, and then pass them in gamestate. Then, we can set our world bounds in our player's constructor as follows:

Player.as#Player
ActionScript 3 Code
1
worldBounds = new AxRect(0, 0, worldWidth, worldHeight);

Now test your game and you'll find you can't move off the screen anymore. Fabulous!

Finally, let's add the ability to jump. In your player's update, check if the space key was pressed, and if so, set the velocity.y to a large negative number. For example:

Player.as#update
ActionScript 3 Code
1
2
3
if (Ax.keys.pressed(AxKey.SPACE)) {
    velocity.y = -360;
}

Now test your game and hit space. Great! The jumping feels floaty, and you can play around with the gravity and jump velocity in order to get it to feel might more tight, if you would like. However, we do have a problem. If you hit jump many times, you can jump midair! We can solve this by checking what side of our player we're colliding on. Every frame if you collide with something, flags are set to show which sides are touching a solid object. We only want to be able to jump if the bottom of our player is touching something solid (standing on the ground). We can easily add this by using the touching method. Alter your jump code to the following:

Player.as#update
ActionScript 3 Code
1
2
3
if (Ax.keys.pressed(AxKey.SPACE) && isTouching(DOWN)) {
    velocity.y = -360;
}

Test once more, and you'll see that you can now only jump if you're touching the ground. Great!

Tip
You can test multiply checks at the same time in a first different ways. isTouching(DOWN | RIGHT) works, as down isTouching(DOWN) || isTouching(RIGHT) as well. The first is more efficient though, so try to | the directions together and make one single function call. Alternatively, you can use the touching variable direction, as follows: if (touching & (DOWN | RIGHT)). This is even more efficient. Finally, you can do the same thing to test if your character was touching the previous frame, using wasTouching(UP) and touched & UP.

Now, one last change, let's add in the animations we added earlier and make the player fade colors. We're simply going to check if the player is moving up or down, and play the jump/fall animations. If he isn't, check if he's moving sideways, and play walking if so. If he isn't, we'll play the standing animation. It's not perfect, but I'll leave it as an exercise to the reader to improve the various animations! When playing the walk animation, we are playing around with the animation delay, so that as your slime comes to a stop, the animation slows down. This is just a nice little touch, but isn't necessary! We're also setting the color of player here. We take advantage of Math.sin() to make the colors fade separately from one another, so that the red, green, and blue components of the color don't change at the same rate. Because of this, you'll see the player fade between various colors. Another nice little touch!

Player.as#update
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
if (velocity.y < 0) {
    animate("jump");
} else if (velocity.y > 0) {
    animate("fall");
} else if (velocity.x != 0) {
    animate("walk");
    animationDelay = 1 / Math.max(6, (Math.abs(velocity.x) / maxVelocity.x) * 16);
} else {
    animate("stand");
}
 
var t:Number = Ax.now / 500;
setColor((Math.sin(t * 0.9) + 1) / 2 + 0.5, (Math.sin(t * 1.3) + 1) / 2 + 0.5, (Math.sin(t * 1.7) + 1) / 2 + 0.5);
Groups and Overlap

Our next step is to add objects that we can collect. These will be in the form of Gems. Our goal is to have a set of golden gems on the level that we can touch to collect. When building games, it's often advantageous to group things together. For example, having all the gems in a separate group is great not only because we can add or remove that group to affect all of them, but also because if they are all in a separate group, we can tell the player that if he touches anything in that group, do something specific, without affecting anything else in the game.

So let's begin by creating a new. To do you, you will use the AxGroup class. In your GameState, create a new gems group:

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
// Goes at the top of your state
private var gems:AxGroup;
 
// Goes in your create method
gems = new AxGroup;
add(gems);

What this does is create a new group, and adds it to our state so that it will get updated and drawn. Now, if we wanted to add things to it, we could create new AxSprites and call gems.add() to add them to the group. In order to add them all quickly, we're going to create another large string mapping of all the coin locations in our Resource class:

Resource.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static const GEMS_DATA:String =  "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + 
                                        "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0";

With this, we can iterate through the entire string, and when we encounter a 1, create a gem at that position, and add it to the gems group. Here's the code to do that:

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
var gemArray:Array = Resource.GEMS_DATA.split(",");
for (var i:uint = 0; i < gemArray.length; i++) {
    if (gemArray[i] == 0) {
        continue;
    }
    var gemX:uint = i % 30;
    var gemY:uint = Math.floor(i / 30);
    var gem:Gem = new Gem(gemX, gemY);
    gems.add(gem);
}

Now the next step is to create the actual Gem class. This will be a lot like how we created our Player, but simpler. One thing to note is that when we calculate gemX and gemY, this is the x and y position in tiles. For example, we would add a coin at 5, 5 -- but this really means the pixel location would be 120, 120, because each tile is 24 pixels. Let's keep that in mind as we write our Gem class:

Gem.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
package {
    import org.axgl.AxSprite;
 
    public class Gem extends AxSprite {
        public function Gem(tileX:uint, tileY:uint) {
            super(tileX * 24, tileY * 24, Resource.GEM);
        }
    }
}

The biggest thing of note here is that we aren't calling load. Instead, we're passing the graphic to the last parameter of AxSprite's constructor. If we are loading a graphic, and it's a single image, we can pass it in super and it will load it as a single frame. Now test your game, and you should see the following:


Coins ahoy!

You'll probably notice that we can't actually collect them yet. Let's fix that now. Just like we called collide with our tilemap, we want to do something similar with gems. However, since they're not actually solid and we don't want them to prevent us from moving, we should call overlap instead of collide. They are very similar, but overlap will simply call the function you give it if there is a collision. We'll take advantage of the AxGrid we made earlier to make it more efficient. Add the following to your update function in GameState:

GameState.as#update
ActionScript 3 Code
1
Ax.overlap(player, gems, onHitGem, GEM_COLLIDER);

This code tells it to overlap the player against the gems group (and because it's a group, we'll overlap against everything in the group), and if there's a collision, call the onHitGem function. To do the overlap, use the GEM_COLLIDER collision group each frame, rather than creating a new one. Now, we told it to call onHitGem when there is an overlap, so we must implement that function. When using a callback with overlap or collide, your function should look something like this:

Overlap Callback
ActionScript 3 Code
1
2
3
private function callback(entity1:AxEntity, entity2:AxEntity):void {
    // do something with entity1 and entity2 here
}

The entity1 will be the entity from the first parameter of overlap or collide that collided, and entity2 will be the from the second parameter. Here, our first parameter is the Player and the second is the gems group, which only contains objects of type Gem. That means we know that the first parameter to the callback will be the Player and the second will be a Gem. If, however, we were colliding against a group with different types, we'd want to make the parameter type AxEntity, and check the type inside, for example:

Example Callback
ActionScript 3 Code
1
2
3
4
5
6
7
private function callback(entity1:Player, entity2:AxEntity):void {
    if (entity2 is Gem) {
        // do something
    } else if (entity2 is Enemy) {
        // do something else
    }
}

You can structure this any way you want. For example, you could have all objects extend a base class Entity that has a collide function that you call. However, because we know the types that will be passed to the callback, ours is going to be very simple: We just want to destroy the gem. So add this to your GameState:

GameState.as
ActionScript 3 Code
1
2
3
private function onHitGem(player:Player, gem:Gem):void {
    gem.destroy();
}

All AxSprites have a destroy() method that destroys them. They will still be present in the group, but they will not be updated or drawn each frame. You can get a dead object back from an AxGroup using group.recycle() or you can clean up the group by removing all dead objects by calling group.cleanup(). Both are useful. However, here, we aren't constantly creating gems, so we can leave the dead ones around without any problems. If you test your game, you can now touch the gems to collect them. But let's make it more interesting, let's add a cool particle effect when you touch a coin!

Particle Engine

One way to add cool effects to your game is to using the particle system. To do this, you first create an effect. Then you register that effect with the system, and add the group it gives you to your state. Then, whenever you want to show the particle effect, you call emit. So let's walk through this!

First, let's create a particles group in our GameState. We'll add it to the top of our create method, so that the particles show up below everything else, rather than on top.

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
// This goes on top of your state
private var particles:AxGroup;
 
// This goes at the top of your create() method
particles = new AxGroup;
add(particles);

Now, we need to create our effect. We're going to name it collect-coin, we'll use the Resource.PARTICLE image as the particle, and we'll specify that we'll only show a max of 10 of them at the same time.

GameState.as#Gamestate
ActionScript 3 Code
1
var effect:AxParticleEffect = new AxParticleEffect("collect-coin", Resource.PARTICLE, 10);

Now we'll set some properties on the effect, to determine what the effect looks like. You should look through the documentation for the full list of properties on AxParticleEffect, but we'll use some of them here:

GameState.as#Gamestate
ActionScript 3 Code
1
2
3
4
5
6
effect.xVelocity = new AxRange(-70, 70);
effect.yVelocity = new AxRange(-70, 70);
effect.lifetime = new AxRange(0.5, 1.5);
effect.amount = 100;
effect.blend = AxBlendMode.PARTICLE;
effect.color(new AxColor(0.3, 0.3, 0.3), new AxColor(1, 1, 1), new AxColor(0.3, 0.3, 0.3), new AxColor(1, 1, 1));

Here we set the random velocity the particles can have, how long they live (the lifetime), the amount that gets emitted when you show it (100), the blend mode (we choose one that looks well with particles, because it's additive the makes the colors get brighter and whiter when they overlap), and the color (we tell it to pick a very random color to start and end at). Because we have a very bright background, the colors won't be too noticeable with this blend mode, but that's okay.

The final step is to register it with the particle system. When you call register, it gives you a group of objects that you should add to your state. We'll add it to our already created particles group:

GameState.as#Gamestate
ActionScript 3 Code
1
particles.add(AxParticleSystem.register(effect));

Great! Now whenever we want to show it, we'll call emit, so let's call it in our onHitGem method, so it shows this effect when we collect a gem:

GameState.as#onHitGem
ActionScript 3 Code
1
AxParticleSystem.emit("collect-coin", gem.center.x, gem.center.y);

Here is the current state of our GameState class:

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package {
    import org.axgl.Ax;
    import org.axgl.AxGroup;
    import org.axgl.AxRect;
    import org.axgl.AxState;
    import org.axgl.collision.AxCollider;
    import org.axgl.collision.AxCollisionGroup;
    import org.axgl.collision.AxGrid;
    import org.axgl.particle.AxParticleEffect;
    import org.axgl.particle.AxParticleSystem;
    import org.axgl.render.AxBlendMode;
    import org.axgl.render.AxColor;
    import org.axgl.tilemap.AxTilemap;
    import org.axgl.util.AxRange;
 
    public class GameState extends AxState {
        private static var TILEMAP_COLLIDER:AxCollisionGroup;
        private static var GEM_COLLIDER:AxCollisionGroup;
        
        private var tilemap:AxTilemap;
        private var player:Player;
        private var gems:AxGroup;
        private var particles:AxGroup;
        
        override public function create():void {
            super.create();
            
            var background:AxSprite = new AxSprite(0, 0, Resource.BACKGROUND);
            background.scroll.x = background.scroll.y = 0;
            add(background);
            
            particles = new AxGroup;
            add(particles);
            
            var effect:AxParticleEffect = new AxParticleEffect("collect-coin", Resource.PARTICLE, 10);
            effect.xVelocity = new AxRange(-70, 70);
            effect.yVelocity = new AxRange(-70, 70);
            effect.lifetime = new AxRange(0.5, 1.5);
            effect.amount = 100;
            effect.blend = AxBlendMode.PARTICLE;
            effect.color(new AxColor(0.3, 0.3, 0.3), new AxColor(1, 1, 1), new AxColor(0.3, 0.3, 0.3), new AxColor(1, 1, 1));
            particles.add(AxParticleSystem.register(effect));
            
            tilemap = new AxTilemap().build(Resource.MAP_DATA, Resource.TILES, 24, 24, 3);
            add(tilemap);
            
            player = new Player(24, 609, tilemap.width, tilemap.height);
            add(player);
            
            gems = new AxGroup;
            add(gems);
            
            var gemArray:Array = Resource.GEMS_DATA.split(",");
            for (var i:uint = 0; i < gemArray.length; i++) {
                if (gemArray[i] == 0) {
                    continue;
                }
                var gemX:uint = i % 30;
                var gemY:uint = Math.floor(i / 30);
                var gem:Gem = new Gem(gemX, gemY);
                gems.add(gem);
            }
            
            TILEMAP_COLLIDER = new AxCollider;
            GEM_COLLIDER = new AxGrid(tilemap.width, tilemap.height);
            
            Ax.camera.follow(player);
            Ax.camera.bounds = new AxRect(0, 0, tilemap.width, tilemap.height);
        }
        
        override public function update():void {
            super.update();
            
            Ax.collide(player, tilemap, null, TILEMAP_COLLIDER);
            Ax.overlap(player, gems, onHitGem, GEM_COLLIDER);
        }
        
        private function onHitGem(player:Player, gem:Gem):void {
            AxParticleSystem.emit("collect-coin", gem.center.x, gem.center.y);
            gem.destroy();
        }
    }
}

Now run your game, and collect the coins. You should see a particle effect similar to this:


Particles!

Even though we're emitting hundreds of particles, you should see no slowdown if you've got a modern GPU.

Tip
At any time, you can press the TILDE (~) or BACKSLASH (\) keys to show the debug console, telling you the version of Axel you're running, your framerate, update/draw calls and times, and whether you are in software or hardware mode. If your game is running in software mode for you, you may not have a GPU that supports Stage3D, and may find that Axel isn't a good fit for you.
Text

Now that we can collect coins, let's add some small text to show how many we've collected. To do this we'll use the AxText class. This class supports using bitmap fonts, along with system and embedded fonts. If you'd like to read about how to use those, feel free to check out the documentation. For this, we'll use the built in font.

Start by adding a new gem counter at the top of your GameState.

GameState.as
ActionScript 3 Code
1
public var gemsCollected:uint = 0;

Now let's increment that counter in our onHitGem function.

GameState.as#onHitGem
ActionScript 3 Code
1
gemsCollected++;

Now, let's just create a new AxText object using the default font, and update its value every frame to keep track of the correct number of gems we've collected.

GameState.as
ActionScript 3 Code
1
2
3
4
5
6
// Top of your class
private var gemText:AxText;
 
// In the constructor
gemText = new AxText(10, 10, null, "Gems: 0");
add(gemText);

Here we just created the gemText variable, and added it to our game. We start it out by saying "Gems: 0", but we'll change it every frame, so you won't actually see that string shown, unless you don't change it.

GameState.as#update
ActionScript 3 Code
1
gemText.text = "@[0,0,0]Gems: @[200,70,70]" + gemsCollected;

Now every frame we update it to show the string in the format Gems: 0. However, we're taking advantage of some coloring code here. In your AxText strings, you can change the color on the fly using the syntax @[R,G,B] where R is the red component (0 - 255), G is the green component, and B is the blue component. Here we use 0,0,0 (black) for the word Gems, and then 200,70,70 (a reddish color) for the actual value. If you'd like to reset to the default color, youcan use @[] to reset to @[255,255,255]. If you change the overall color of the text (such as using gemText.setColor(0.4, 0.8, 0.2) those colors are multiplied together to get the final color. Because our overall color was not set (so it is white), it will use the exact color in our @[] syntax.

Now test your game! Unfortunately, you won't see your text. However, if you climb up to the upper left of your map, you'll see it! This is because we added it at 10,10, so that's where it put it, in the upper left corner of the map. However, what we want is for it to be at 10, 10 in screen space, and follow us around. We can achieve this using the scroll variable. Add the following code when you create your text:

GameState.as#GameState
ActionScript 3 Code
1
2
gemText.scroll.x = gemText.scroll.y = 0;
gemText.scale.x = gemText.scale.y = 4;

By default, the scroll value is 1, meaning that for every 1 pixel the camera moves, it should move 1 pixel. You can set it anywhere between 0 and 1. When you set it to 0, you are effectively telling it that it should multiply the camera movement by 0, meaning it won't even use the camera position in determining where to draw. Use 0 for static components such as a UI. You can use values between 0 and 1 to achievement parallax scrolling backgrounds, and similar effects. We also scale it up to match the scaled up graphics of the tutorial.


We now have text!
Sounds and Music

Now, for the final touch, we're going to add some background music, and a sound when you collect coins. We're going to keep it extremely simple. First, make sure you have the sound files in your res directory that we're going to use: simplex.mp3 and gem.mp3. You can find these in the source download for this tutorial. Music is courtesy Kevin MacLeod. The music very short so as to keep the file size of the tutorial down. Once you're sure they're in our resource folder, let's add them to the Resource.as file:

Resource.as
ActionScript 3 Code
1
2
[Embed(source = "/simplex.mp3")] public static const MUSIC:Class;
[Embed(source = "/gem.mp3")] public static const SOUND:Class;

In the constructor of your GameState, call Ax.music to play the music. By default, this will automatically loop the song:

GameState.as#GameState
ActionScript 3 Code
1
Ax.music(Resource.MUSIC);

And in your callback for collecting a gem, play the using using Ax.sound. Unlike the music command, this will not repeat it (but takes in a parameter to allow you to loop it). It also takes in a parameter for the volume (1 is default, and normal level). We'll set the volume to 0.8 to make the sound a bit quieter. Also, we'll set the start location (in ms) to 70, to help offset the silence at the beginning of the sound.

GameState.as#onHitGem
ActionScript 3 Code
1
Ax.sound(Resource.SOUND, 0.8, false, 70);

Test your game, and you should hear music repeating in the background, and a sound will play when you collect gems!

Congratulations! You have successful used the Axel library to create a simple platforming game. To learn more, feel free to check out some of the tutorial games. These are open source games where the source is documented to help you learn more and more about Axel. If you want to learn everything there is to know, check out the documentation pages. It contains documentation about all the classes and functions that are available to you in the library. Also, feel free to browse the source of the library, and read the comments in the files (these comments are what generates the documentation page). You can also change the library as needed to fit your needs.

Good luck!

Browse Source
Download Source
Discuss Tutorial