Flixel Adventure Game Tutorial – Part 5: Interactions

Index
Previous part: The World

In the previous part we have made a world for our main character to live in and walk around. However, he is a bit sad and lonely and we want to give him a pal to talk to. The sad truth is however, that he cannot talk at all, since we have not written any functions concerning dialogue for our character. In this part of the Flixel Adventure Game Tutorial, we will address the issue of dialogue and interaction with other characters.

Let’s start by adding another character to the world, to avoid confusion we will use a different character-spritesheet. Download the image and save it as ‘Char2.png’ in your com/data folder.

Guy with glasses spritesheet

Create a new private variable for this char and import the image in the PlayState.

// Char 2
private var char2:Character;
[Embed(source = "data/Char2.png")] private var imgChar2:Class;

And instantiate the variable in your constructor, do this after you have created the map, otherwise the sprite will be drawn underneath the map and you won’t see it.

// Second character
char2 = new Character(10, 5, imgChar2, map);
add(char2);

Run the game, you will now see two characters on-screen, one that you can control and the other that is just standing there. You can also walk on top of the other character, we’ll have to fix that. We want to have a list of all the characters so we can easily perform checks on all of them.

public static var characterList:Array = new Array();

In the constructor, add all the characters you make to the characterList array, starting with the character that you control (the player).

characterList.push(char);
characterList.push(char2);

In the Character class we will write a function that will check if there is another character on the next tile. We will use the list that we have created in the PlayState class, because it is static we can call it in other classes. Note that this is not a perfect way of programming OOP-style, but for now it’ll do.

// Returns false if there is an NPC
public function checkNPC(dir:String):Boolean
{
	for (var i:int = 0; i < PlayState.characterList.length; i++)
	{
		if (getNextTile(dir).x == PlayState.characterList[i].x &&
		   getNextTile(dir).y == PlayState.characterList[i].y)
			return false;
	}
				
	return true;
}

While we are at it, add the following helper function that we will use later on.

// Returns npc in front of char.
public function getCheckNPC(dir:String):Character
{
	for (var i:int = 0; i < PlayState.characterList.length; i++)
	{
		if (getNextTile(dir).x == PlayState.characterList[i].x &&
		   getNextTile(dir).y == PlayState.characterList[i].y)
			return PlayState.characterList[i];
	}
				
	return null;
}

In the move function, add the checkNPC function to the checkWall if-statement. The order of statements will then look like this:

