Flixel Adventure Game Tutorial – Part 4: The world

Index
Previous Part: Characters

Flixel Adventure Map

In the previous part we have created a basic Character class that is able to move around on a grid, now we will create a world that fits the same grid. We will also look into the Character interaction with the world, such as using doors and collision-detection.

Spritesheet for tutorial world

The picture on the top of this post is a part of the map that we will me using in our game. Before we can show anything though, we will have to load both the data of the map and the sprites that make up the map. The map is a text file containing the index of a sprite on the world-spritesheet, which Flixel translates into mapping all the corresponding sprites to the correct number and positions on-screen. You can get the map here, the spritesheet we will be using is the one above this paragraph. Save both files in your project’s com/data folder.

Import the FlxTilemap class in your PlayState class.

import org.flixel.FlxTilemap;

Add the following variables, these will be needed to load the map and the spritesheet of the map.

// Loading the map.
[Embed(source = "data/Map1.txt", mimeType = "application/octet-stream")] private var txtMap:Class;
// Loading the map spritesheet.
[Embed(source = "data/World.png")] private var imgMap:Class;
// Map variable
public var map:FlxTilemap;

Add the following code to the constructor, before all the other code you have in there. If you don’t do this, the map will end up being drawn on top of all the other objects and you will not be able to see the character.

// Setting up the map.
map = new FlxTilemap();
map.startingIndex = 0;
map.drawIndex = 0;
map.loadMap(new txtMap, imgMap, 16, 16);
add(map);

Compile your game and walk around a bit, you will notice that you can walk on top of trees and on water, we’ll have to fix that.

Flash Game Tutorial Adventure Part 4 Picture 2

Go to your Character class and import the FlxTilemap class and make a variable named map of said class.

private var map:FlxTilemap;

Add another argument to the constructor, also a FlxTileMap variable.

public function Character(x:uint, y:uint, image:Class, map:FlxTilemap) 

Now, add the following line to the constructor.

this.map = map;

Go to the PlayState class and add the extra argument to the Character variable instantiation.

char = new Character(5, 5, imgChar, map);

We are now able to use the map we have created in the PlayState Class, in our Character class. We can now write a function that checks if the index of the tile that the Player wants to move to is not a wall (by wall I mean everything that is not walkable is a wall, so for example a tree is also a wall).

First, add this helper function to your Character class, it calculates the x and y positions of the next step and saves these in a Point.

private function getNextTile(dir:String):Point
{
	// Setting up the point.
	var next:Point = new Point();
	next.x = x;
	next.y = y;
			
	// Getting position of next step.
	switch(dir)
	{
		case "UP":
			next.y -= 16;
			break;
		case "DOWN":
			next.y += 16;
			break;
		case "LEFT":
			next.x -= 16;
			break;
		case "RIGHT":
			next.x += 16;
			break;
	}	
	return next;
}

You will also have to import the Point class.

import flash.geom.Point;

Now for the wall check, we will use the helper function we have just created.

// Function returns false if there is a wall.
private function checkWall(dir:String):Boolean
{
	// To 'close' the interior, black needs to be wall too, index 0.
	if (map.getTile(getNextTile(dir).x / 16, getNextTile(dir).y / 16) > 6 ||
	map.getTile(getNextTile(dir).x / 16, getNextTile(dir).y / 16) == 3 ||
	map.getTile(getNextTile(dir).x / 16, getNextTile(dir).y / 16) == 0)
		return false;
				
	return true;
}

In the move function, add the checkWall function to the move-check if-statement. Create an else-if to make sure that if a player is not allowed to walk to a certain place, it will be allowed to look at it.

if (checkWall(dir))
{
	// Setting variables.
	move_dir = dir;
	moving = true;
	move_dis = 16;
					
	// A switch statement works like a combination of else-if-statements.
	switch(dir)
	{
		case "UP":
			play("UP_WALK");
			break;
		case "DOWN":
			play("DOWN_WALK");
			break;
		case "LEFT":
			play("LEFT_WALK");
			break;	
		case "RIGHT":
			play("RIGHT_WALK");
			break;
	}
}
else
{
	look(dir);
}

Run the game and try to walk the road to the east, the character will exit the screen, now we can’t see him any more! Fortunately, Flixel has this covered. By add the following code to the PlayState’s update function, the screen will always focus on the ‘char’ object.

// Update Camera.
FlxG.followBounds(0, 0, 9999 , 9999);
FlxG.follow(char);

Note: The functions for updating the camera above have been deprecated in versions of Flixel later than 2.43. The latest version of Flixel works with instances of so-called FlxCamera objects. When you have downloaded a later version you will get an error, you can substitute the code above by the following lines:

// Update Camera.
FlxG.camera.setBounds(0,0,9999,9999);
FlxG.camera.follow(char);

Our little friend should now be able to reach the house. Actually, he could already do that, we were just unable to see it! We can check if there is a door the same way we check if something is a wall.

