How to Make a Game Like Monument Valley
Learn to create a simple click-to-move game where you navigate a series of seemingly impossible platforms like in Monument Valley. By Wilmer Lin.
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 a Game Like Monument Valley
35 mins
- Getting Started
- Setting up the Game View
- Discovering Penrose Triangles
- Adjusting Camera and Lighting
- Fixing Seams
- Shifting Screen Z
- Adding Level Geometry
- Building Platforms
- Bridging the Gap
- Using Layers With the Camera Stack
- Using Layers
- Creating the Camera Stack
- Adding a Graph, Nodes and Edges
- Exploring Nodes and Edges
- Adding the Graph
- Connecting Edges Manually
- Setting the Goal Node
- Starting Pathfinding
- Clicking Input
- Controlling the Player
- Animating the Player
- Holding out Geometry
- Visualizing the Clicks
- Building Impossible Paths
- Linking the Bridge Rotation
- Adding Node Events
- Completing the Game Manager
- Where to Go From Here?
Adding the Graph
Nodes need a graph to make them aware of each other. You’ll add one now.
Create an empty GameObject in the Hierarchy called Graph and add the Graph component.
Make sure you activate Gizmos for the Scene view.
You can set up Edges manually for each Node in the Inspector. To save time, horizontally-adjacent Nodes also detect each other automatically at runtime.
Select Node6. The Edges field currently shows a Size of 0.
Enter Play mode. Notice that it now has two active Edges that connect to Node5 and Node7.
Select other Nodes under HorizontalPlatform. Each Node is represented by a Gizmo sphere with lines drawn to its neighbors.
Connecting Edges Manually
You must set up vertical and diagonal nodes manually. To do this, exit Play mode and select NodeStairRamp1 in the StairsPlatform.
Set the Edges’ Size to 2. Add Node25 as the Neighbor of the first Edge and check isActive.
Add NodeStairRamp2 as the Neighbor to the second Edge and, again, check isActive.
Enter Play mode. Select several Nodes from the StairsPlatform, Bridge and HorizontalPlatform. Note how the Gizmos represent their Edges to neighboring Nodes.
To save time, the other vertical or diagonal Edges for the level platforms are already set up. Check the Gizmos to verify that.
Setting the Goal Node
The Graph
class manages the network of Nodes. Open Graph.cs to examine its functionality.
Graph
references a list of all the Nodes, as well as a public field for the NodeGoal. It also contains several useful methods:
-
FindNodeAt
: Returns a single Node located exactly at a specific 3-D point in space. -
FindClosestNode
: Locates the nearest Node in screen space to a given 3-D coordinate. -
ResetNodes
: Clears thePreviousNode
fields to start a new graph search.
Locate NodeGoal under the StairsPlatform and drag it into the Graph’s goalNode field.
Your Graph now has an end goal assigned for this level.
Starting Pathfinding
You can create a path through the level with a graph search.
Add the Pathfinder component to the Graph GameObject.
The included pathfinding algorithm uses a breadth-first search, one of the simplest graph searches.
Here, you designate one Node as your starting point and another as your destination.
The algorithm works iteratively, trying to connect the two. On each step, the search expands outward and increases by one set of neighbors.
Every explored node records a breadcrumb trail back to the starting point.
This outward search, or expansion, then repeats.
With each iteration, you explore more and more of the graph. The search ends when it either finds the destinationNode or is exhausted.
If you located the destinationNode, the Pathfinder creates a backward path to the starting Node.
Next, test the Pathfinder yourself. In the Inspector, set the Start Node to Node5 and the Destination Node to Node10.
Finally, enable Search On Start.
Enter Play mode and you’ll notice that the Scene view shows a line of Gizmo icons drawn like this. Success! You’ve drawn a temporary path between Node5 and Node10.
Uncheck Search On Start and clear the Start Node and Destination Node fields. In the next step, you’ll develop a better system for setting up the pathNodes.
Clicking Input
The primary game mechanic uses a click-to-move controller. Each 3-D cube in the HorizontalPlatform, Bridge and StairsPlatform has a Box Collider and a Clickable component attached.
The player can’t move across the surfaces of the VerticalPlatform, so those boxes don’t have Clickable components.
Clickable.cs contains some simple logic to process mouse clicks:
-
Clickable: Implements
IPointerDownHandler
from UnityEngine.EventSystems.OnPointerDown
invokes whenever the user mouse clicks over a Collider. - Nodes: Nodes that are parented to the current Transform are kept in a list. The Pathfinder will search these possible childNodes to form the best path.
-
clickAction
: The player will listen to a System.Action calledclickAction
, which is stored in each Clickable component. This allows you to invoke custom behavior when you reach a certain part of the level.
Now, you have everything ready to be able to start moving your player around the surfaces.
Controlling the Player
Your player’s current position is the starting point for pathfinding. Start by moving the player to the middle of the HorizontalPlatform at (X:2, Y:0.5, Z:0).
Add the PlayerController and PlayerAnimation components to the Player.
When you’re in Play mode and you click anywhere on the HorizontalPlatform, your character now moves to the corresponding Node. Nice job!
With gizmos turned on for the viewport, you can see the path drawn between the player’s starting position and the clicked destination.
PlayerController
has custom behavior to move your player:
-
PlayerController
listens for theclickAction
defined inClickable
. When the user clicks a valid Collider, thePlayerController
invokesOnClick
-
OnClick
, in turn, calls Pathfinder’sFindBestPath
method for all child nodes underClickable
. - If it finds a valid path,
MoveToNodeRoutine
lerps the character’s position to thedestinationNode
based onMoveTime
. Some nodes may be farther apart than they appear in the Game view. Movement based on time rather than distance keeps the motion more uniform. -
FaceNextPosition
updates the character’s rotation to align with the path as it walks.
Animating the Player
The PlayerAnimation
component has a simple Animator to transition between two AnimationClips
: Idle
and Walking
.
Drag Player’s child object, SamuraiAnimator, into the Animator field.
Enter Play mode. Click to move your character and you’ll see the Animator switches between animations so the character appears to be walking or standing still.
You can view the simple PlayerAnimController
in RW/Animation by selecting Window ▸ Animation ▸ Animator.
This has two animation states, with transitions triggered by the isMoving
parameter.
Do you think the player is moving too quickly or too slowly? Tweak the corresponding Walk Anim Speed slider in PlayerAnimation
to your liking. The Walking animation clip should match the player’s overall motion. Any value between 0.7 and 1.3 should work.
Holding out Geometry
Holding out geometry in this tutorial is essentially the process of masking it off so that it is hidden from the Camera’s view.
Try walking around the HorizontalPlatform. Move to Node7 near the VerticalPlatform and you’ll see your Player draws incorrectly.
To prevent this, you’ll add holdout geometry.
Exit Play mode. Drag an LJoint prefab into the Hierarchy. Rename it: LJoint12.
Adjust its Transform properties:
- Rotation: (X:0, Y:-90, Z:-90)
- Position: (X:6, Y:1.5, Z:0)
- Scale: (X:3, Y:1, Z:1)
With these settings, it should approximately cover Box12. Switch its Layer to Player.
Now, test Play mode again. When the Player approaches the right side of the platform, the foreground LJoint12 now holds out the Player properly.
Now that your character displays correctly, you’ll work on making it easier to navigate the game.