if (!moving && move_dis <= 0) 
{
	if (checkWall(dir) && checkNPC(dir)) 
	{
	// Rest of code.

Run the game, you will see that you can’t walk through the other character anymore. Let’s take a look at the dialogue, which is an important factor in adventure games. Our “Hello World” text is still there, but once you move around, it will remain fixed on the left top corner of the world. Flixel has a useful variable that allows objects to scroll with the screen called ‘scrollfactor’. Add the following code to the text object in the PlayState constructor.

text.scrollFactor.x = 0;
text.scrollFactor.y = 0;

When you walk around the text will remain fixed on the screen, like an overlay. The text is hard to read, if we give it a background it will be much better. Import FlxSprite and create a new variable.

private var dialogBG:FlxSprite;

And add the following code above the text variable instantiation.

// Dialog background.
dialogBG = new FlxSprite(0, 0);
dialogBG.createGraphic(FlxG.width, 42, 0xff333333);
dialogBG.alpha = 0.75;
dialogBG.scrollFactor.x = 0;
dialogBG.scrollFactor.y = 0;
add(dialogBG);

We want the text to stretch the whole length of the screen, minus the edge on the left and right side. Replace the first text line with the following.

text = new FlxText(5, 5, FlxG.width - 10, "Hello World!");

We want to access the move_dir variable in our Character class, but it is declared as private, so make it public.

public var move_dir:String = "DOWN";

Switch back to the PlayState, the following code will check if space is pressed, and if there is an npc in front of the player, it will make this npc face the player.

First declare a new private variable.

private var tempChar:Character;

Then a big chunk.

else if (FlxG.keys.justPressed("SPACE"))
{
	// If player is not moving and there is an npc in front
	if (!char.checkNPC(char.move_dir))
	{
		// Tempdir is inverse direction of player.
		var tempDir:String = "";
		switch(char.move_dir)
		{
			case "UP":
				tempDir = "DOWN";
				break;
			case "DOWN":
				tempDir = "UP";
				break;
			case "LEFT":
				tempDir = "RIGHT";
				break;
			case "RIGHT":
				tempDir = "LEFT";
				break;
		}
		tempChar = char.getCheckNPC(char.move_dir);
		// Make npc look towards player
		tempChar.move_dir = tempDir;
	}
}

Run the program and try to talk to the other character, as you will see it will turn to face the player. Just turning the npc’s around however, isn’t enough. What we want is to be able to chat to each npc. We can give each character an array with Strings to hold as it’s dialogue, declare the following variable in the Character class.

// Variable holding text for talking.
public var dialog:Array = new Array();

In our PlayState we will have to declare each sentence in the conversation for each char (except for the player char).

char2.dialog = ["Richard: Hey how are you today!",
		"You: I'm ok, thank you.",
		"Richard: Do you want some candy?",
		"You: No."];

To change the text, we need to call the text-storage of the npc we are talking to.

First declare an index variable, which we will use to keep track of the number of lines we have displayed.

private var dialogIndex:uint = 0;

Add the following code after the changing-of-the-npc-facing lines.

// Change text to dialogtext.
dialogIndex = 0;
text.text = tempChar.dialog[dialogIndex];

There are two problems now, first we can still walk while talking, second, we can only see the first line.

Talking to Richard

We will declare a variable in the PlayState that we will use to freeze the world if the player is talking.

public var freeze:Boolean = false;

The second problem we found can be solved also by using this variable. The next part might look a bit overwhelming, so I suggest you take a good look at it to see what is going on.

if (!freeze)
{
	// If down arrow key is pressed.
	if (FlxG.keys.DOWN)
	{
		char.move("DOWN");
	}
	// Else if up arrow key is pressed.
	else if (FlxG.keys.UP)
	{
		char.move("UP");
	}
	// Else if left arrow key is pressed.
	else if (FlxG.keys.LEFT)
	{
		char.move("LEFT");
	}
	// Else if right arrow key is pressed.
	else if (FlxG.keys.RIGHT)
	{
		char.move("RIGHT");
	}
	// Else if space is pressed
	else if (FlxG.keys.justPressed("SPACE"))
	{
		// If player is not moving and there is an npc in front
		if (!char.checkNPC(char.move_dir))	
		{
			// Tempdir is inverse direction of player.
			var tempDir:String = "";
			switch(char.move_dir)
			{
				case "UP":
					tempDir = "DOWN";
					break;
				case "DOWN":
					tempDir = "UP";
					break;
			    case "LEFT":
					tempDir = "RIGHT";
					break;
				case "RIGHT":
					tempDir = "LEFT";
					break;
			}
			tempChar = char.getCheckNPC(char.move_dir);
			// Make npc look towards player
			tempChar.move_dir = tempDir;
			// Change text to dialogtext.
			dialogIndex = 0;
			text.text = tempChar.dialog[dialogIndex];
						
			// Freeze world
			freeze = true;
						
			// Show text & dialog
			text.visible = true;
			dialogBG.visible = true;
		}
	}
}
else
{
	if (FlxG.keys.justPressed("SPACE"))
	{
		// increase dialog index
		dialogIndex++;
		// If last line, unfreeze
		if (dialogIndex >= tempChar.dialog.length)
		{
			// Unfreeze
			freeze = false;
						
			// hide text & dialog
			text.visible = false;
			dialogBG.visible = false;
		}
		// Else display next line
		else
			text.text = tempChar.dialog[dialogIndex];
		}
	}
}

So what we did was add an if-statement to check if the world is frozen. If that is the case, then it is allowed to move and start a conversation. If the world is frozen, however, we advance the conversation or, if the final line is reached, the world will be unfrozen. For aesthetic purposes we also make the dialogue-box visible/invisible during frozen/unfrozen status.

In the constructor we still need to make the dialogue-box invisible so at the start you won’t see the ‘Hello World!’ text.

For the background.

dialogBG.visible = false;

And the text.

text.visible = false;

Run your program, it will be awesome.



Our program is become more and more a game that is playable, especially since we are now able to chat to other characters. In the next part we will learn how to group objects using FlxGroup, and manipulate said groups.

You can download the code here: Flixel Adventure Game Tutorial part 5 code.

Part 6: Creating layers with FlxGroup

Join the Conversation

4 Comments

  1. Followed your tutorial through Part 4 – great stuff! Started Part 5, and the player can walk through the NPC (right after adding the checkNPC to the move function). After several hours I cannot see the error. I am using the exact versions of FlexDevelop and Flixel that you did (tried the current versions and was able to get to the same spot with minor tweaks, but thought this problem might be related to the versions). Any thoughts?

    1. I have sent you an e-mail. If you can send me your code I will take a look at it, I don’t know what your problem is at the moment. 🙂

  2. Just going through the tutorial and I got an error on createGraphic(). I’m running latest version of Flash Develop and I done some investigating and makeGraphic() seems to work instead.

    Hope this helps,

    Ger

Leave a comment

Leave a Reply to Kees Cancel reply

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