How to Make an Adventure Game Like King’s Quest
In this tutorial you will learn how to implement the core functionality of text-based games like King’s Quest I using Unity. By Najmm Shora.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Make an Adventure Game Like King’s Quest
35 mins
Text-based games have existed ever since teleprinters were interfaced with mainframe computers. With the availability of video terminals, universities started developing text-based adventure video games.
With the advent of graphics came graphic adventure games. While text adventure games only described the environment textually, graphic adventure games let you see and interact with environment.
When King’s Quest I was released in 1984, its key selling point was the use of non-static screens which responded dynamically to player input. For instance, the player could move the character in realtime.
In this tutorial you’ll learn how to implement the core functionality of King’s Quest I by using Unity to make a simple clone called Wolf’s Quest. You’ll learn how to:
- Parse text commands.
- Fake 3D in a 2D world.
- Implement text based interactions within the Unity Editor.
Time to get started!
Getting Started
Download the project materials by clicking the Download Materials button at the top or bottom of this tutorial. Open Unity, extract the zip file and open the starter project.
The project contains some folders to help you get started. Open Assets/RW and find the following directories:
- Animations contains pre-made animations and the animator for the main character.
- Resources has font files and a single audio file for ambience.
- Scenes contains the main scene with which you’ll work.
- Scripts has all the project scripts.
- Sprites contains all the pixel art for the project.
Switch to Game View inside Unity Editor and use the dropdown to set the aspect ratio to 4:3.
Now, navigate to RW/Scenes and open Main. Click Play and you’ll see a text overlay. Click the close button at the top right to hide it.
Notice the werewolf character animates on its own. The werewolf’s animations were included so you can focus on the core implementation. Currently, you can’t move the character, so that’s what you’ll work on next.
There’s a black area for typing commands at the bottom of the Game View. Type something there and press Return. Nothing interesting happens yet, but you’ll change that shortly. :]
Stop the game and look at the Hierarchy.
The Boundary GameObject has colliders to prevent the character from going outside the camera view.
The Main Camera is setup and has an attached Audio Source which plays a nice ambient sound on loop.
There’s a Canvas set up for the overlay UI. It has the UI Manager component attached and contains the following child GameObjects:
- InputField: This GameObject has an attached Input Field component. You’ll use it to enter the in-game commands.
- Dialogue: This is the text overlay UI you saw earlier. It has a Canvas component attached to it. It also has a Close Button child GameObject which disables the said Canvas component upon clicking.
There’s also the Character GameObject with the Animator component already setup. Notice it also has an Edge Collider and Rigidbody 2D components.
Why you ask? You’ll find that out soon. :]
Now, expand the Environment GameObject.
Notice it has several GameObjects. You’ll set up some of them later for interactions within the game. For now, notice how most of them have a Sprite Renderer while some of them have a Box Collider 2D component.
Finally, briefly look at the werewolf’s animator. Open the Animator window and select the Character in the hierarchy.
As you can see, there are two Int parameters, X and Y, to drive transitions in the state machine. The states are as follows:
- WalkRight: To transition to this state, the (X,Y) parameter values should be (1, 0).
- WalkLeft: To transition to this state, the (X,Y) parameter values should be (-1, 0).
- WalkUp: For this state, the (X,Y) parameter values should be (0, 1).
- WalkDown: For this state, the (X,Y) parameter values should be (0, -1).
Wow, that was quite the tour! Now it’s time to start coding. In the next section, you’ll implement character movement.
Moving the Character
In King’s Quest I, you press an arrow key once to move the character. To stop the character you press the same key. If you press any other arrow key during movement, the character changes direction.
You’ll replicate that character movement style in this section.
Navigate to RW/Scripts and open CharacterMovement.cs inside your code editor.
Replace //TODO: Require statement goes here
with [RequireComponent(typeof(Animator))]
.
It’s good practice to ensure an essential component, in this case an Animator, is present on the GameObject the script is attached to, instead of assuming it’s there.
Now paste the following code inside the class:
[SerializeField] private float speed = 2f;
private Animator animator;
private Vector2 currentDirection = Vector2.zero;
private void Awake()
{
animator = GetComponent<Animator>();
animator.speed = 0;
}
The code you added:
- Declares the variable
speed
which stores the speed at which the character moves.SerializeField
makes this private field accessible via the Inspector. - The variable
animator
is declared and later initialized insideAwake
to store the Animator component attached to the GameObject. The animator’s speed is set to zero inAwake
so the character’s animation is paused in the beginning. This script toggles theanimator.speed
between zero and one as you’ll see later. -
currentDirection
is aVector2
which stores the 2D direction in which the character moves. It’s initialized to its default value.
Now you need to code the actual movement. Copy and paste the following below Awake
:
private void StopMovement()
{
animator.speed = 0;
StopAllCoroutines();
}
private void ToggleMovement(Vector2 direction)
{
StopMovement();
if (currentDirection != direction)
{
animator.speed = 1;
animator.SetInteger("X", (int)direction.x);
animator.SetInteger("Y", (int)direction.y);
StartCoroutine(MovementRoutine(direction));
currentDirection = direction;
}
else
{
currentDirection = Vector2.zero;
}
}
private IEnumerator MovementRoutine(Vector2 direction)
{
while (true)
{
transform.Translate(direction * speed * Time.deltaTime);
yield return null;
}
}
MovementRoutine
translates the character by the value of speed
per second in the 2D direction specified by the Vector2
input parameter direction
. This coroutine keeps running until it’s stopped elsewhere.
StopMovement
sets the animator
speed to zero. It also stops any running coroutines, which essentially stops the MovementRoutine
.
ToggleMovement
accepts a Vector2
parameter direction
. Before doing anything, it calls StopMovement
. Then it checks to see if the direction is updated.
If currentDirection
is the same as direction
, the value of currentDirection
is reset to the default value of Vector2.zero
. Then the method returns, which effectively stops the character’s movement.
However, if the direction has changed, the animator.speed
is first set to one to enable animation, followed by setting X
and Y
animator integer parameters to the values of direction.x
and direction.y
respectively to set the animation state. Finally, the MovementRoutine
starts.
To make this code work, you need to use it inside the Update
loop. Paste the following after Awake
:
private void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow)) ToggleMovement(Vector2.up);
if (Input.GetKeyDown(KeyCode.LeftArrow)) ToggleMovement(Vector2.left);
if (Input.GetKeyDown(KeyCode.DownArrow)) ToggleMovement(Vector2.down);
if (Input.GetKeyDown(KeyCode.RightArrow)) ToggleMovement(Vector2.right);
}
This code polls the input for arrow keys and calls ToggleMovement
when the user presses an arrow key. Each arrow key is associated with a 2D direction. It then passes this direction to ToggleMovement
.
Save everything and return to the Main scene inside Unity. Press Play and move the character by pressing any arrow key. Press the same arrow key to stop the character or press another arrow key to change direction.
Notice the colliders in the scene prevent the character from going through, thanks to the Edge Collider attached to the character you saw before and the Box Collider 2D components on the other objects. You might also notice some weird sprite sorting issues. Don’t worry, you’ll fix these issues later on.
There are some issues you’ll want to solve right away: the character continues animating if it hits a collider and the MovementRoutine
is still running when this happens.
To correct this behavior, paste the following after all of the methods inside CharacterMovement.cs:
private void OnCollisionEnter2D(Collision2D other)
{
StopMovement();
}
This ensures the animation, as well as the MovementRoutine
, stops when the character hits a collider. OnCollisionEnter2D
works because the character has a Rigid Body 2D component attached. Every physics interaction the werewolf has will trigger this method to run. Save everything and play again to test it.
That leaves just the sorting issues to ‘sort’ out. :] You’ll fix them in the next section.