Make an RPG-Style Text System for Your Next Game

Make an RPG-Style Text System for Your Next Game

Tutorial Details
  • Difficulty: Intermediate
  • Program: Flash
  • Estimated Completion Time: 60 mins

In this tutorial we’ll be creating a text system similar to what you see in a lot of role-playing games. Our class will dynamically display an icon for the characters as they speak, and will type each block of text letter by letter.


Final Result Preview

Here’s an example of the text system we’ll be creating:


Step 1: Set Up Your Flash File

Create a new Flash file (Actionscript 3). Your movie settings will vary depending on your game. For this demo I’m setting up my movie as 500×300, with a black background and 30 fps.

Flash file setup

Step 2: Add a Background Image

You’ll most likely be showing the text module over an image or animation from your game. For this demo I’m just using an image I threw together with a few of the characters from my games standing in a field of snow.

Put your background image on a layer called “background”.

Background image or animation

Step 3: Create the RPGText MovieClip

Create a new MovieClip on the stage (Insert > MovieClip) named “RPGText.”

In the Symbol Properties check “Export for Actionscript” and set the class name to “RPGText”. We’ll use this class name later to link code to this MovieClip.

RPGText Symbol Properties

Click OK. If you see a warning here that the class definition could not be found, that’s OK. It just means there isn’t any code to link to this symbol (yet).

Give your MovieClip the instance name “rpgText.” Remember, when I’m talking about “RPGText” (uppercase) I’m referring to the class (or MovieClip); “rpgText” (lowercase) is the name of an instance of that class.


Step 4: Add a Text Panel Background

Draw a rectangle inside your new RPGText MovieClip. This will be the background for the character icons and speech bubble. Design it however you like, but it should span the entire width of your movie and be short enough that it doesn’t cover too much of your game.

I made mine 500px wide (to match my movie) and 100px high. I filled it with a gradient from #666666 to #999999 (dark gray to lighter gray).

Background Rectangle

Quick Tip: to draw a rectangle of a specific size, select the rectangle tool and Alt-click on the stage. You’ll get a dialog box where you can enter the dimensions of your rectangle.


Step 5: The Character Icon MovieClip

Create a new layer inside the RPGText MovieClip called “icon.” Create a new MovieClip on this layer called “characterIcon” and give it the instance name “characterIcon.”

Inside the characterIcon MovieClip create two new layers: “icons” and “labels.” The icons layer will contain all of your character icons (each on its own keyframe) and the labels layer will contain frame labels that we’ll use to display the characters at the proper time.

Import (or draw in Flash) an icon for each of the characters in your game that will be speaking. For this demo I made a 75x75px JPG for each of my characters. Add the icons to the icons layer, making a new keyframe for each character. The order in which they appear is not important, but make sure each icon is placed at x:0, y:0 so they don’t appear to jump around when you switch characters.

Character Keyframes

Step 6: Add Frame Labels

Now create a new keyframe on each frame of your labels layer. A quick way to to do this is to select all the frames and hit F6.

Select each label keyframe one by one and add a frame label that corresponds to the name of the character that appears in that frame. If you add a few empty frames (F5) in between your keyframes, it will make it easier to read the frame labels, just make sure your labels keyframes stay lined up with your icons keyframes.

Adding Frame Labels

Make sure that each of your labels has a unique name. If you have two characters with the same name, you’ll need to differentiate them somehow (‘John_L’ and ‘John_K’ for example).


Step 7: Draw the Speech Bubble

Go back to the RPGText MovieClip and create a new layer called “textBackground.”

Draw a speech bubble. I drew a simple bubble with squared corners, but you can make yours look however you want. Make it big enough that it fills most of the background rectangle and sits nicely next to your character icons.

Select your speech bubble and convert it to a MovieClip (Modify > Convert to Symbol). Now that it’s a MovieClip we can add a drop shadow filter to it. I set mine to black, 50% strength, 5px blur and 1px distance.

Speech Bubble Graphic

Step 8: Add the Dynamic Text Field

Create a new layer in the RPGText MovieClip called “text.” Use the text tool to draw a text box. Make it fit just inside the edges of the speech bubble graphic.

Make it a multiline dynamic text field with the instance name “txt.” Remember to embed the font if you’re not using system text. I’m using 13pt Courier.

Dynamic Text Field Properties

