In this tutorial, I’ll show you how to make a simple text adventure game, where you have to make choices to progress in the story.
This is what we will achieve at the end of this tutorial:
To start, all you need is one object and a room. Place the object inside the room, and let’s get to the coding!
Create Event
All of the story for the adventure will be put inside the Create event.
First of all I’ll initialize some variables:
//vars
cur_story = 0;
char = 0;
text_speed = 1;
choices = 2;
cur_story will store the id of the story the player is on, char will be used for the typewriter effect on the text, text_speed is the speed of the typewriter effect, and choices is the number of choices there will be in the game.
Next I will make the story of the game. There will be different chapters, and they will be based on the room. When one chapter ends, the game will move to the next room, and the next chapter will start. So in the Create event, I will use conditions to check which room I’m in, and then write the story to an array.
So for the first room, “room0”, I will add this condition:
if (room==room0){ }
Inside that condition, I will make all my story. Here is how I am going to do it:
There will be an array called “story” which will contain all the different possibilities in the adventure.
There will be a 2D array called “choice” which will contain the choices for each story in the “story” array. For example, for the story inside story[0], the choices will be choice[0, 0] and choice[0, 1].
Then there will be another 2D array called “path“, which will contain the story id where that choice will end up. So if the choice[0, 0] will go to story[1], then path[0, 0] will contain 1.
This way, I can make the story:
if (room==room0){ story[0] = "You are in a forest, and see a lake. What do you do?"; choice[0, 0] = "Dive in."; choice[0, 1] = "Run away."; path[0, 0] = 1; path[0, 1] = 10; story[1] = "You dive in the cave, and you see an underwater castle. What do you do?"; choice[1, 0] = "Go into the castle."; choice[1, 1] = "Go behind the castle."; path[1, 0] = 2; path[1, 1] = 11; story[2] = "You go into the castle, the guards catch you and throw you out of the water."; choice[2, 0] = "Restart Chapter."; choice[2, 1] = "Next Chapter."; path[2, 0] = -1; path[2, 1] = -2; story[10] = "You run away."; choice[10, 0] = "Restart Chapter."; choice[10, 1] = "Next Chapter."; path[10, 0] = -1; path[10, 1] = -2; story[11] = "You go behind the castle. There is a shark there, and it chases you out of the water."; choice[11, 0] = "Restart Chapter."; choice[11, 1] = "Next Chapter."; path[11, 0] = -1; path[11, 1] = -2; }
Let’s take story[0]. It has two choices, choice[0, 0] and choice[0, 1]. The stories those choices lead to are 1 and 10 respectively, which are stored inside the “path” array.
You can see that the last three stories (2, 10 and 11) are the endings, because the choices for them are “Restart Chapter” and “Next Chapter”. They both lead to -1 and -2, respectively. This way we can make the chapter restart if the chosen path is -1 and move to the next chapter if it is -2. So it is only for our reference, it doesn’t really lead to any place inside the array.
This way you can add story for each chapter by checking the room that is running.
Step event
Inside the Step event will be the system that will check for player input and select the relevant choice. The game will take input from the number keys on the keyboard.
//get input var input = -1; for(var i=0; i<choices; i++){ if (keyboard_check_pressed(ord(string(i+1)))){ input = i; break; } } //set story var str_len = string_length(story[cur_story]); if (input!=-1 && char>=str_len){ //move to next story cur_story = path[cur_story, input]; char = 0; //restart chapter if (cur_story==-1){ cur_story = 0; } //next chapter if (cur_story==-2){ if (room_exists(room_next(room))) room_goto_next(); else room_restart(); } }
//get input
The local variable input will store the input from the player, if there is any, or it will stay -1.
The for loop will loop through the number of choices and check if the relevant number key has been pressed. Note that when checking for a keypress, I am using “i+1”. The reason is that while the choices internally are 0 and 1, for the player, the choices are 1 and 2. So for selecting choice 0, the key that has to be pressed is 1.
So if a number key is pressed, input is set to i, and the loop is ended.
//set story
str_len is storing the number of characters there are in the current story text, which is story[cur_story] – because cur_story stores the number of the story the game is on.
Then the condition checks if there is some input by checking if it’s not equal to -1, and that char is greater than or equal to str_len. char will store the number of characters drawn on the screen, so this condition basically checks whether the complete text has been drawn.
If the conditions are true, cur_story is set to path[cur_story, input], so the story progresses based on the input. Then char is set to 0 so that the new text is drawn from the start.
Then comes the check for the ending. If the path returned is -1, the chapter will be restarted by moving to the first story, that is 0. If the path returned is -2, the game will move to the next room, if one exists. If it doesn’t, the current room will be restarted.
Draw event
Here, I will draw the interface.
//properties var margin = 32; var width = room_width-margin*2; var choice_pos = room_height*0.7; //draw story var text_draw = string_copy(story[cur_story], 1, char); draw_text_ext(margin, margin, text_draw, -1, width); //draw choices var str_len = string_length(story[cur_story]); if (char>=str_len){ var prev_height = 0; for(var i=0; i<choices; i++){ //text var text = "("+string(i+1)+") " + choice[cur_story, i]; draw_text_ext(margin, choice_pos + prev_height, text, -1, width); //choices height prev_height += string_height_ext(text, -1, width)+8; } } else{ char += text_speed; }
//properties
margin is the margin of the text drawn inside the room, width is the maximum width of the text drawn and choice_pos is the position where the choices will be drawn, vertically. I am setting it to 70% of the total height of the room.
//draw story
text_draw will copy text from the current story up until the char variable, which is the number of characters the game has to draw. Then the text is drawn.
//draw choices
Just like in the Step event, this condition is checking if all of the text has been drawn. If it is not, in the else part, char is incremented by the value of text_speed.
If the condition is true, a loop runs that draws the choices. The variable prev_height store the height of the text drawn.
The text drawn is “(i+1) choice”. Again, I am using “i+1” here because what is 0 internally is 1 to the player.
The text is drawn at a vertical position of “choice_pos + prev_height“, this way it will draw the text below the text that has been drawn already.
Then the height of the text just drawn is added to prev_height.
Done!
The text adventure engine is ready! You can run the game and play out your choices.
Now you can add more story to the game, and create more rooms to create new chapters.
Show Choice Selected
Currently, you do not see which choice has been selected. As soon as you select a choice, the game moves to the next point in the story.
You can improve on this by moving the input reaction code to an alarm.
Create a variable that stores the input for the alarm.
Create event: alarm_choice = -1;
In the Step event, replace the “set story” code with this:
//set story
var str_len = string_length(story[cur_story]);
if (input!=-1 && char>=str_len){
alarm_choice = input;
alarm[0] = 20;
}
So this will set alarm_choice to the input, and call the alarm 0.
Move the deleted code to the alarm and instead of input, check the alarm_choice variable. Also, make sure you reset alarm_choice to -1.
//move to next story cur_story = path[cur_story, alarm_choice]; char = 0; alarm_choice = -1; //restart chapter if (cur_story==-1){ cur_story = 0; } //next chapter if (cur_story==-2){ if (room_exists(room_next(room))) room_goto_next(); else room_restart(); }
Now, it will not immediately move to the next point in the story after you press a key. So you can use that time to show which choice the player has selected.
An easy way of doing it is in the Draw event, where you draw your choices. Inside the for loop, you can check if alarm_choice is equal to i – which means that choice has been selected. If that condition is true, you can make some visual change to show that.
For example, if the condition is true, I will draw that choice in a green color. Look at the //color code:
//draw choices if (char>=string_length(story[cur_story])){ var prev_height = 0; for(var i=0; i<choices; i++){ //color if (alarm_choice==i) draw_set_color(c_green); //text var text = "("+string(i+1)+") " + choice[cur_story, i]; draw_text_ext(margin, choice_pos + prev_height, text, -1, width); //choices height prev_height += string_height_ext(text, -1, width)+8; //reset color draw_set_color(c_white); } } else{ char += text_speed; }
Now you can see the selected choice:
Conclusion
If you ever need any help with GameMaker, this is the Discord server where you need to be!
See you later, and till then, happy dev’ing!
Hi,
it’s possible to choose the direction with buttons instead pressing the key?
Something like these, where the player can choose with mouse click the directions.
LikeLike
Hey Luca.
Please join the Discord server linked at the end of the tutorial. I will be glad to assist you there.
LikeLike
Hi man, I don’t like the chat.. what do you write is missing the next day…
By the way I have added this instructions to step events to catch some keyboard key
if keyboard_check_pressed(ord(“A”))
{input = 1;}
else if keyboard_check_pressed(ord(“B”))
{input = 2;}
else if keyboard_check_pressed(ord(“C”))
{input = 10;}
show_debug_message(input);
but when I compile GMS give me this error
string_copy argument 1 incorrect type (number) expecting a String (YYGS)
at gml_Object_obj_pc_txt_adventure_DrawEvent_1 (line 13) – var text_draw = string_copy(story[cur_story], 1, char); ############################################################################################
LikeLike
The variable “input” should be set to 0 or 1, if there are two choices. Why are you setting it to 1 and 2, and even 10?
Also, about Discord: we will chat in PMs, so the chat won’t “go” anywhere. It also will be easier to help you by chatting in real-time. So please hop up in the server where you can find me (matharoo) and PM me.
LikeLike
Hi again man.
I corrected the error due to a missing index in the array.
Now I can use letters to make the choice.
Next step is try to implements buttons instead letters 🙂
LikeLike
Okay. Still, if you need any help, feel free to PM me on Discord.
LikeLike
Thanks for the tutorial bro…
it’s works!!!
LikeLike
Hello, the Source code project for this thing doesn’t work on my Version of my GMS2. My version uses .yyp instead of .yyz format. A little help?
LikeLike
Use the Import option. That’s where yyz’s are used.
LikeLike
This is a great introduction! I’ll be sure to let others know about it. Thanks a ton!
LikeLike
which codes should i remove to remove the typewriting effect ? i just want them to come up all at once.
LikeLike
Set char to the length of the string.
LikeLike