// Function returns true if there is a door.
private function checkDoor(dir:String):Boolean
{
     if (map.getTile(getNextTile(dir).x / 16, getNextTile(dir).y / 16) == 6 || 
         map.getTile(getNextTile(dir).x / 16, getNextTile(dir).y / 16) == 5)
              return true;
				
   return false;
}

Note that we check for both index 5 and 6, that is the door sprite and the doormat sprite, the latter one will function as an exit for the interior. Now we will make a list with where the entry is and where the exit is. Add the following variable to the Character class. A static variable will be the same for all instances of the class, so each character will be able to use the same door variable.

// Array holding doors entry and exit points.
public static var doors:Array = new Array(33,6,13,24);

In case the numbers don’t make sense to you: the door is located on tile (33,6) and the doormat is located on tile (13,24). We will use this array to teleport the player if it is walking on a door.

Add the following code after the switch statement in the move function. What it does is loop through the doors array and check if the next step is either the entry or the exit point, and teleports the player to the corresponding door(mat).

if (checkDoor(dir))
{
	for (var i:int = 0; i < doors.length; i += 4)
	{
		if (getNextTile(dir).x / 16 == doors[i] &&
		    getNextTile(dir).y / 16 == doors[i + 1])
		{
		    x = doors[i + 2] * 16;
			y = doors[i + 3] * 16;
		}
		else if (getNextTile(dir).x / 16 == doors[i + 2] &&
			getNextTile(dir).y / 16 == doors[i + 3])
		{
			x = doors[i] * 16;
			y = doors[i + 1] * 16;
		}
	}
}

You are now able to walk around in your world and enter the house. As you approach the edge of what is known, you will see a lot of black, this is caused by the 0’s in the map file, you can change the world to your liking by editing the map file with a text-editor! Note that the interior of the house is located on the same plane as the exterior, doing this was a design choice. When you have completed this tutorial you could choose to make every world-part in a different map file. You can also choose to not have any interiors at all, and/or have an ‘open-roof’ kind of world.

In the next part we will focus on creating other characters to interact with, and starting with some basic dialogue.

You can download the code for this part here: Flixel Adventure Game Tutorial part 4 code.

Next Part: Interactions

Join the Conversation

12 Comments

  1. Those two functions:

    FlxG.followBounds(0, 0, 9999 , 9999);
    FlxG.follow(char);
    

    doesn’t exists.

    Are they deprecated?, is there any substitute?

    1. They might have been deprecated in later versions of Flixel, the version used in the tutorial is 2.43. The newest version of Flixel works with instances of FlxCamera that are used to track the position of the screen.

    2. these two functions are not depricated, they just moved. They are available through the camera object of FlxG, rather than direct through FlxG.

      FlxG.camera.setBounds(0,0,9999,9999);
      FlxG.camera.follow(player);
      

      works as expected.

      1. Thank you for your comment, I have added the substitute to the text in this part of the tutorial. The newest version of Flixel works with instances of FlxCamera that are used to track the position of the screen. The tutorial was written with for Flixel version 2.43, things used to be different. 🙂

  2. Hey, I’ve been follwing this tutorial and it’s been great so far but i’m having an issue at

    map.startingIndex = 0;
    map.drawIndex = 0;
    

    It’s giving me an error “access of possibly undefined property startingIndex through a reference with a static type org.flixel:FlxTilemap”

    I’m pretty new to flixel and as3 so I was wondering if maybe you know what I’m doing wrong here. Thanks.

    1. That error occurs when you are using a version of Flixel newer than 2.43. You can try replacing this:

      // Setting up the map.
      map = new FlxTilemap();
      map.startingIndex = 0;
      map.drawIndex = 0;
      map.loadMap(new txtMap, imgMap, 16, 16);
      add(map);
      

      With this:

      // Setting up the map.
      map = new FlxTilemap();
      map.loadMap(new txtMap, imgMap, 16, 16, 0, 0, 0);
      add(map);
      
  3. I’m having trouble with this part of the tutorial. Could you post the completed source code to verify against? Am I supposed to be able to walk through trees? It seems this happens when I walk through the middle of tiles. Also, in general it seems that the collision detection is really off and I can’t get the door teleportation to work.

  4. Hi there Kees,

    I’m using the latest version of flixel and having some trouble setting up the map in the first step. The updated code you suggested:

    map = new FlxTilemap();
    map.loadMap(new txtMap, imgMap, 16, 16, 0, 0, 0);
    add(map);
    

    doesn’t work due to the embed occurring as Class and loadMap() accepting only a string as the first argument. I typecasted Map1.txt:Class to String but now I’m getting “Error #2015 Invalid bitmap data.”

    Can you provide insight to what is happening? I looked around flixel forums but could find anything relevant… I think.

    Thanks,
    Katz

    1. It is not necessary to cast txtMap to a String when using the proper mimeType:

      [Embed(source = 'data/Map1.txt', mimeType = 'application/octet-stream')] private var txtMap:Class;
      

      I have sent you an e-mail, if you reply with your code I can take a look at it. Besides that, there is also a source code included with this post which might help.

Leave a comment

Leave a Reply to Kees Cancel reply

Your email address will not be published. Required fields are marked *