Step 9: Add the Next Button

We need a way for the player to advance to the next text block when the player has finished reading. Let’s add a small “next” button in the corner.

Create a new layer in the RPGText MovieClip called “button.” Add a new button symbol called “b_next.” Design the four states of your button however you like. I used a small down arrow as the button symbol because I see that in a lot of games and assume players are familiar with it.

Put your button in the lower right corner on top of your speech bubble. Don’t worry about giving it an instance name. I’ll explain why later.

Complete RPGText MovieClip

Step 10: Create the Document Class

Create a new Actionscript file called “Main.as” and add this code to create the empty shell for the class:

package {
	import flash.display.MovieClip;
	public class Main extends MovieClip {

		// CONSTRUCTOR
		public function Main() {
		}
	}
}

Set Main as the Document Class in your Flash file. If you’d like a quick refresher on using a Document class, this Quick Tip from Michael Williams is one of the best explanations I’ve seen.


Step 11: Add the Text Blocks

If you’re using this in a game you’ll probably choose to put it elsewhere, but for now we’ll be adding it to the Document Class. Add this code to the constructor function in the Main class:

var textBlocks:Array = new Array(
		["Kid", 	"Look, a robot!"],
		["Abe", 	"BLEEP-BLOOP. I am an Autonomous Botanical Engineer. You can call me ABE."],
		["Kid", 	"Hi Abe. Meet Frosty the Snowman."],
		["Frosty", 	"Happy Birthday!"],
		["Abe", 	"BEEP-BIP. Hello, Frosty."],
		["Abe", 	"Does this frog belong to you?"],
		["Frog", 	"Ribbit..."],
		["Kid",		"No I've never seen him before. Aren't you cold frog?"],
		["Frog",	"Ribbit..."]
						   );

rpgText.textBlocks = textBlocks;

Here we’re creating a two-dimensional array (an array that contains other arrays) to hold the script for our scene. Before you change anything, take a look at how it’s structured. Each array is a separate text block that contains two elements. The first is the character’s name and the second is the text that he’ll speak. The text blocks are listed in the order that they will appear in the scene.

The last line just sends the textBlocks array to the rpgText MovieClip (remember “rpgText” is the instance name of the RPGText MovieClip on the stage). More on this later.

Go ahead and edit this section to fit your scene. Be extra careful that the character names correspond exactly to the names you used for the frame labels in the characterIcon MovieClip.


Step 12: Create the RPGText Class

We’re finally ready to start writing the code for the RPGText class.

Create a new Actionscript file named “RPGText.as” and add this code:

package {
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.display.MovieClip;
	import flash.media.Sound;
	public class RPGText extends MovieClip {
		private const SPEAKER:int = 0;
		private const TEXT:int = 1;
		private var _currentTextBlockIndex:int=0;
		private var _currentTextBlock:String;
		private var _textBlocks:Array;
		// CONSTRUCTOR
		public function RPGText() {
		}

	}
}

This is just a basic shell for the class. It doesn’t do anything yet, but let’s take a look at what’s there:

  • The first few lines are just importing some of the classes we’re going to need.
  • In the class declaration we’re extending the MovieClip class. We need to do this because this class is linked to the RPGText MovieClip in the Library (see Step 3).
  • Next, we have two constants, SPEAKER and TEXT that we’ll use to get the speaker name and text from the textBlocks array we set up in the previous step.
  • The variable _currentTextBlockIndex will keep track of which block of text we’re currently displaying.
  • _currentTextBlock will hold the actual text.
  • _textBlocks will hold the entire array of text blocks.
  • Lastly, there’s the empty class constructor.

(Note: I’m using the underscore in my variable names to indicate private variables.)


Step 13: The textBlocks Setter Function

Since our _textBlocks variable is private we’ll need a way to access that variable from the Main class where we’re setting up the text blocks. We’ll do that by creating a “setter” function. Add this to the RPGText class just below the constructor function:

public function set textBlocks(txt:Array):void {
	_textBlocks = txt;
}

The cool thing about setters in Flash is that we can access this function as though it were a public property of the RPGText class. Which is exactly what we did on line 21 of the Main class in Step 11:

rpgText.textBlocks = textBlocks;

Step 14: Add the updateText Function

Add this function to the RPGText class:

private function updateText(e:Event):void {
	if(txt.text.length < _currentTextBlock.length){
		txt.text = _currentTextBlock.substr(0, txt.text.length+1);
	} else {
		removeEventListener(Event.ENTER_FRAME, updateText);
		fillText();
	}
}

This is the core functionality of the class, where the letter-by-letter typing takes place. Let’s take a closer look at what’s happening here:

  • Line 27: This function accepts an Event as an parameter because we’ll be calling it using an ENTER_FRAME event.
  • Line 28: We compare the length (number of characters) currently in the txt text field to the number of characters in the _currentTextBlock String.
  • Line 29: If there are fewer characters in the text field then we use the substr method to get all the characters from the beginning of _currentTextBlock up to one more than the number of characters currently in the text field. We put all those characters into the text field, which has the effect of adding one more character to the end of the text in the text field.
  • Line 31: If there are the same number of characters in the text field as in the _currentTextBlock String, remove the ENTER_FRAME event that calls this function.
  • Line 32: Call the fillText function. We’ll write this function in the next step.

Step 15: Add the fillText Function

Add this function to the RPGText class:

private function fillText(e:MouseEvent = null):void {
	txt.text = _currentTextBlock;
	if(_currentTextBlockIndex < _textBlocks.length-1){
		addEventListener(MouseEvent.CLICK, nextTextBlock);
	}
}

The main purpose of this function is to fill the txt text field with the text from the _currentTextBlock (line 37). If we let the animation play through, the updateText function should take care of that, but it’s good to make sure nothing went wrong. We can also hook this function up to our “next” button to allow players to skip the text animation and immediately fill the text field with the entire text block.

Notice this function accepts a MouseEvent as an argument, but we set its default value to null. This allows us to use this function with a MouseEvent listener, since it will accept the event. Since we give the event a default value we can also call the function without sending an event as we do at the end of the updateText function.

After we fill the text field, we do a check to see if this is the last text block in the array (if the _currentBlockIndex is less than the number of elements in the _textBlock array). If not, we add a CLICK listener to trigger a function called nextTextBlock which we’ll write next.


Step 16: About that Click Listener

Remember when we created the “next” button and I said not to worry about giving it an instance name? Did you notice in the last step how we attached the CLICK listener to the entire RPGText MovieClip instead of the button? This makes it so the player can click anywhere on the MovieClip to advance the text. We really don’t even need the button, but I like to put one in so there is some indication that you click to advance the text.

Of course this is just a personal preference of mine. If you want you could give the button an instance name and attach the CLICK listener to the button instead. I just find the bigger hit area to be easier to use.


Step 17: Add the nextTextBlock Function

Back to business. Add this function to the RPGText class:

private function nextTextBlock(e:MouseEvent):void {
	removeEventListener(MouseEvent.CLICK, nextTextBlock);
	txt.text = ""; // clear the text
	_currentTextBlockIndex++;
	_currentTextBlock = _textBlocks[_currentTextBlockIndex][TEXT]; // set the text
	characterIcon.gotoAndStop(_textBlocks[_currentTextBlockIndex][SPEAKER]); // set the character icon
	addEventListener(Event.ENTER_FRAME, updateText); // start updating the text
	addEventListener(MouseEvent.CLICK, fillText);
}

The first three lines are pretty simple. Remove the MouseEvent listener, clear the text field, and increment the _currentTextBlockIndex var to point to the next text block.

Line 47 is using the TEXT constant to get the current text string from the _textBlocks array and assign it to _currentTextBlock.

Next we use the SPEAKER constant to get the name of the character. Since the character names match the frame labels in our characterIcon MovieClip we can use gotoAndStop to send the characterIcon MovieClip to the frame that displays that character icon.

Finally, we add an event listener to start typing on the new text string and then add a CLICK listener to run fillText when the MovieClip is clicked.


Step 18: Add the startText Function

We’re almost done, we just need to add a function that will get everything started. We’ll do that with a public function called “startText.” Since this is a public function let’s put it near the top of the RPGText class, just below the textBlocks setter:

public function startText():void {
	_currentTextBlock = _textBlocks[_currentTextBlockIndex][TEXT];
	characterIcon.gotoAndStop(_textBlocks[_currentTextBlockIndex][SPEAKER]);
	addEventListener(Event.ENTER_FRAME, updateText);
	addEventListener(MouseEvent.CLICK, fillText);
}

Look familiar? This code does almost the exact same thing as the nextTextBlock function. It sets the current text and character icon, and adds the event listeners for updateText and fillText. Since this function only runs when the text first starts we don’t need to worry about clearing the text field or incrementing the _currentTextBlockIndex like we did in nextTextBlock.


Step 19: Invoke the startText Function

Now we have a publicly accessible way to start the text. Let’s put it to use.

Add this line to the bottom of the Main class constructor function:

rpgText.startText();

This is just calling the startText function inside the RPGText class. That should get everything going.


Step 20: Add Sound

You should be able to test your movie now and see everything working. There’s just one thing missing though: sound.

Find (or create) a sound to play as the text is typing on. When choosing a sound for this keep it very short, since this sound will play over and over as the text types on. A small “boop” or button click works best for this effect.

Import the sound into the Library in your Flash file, check “Export for Actionscript” and give it the class name “TypingSound.”

Sound Export Settings

To get this sound to play we only need to add two lines to the RPGText class. First we need to instantiate the sound. Add this line at the top of the class beneath the three other private variables:

private var _typingSound:Sound = new TypingSound();

Now skip down to the updateText function and add a line that will actually play the sound each time the text updates (line 38 is new):

private function updateText(e:Event):void {
	if(txt.text.length < _currentTextBlock.length){
		txt.text = _currentTextBlock.substr(0, txt.text.length+1);
		_typingSound.play();
	} else {
		removeEventListener(Event.ENTER_FRAME, updateText);
		fillText();
	}
}

Step 21: Taking it Further

That’s it for the demo. Everything should work at this point, but if you want to integrate this into a game, you’ve still got some work ahead of you.

First, depending on how your game is set up you’ll probably want to pull the text blocks out of the document class. You might have a Scene class that you use to set up the individual conversations that occur in your game, or a Strings class that holds all the text for every conversation.

Second, you’ll want to think about how and when the text module will appear in your game. You might want to add a tween animation that makes it slide in and out from the bottom when a conversation starts and ends. You’ll also want to be listening for when the conversation is over, to either hide the text module or start the next conversation.

Since we’re already checking to see if the last text block has been reached in the fillText function, you could easily add something there that handles the end of the conversation.

I’m not including these topics in the tutorial because the way you go about these things will be very specific to your game. This should be enough to get you started though.

I hope you liked it! Post a comment and let me know what you think.

Add Comment

Discussion 32 Comments

  1. Deoxys says:

    Haha, lovely :)

  2. Lol, i hate those type games, i.e. Zelda. Nice tut though. Cool sound.

  3. geoff brown says:

    great tut! i’ve been looking for a way to do this for a long time. thanks for the useful contribution.

  4. jay says:

    are you two crazy….this tut is amazinggggg. zelda rocks!

  5. flanture says:

    hello, I think source file class RPGText.as is missing one import statement:

    import flash.text.TextField;

    it won’t work without it, other than that – good tutorial

    • Cadin says:
      Author

      It works fine for me as is (in CS4).
      Are you using CS3 by chance?

      I seem to remember that CS3 doesn’t auto-import classes for items on the stage like CS4 does.
      Thanks for the fix though. I’m sure there are other CS3-ers here who will appreciate it.

  6. Bruno Crociquia says:

    Loved it! :D

  7. Nice tut. Thanks for posting.

  8. Blabber says:

    Hi, I tried to use a button to move on to the next text block called next_btn (instance) and put it on the function filltext()
    next_btn.addEventListener(MouseEvent.CLICK, nextTextBlock);

    And I removed it in the function nextTextBlock
    next_btn.removeEventListener(MouseEvent.CLICK, nextTextBlock);

    It continues on to the next text block, but the “typing” thing is not happening. Any idea why?

    • Mabbu says:

      if you use the function fillText, the typing thing will not happen.
      Only updateText makes the typing thing happen ^^

  9. Mabbu says:

    I love the tutorial! It explains things very well :D

  10. Yuki Yamamoto says:

    I CAN’T DOWNLOAD IT!!! TT^TT
    I wanna create a flash game before my school holiday over and I left 7 days!
    (I wonder if that’s enough for me to create one)

  11. Mabbu says:

    I realised this textbox cannot be used in 2nd keyframe onwards without receiving a “Error #1009: Cannot access a property or method of a null object reference” :(

    Ensure that the Arrays are put into the class and not the main function :

    public class TextBox extends MovieClip {
    var textBlocks:Array = new Array(
    …….
    );

    Then add this instead :

    public function TextBox() {

    addEventListener(Event.ENTER_FRAME, onEnterFrame);

    }
    private function onEnterFrame(event:Event):void
    {
    if(rpgText != null){
    //to check if rpgText exists to prevent the error
    rpgText.textBlocks = textBlocks;
    rpgText.startText();

    }

  12. Frikin says:

    All very nice,thanks for that.
    Still can’t figure out how to move to the next scene once the conversation is over though.

  13. Elice says:

    yes dont suppose you could explain a simple way to move to the next scene? would be nice
    maybe even after picking a certain answer to a question?

    just moving to the next scene after its done in itself would work fine however

  14. Mayhew says:

    Any chance there’s a AS2 (Actionscript 2.0) version of this? I already got the dialogue text working perfectly. All I need now is adding the sound effect. Any suggestions? D:

  15. Joseph says:

    Hello is there any chance youll teah us how to move to the next scene? thanks so much

  16. Diftic says:

    could someone please tell me how to put all of this in one level? because i am having quite a few troubles putting all of it in one file.

    am i just having trouble with this because i am using levelmanagermain -> levelbase -> level1/2/so on, or is it something entirely different?

  17. Hornyhunk127 says:

    Waaaaaaaaaaay too much acionscript,im better off just putting a letter in 1 frame at a time till i make the sentance

    so this is pretty much a fail to me

  18. Rubixchick says:

    I really found this useful and for large projects, it’s definitely a better alternative than making these big timeline messes and tweens to get the same effect.

    I’m new to scripting on external .as files and trying to learn fast because I’m currently working on a game demo that’s become too much of a pain to stay timeline based. Variables, multiple if/else statements, the works. And so, I just wanted to ask, if I wanted to use this as it’s own class, so rather than main, it would be scenes.as. And then that can be called upon from the main perhaps when it’s something like, “If player clicks on this button (which would be a character) go to scenes.as to activate this scene.”? Is that how it works?

    Also using your RPG Text example, let’s say I didn’t want a completely linear scenes, like give the player a choice that will then change the text depending on that choice, are the if/esle’s put into this same file or is this meant to just hold the conversations and the rules of how they’re activated, written elsewhere?

  19. Bender says:

    I don’t get it! AS3 is too complicated. I need an AS2 version.

  20. Ash says:

    Mine works great, thanks!

    Only problem is my text doesn’t carry over to the next line once it reaches the end of the text box. It just keeps going and runs off the screen.
    Any ideas on what’s wrong? I’ve practically copied everything straight over so I don’t know what could be wrong :S

    Any help would be greatly appreciated!!

  21. Jordan S says:

    Nice tutorial! Ash, I had the same problem- make sure your textfield’s behavior is set to “Multiline”. :)

  22. Ash says:

    Ahhh thank you so much Jordan!! :D

  23. Reval says:

    Hi,
    I’m rather new to flash but wanted to get this working as I am interested in these sorts of games.

    I am having the problem

    1013: The private attribute may be used only on class property definitions.

    with the line

    private var _typingSound:Sound = new TypingSound();

    and cannot figure it out and if I remove it then I get that 1013: error on bout 4 other different lines.

    I can see the date on this is rather old now but any help would be very much appreciated
    thank you

  24. Marc says:

    I want to make it so you have to click the next button (not the whole box) to advance the text. How would I do this???

  25. KLQ says:

    Very good tutorial, works perfectly.
    Several questions:
    - I have two text areas instead of one. Would it be very complicated to implement this? For example, if person A or C is talking, use textbox on the left, if person B or D is talking, use textbox on the right. I’m not sure how to separate this as in your code you basically fill all the text into the instance “txt”. I’m also unsure how to add “identifiers” to the array. Is it: if (_textBlocks[_currentTextBlockIndex][SPEAKER] == “personA” || _textBlocks[_currentTextBlockIndex][SPEAKER] == “personC”)
    _textBlocks = txt;
    - My dialogue script is extremely long, it wouldn’t be very feasible to put all of it into an array. Is it possible to put it into an external text file?
    Thanks

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.