Home Game Tech Tutorials

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.


  • C# 7.3, Unity 2020.1, Unity

In this tutorial, you’ll learn how to create a simple click-to-move game like Monument Valley, an exploration game where you navigate a series of seemingly impossible platforms. Like an M.C. Escher-inspired maze, optical illusions form each level. Decipher its physics-defying architecture and create something like it yourself. Level up with the following achievements along the way:

  • Penrose Triangle creation in Unity.
  • Scene Camera settings to work in an isometric view with Penrose Triangles.
  • How to piece together a level architecture using Penrose Triangles and other parts.

demo play of a game like Monument Valley

By following through this tutorial, you’ll learn how the famous Monument Valley game mechanics and camera views work. On top of that, you will have added a few really cool techniques to your Unity toolbelt :]

Note: This tutorial assumes that you’re already comfortable working with Unity. No additional scripting is required to complete the exercises. However, you’ll gain the most from the demo project if you’re proficient in C#. If you’d like to brush up on your skills, check out our Beginning Programming With C# video course.

Getting Started

Click the Download Materials button at the top or bottom to download the project files. Unzip and open MonumentValleyStart using Unity 2020.1 or later.

The RW folder contains everything you need to get started:

  • Animation
  • Fonts
  • Materials
  • Models
  • Prefabs
  • Scenes
  • Scripts
  • Settings
  • Shaders
  • Sprites

There are also additional subfolders for extra project organization, such as for Level and Interface items within Prefabs. Both the Project window and the Hierarchy have a search feature, in case anything’s hard to find.

This project uses the Universal Render Pipeline. In Package Manager, confirm you have the latest Universal RP package installed.

Pipeline settings showing the latest version of Universal RP

Select Edit ▸ Project Settings ▸ Graphics. Make sure that UniversalRP-HighQuality is the current Pipeline Asset.

Load TutorialLevel from Scenes. This includes a Main Camera, a Backdrop for the environment and several lights. PostProcessing contains a Volume with basic image effects.

Note: Several empty spacer GameObjects break up the Hierarchy:

Hierarchy with spacers indicated by asterisks

As you add objects to the Hierarchy, position them between these spacers to stay organized.

Rather than coding from scratch, you’ll construct your app from prefabs. Experienced users can customize and extend the provided scripts.

Setting up the Game View

In keeping with the original game’s mobile platform origins, you’ll set the Game view’s aspect ratio to 9:16.

To do this, ensure you are in the Game window view. Click the + icon in the Free Aspect drop down. Select Aspect Ratio for the Type and label it HDPortrait. Enter 9 and 16 for the Width and Height. Finally, click OK.

Add screen with HDPortrait as the Label, 9 for the Width and 16 for the Height

Select your newly-created HDPortrait for the Game view aspect ratio.

Now that you have that out of the way, it’s time to start constructing your impossible platforms.

Discovering Penrose Triangles

The impossible shape that you’ll build for your platform is based on a Penrose Triangle, a triangular object that relies on perspective and can’t exist in real space. You’ll use a similar idea to make a platform that your player will try to navigate — but that won’t be as easy as it seems.

The scene currently shows a simple set of cubes grouped under PenroseTriangle.

Three beams at right angles to each other along the x, y, and z axes, forming the start of a Penrose triangle

The Main Camera has the following Transform values:

  • Position: (X:22, Y:24, Z:-18)
  • Rotation: (X:35.264, Y:-45, Z:0)

You can adjust the translation as needed, but be sure to use the exact values for the rotations! Monument Valley’s perspective puzzles only work with specific camera angles.

Spin the PenroseTriangle group on the Y-axis. When you reach 0 or 360 degrees, the blocks almost appear to connect with each other in the form of a triangle.

Penrose triangle perspective camera

Next, you’re going to enhance the illusion so that the beams form a triangle.

Adjusting Camera and Lighting

An isometric camera makes the illusion look better.

Change the Projection of the Main Camera to Orthographic.

Penrose triangle orthogonal camera

Removing any unnecessary lighting cues improves the illusion. In the Light components of KeyLight and FillLight, set the Shadow Type to No Shadows.

You have one more step to take to complete your optical illusion.

Fixing Seams

The Vertical beam appears to connect with the HorizontalX blocks. However, a small amount of shading near the top reveals an imperfection. You’ll use some helper geometry to correct this.

Drag the LJoint prefab into the Hierarchy. This is simply two polygons representing a “half cube”.

LJoint prefab view

Position the LJoint at (X:8, Y:9, Z:1), next to the vertical blocks. Rotate it to (X:90, Y:-90, Z:0). With this view, you can no longer see the gap, giving you the illusion of a smooth but twisty triangle.

LJoint fix

Voila! You’ve created a Penrose triangle, a classic impossible object that forms the basis for Monument Valley’s optical illusions.

Penrose triangle complete

Challenge: Who created the Penrose triangle?
[spoiler title=”Solution”]
Swedish artist Oscar Reutersvärd first discovered the Penrose triangle in 1934. Father and son, Lionel and Roger Penrose, later popularized the design.

Your next goal is to enable movement along these beams so that a character seems to move in an impossible fashion.

Shifting Screen Z

Orthogonal cameras don’t show perspective. Moving any object along the camera’s local Z-axis does not affect the object’s screen space position.

Confirm this for yourself by translating the LJoint to (X:10.5, Y:11.5, Z:-1.5).

camera/screen Z-axis

Through the Game view, nothing appears to have happened. However, the mesh has clearly shifted positions in the Scene view.

You’ll use this phenomenon to make movement challenging for your player.

Adding Level Geometry

Your goal is to connect smaller platforms to imitate the Penrose triangle effect. Eventually, you’ll guide a character to reach a goal platform at the top.

Now that you’ve seen how the Penrose triangle works, you don’t need your model of it anymore. So select the LJoint and the PenroseTriangle, then right-click and select Delete to remove them from the Hierarchy.

Next, you’ll build a course for your character to follow.

Building Platforms

You’ll start with two more prefabs. Drag the Bridge and SpinnerControl into the Hierarchy. Bridge should have a Position of (X: 6, Y: 7, Z:0) and SpinnerControl should have a position of (X: 7.85, Y: 7, Z:0).

Bridge and Spinner prefabs

The SpinnerControl contains custom Drag Spinner and Highlighter components.

Spinner components

You want to use the Drag Spinner to change the position of the bridge, so set its Target To Spin to the Bridge.

Next, set the Highlighter’s Mesh Renderers Size property to 1.

Locate the SpinnerMesh under the Bridge. Assign this to the empty field in Element 0.

Finally, you need a few more platforms for the character to move along. So import the HorizontalPlatform, VerticalPlatform and StairsPlatform prefabs. Set their rotations and translations to zero, if necessary.

Starting platforms and a spinner

For your next step, you’ll give the character a way to move toward its goal.

Bridging the Gap

Open the Scene view. Note the large physical gap between the HorizontalPlatform and the Bridge. That’s what you’ll be working with in this section.

scene view showing the large physical gap between sections that needs to be bridged.

Enter Play mode in the Editor, then hover your mouse over the SpinnerControl. The Highlighter component causes the SpinnerMesh to glow red. Clicking and dragging snaps the Bridge to 90-degree intervals.

Platforms with the spinner highlighted in red

Watch when the Bridge‘s X rotation reaches 90 degrees. Notice how Box10 appears to connect with Box15 in the Game view. In the Scene view, however, the gap remains.

Gap between platforms visible in the Scene view on the left, but obscured in the Game view on the right

Note: To better preview this effect, switch the Scene view from Perspective to Isometric.

You want the two platforms to appear seamless. But right now, when you look through the Main Camera you can see a shading error where the two platforms join.

section where platform and spinner appear to not connect

A small piece of geometry can conceal this region to repair the glitch, similar to the way you fixed the Penrose triangle in an earlier section.

Drag a new LJoint prefab into the Hierarchy. Set it’s Position to (X:7, Y:8, Z:-5) with a Rotation of (X:90, Y:-90, Z:0). To stay organized, rename it to LJoint10 to match Box10.

This conceals the break between Box10 and Box15.

Adjust the SpinnerControl in Play mode. The connection now appears seamless.

Platforms with no visible gap

Now, it’s time to introduce the character.

Using Layers With the Camera Stack

Adding a playable character on top of the level adds an unfortunate complication — the character doesn’t display properly because the draw order is incorrect.

Check this out by dragging the Player from Prefabs/Player into the Hierarchy. Position it at (X:-1, Y:0.5, Z:3).

Now, you can see that LJoint10 incorrectly obscures the Player‘s MeshRenderer.

Player character partially obscured by the platform its standing on

The holdout geometry needs to appear in front of the level. However, it also needs to render behind the Player. Tricky!

To solve this problem, you’ll use multiple cameras to adjust the draw order of meshes onscreen. Exit Play mode to get started.

Using Layers

The project already has several custom layers, which you’ll find in Layers ▸ Edit Layers…Tags and Layers. Select Layers ▸ Edit Layers… to confirm.

Next, check that User Layers 8 to 10 are labeled Background, Level and Player.

User Layers including the correctly-labeled 8, 9 and 10

Unity can use a camera stack to render multiple camera passes. This allows you to control the order of everything drawn onscreen.

To use this feature, start by cloning the MainCamera twice using Edit ▸ Duplicate.

Rename the clones to LevelCamera and PlayerCamera. Remove their AudioListener components.

Multiple Cameras in the Cameras section of the Hierarchy

Set the LevelCamera and the PlayerCamera to use RenderType: Overlay. The MainCamera should continue to use RenderType: Base.

Select the MainCamera and modify its CullingMask in the Rendering options. First, choose Everything for Culling Mask. Then, uncheck Level and Player. This camera will not draw those layers.

Main Camera Culling Mask with Player and Level deselected

In LevelCamera’s CullingMask, select Nothing and then select the Level layer. This camera will only render the Level layer.

Finally, in the PlayerCamera, select Nothing in the CullingMask and select the Player layer. This camera will only draw the game’s foreground elements.

Assign the Player to use Layer 10: Player. Select Yes, Change Children when prompted.

Player Layer

The PlayerCamera view will now look like this:

view of the player from PlayerCamera

The LevelCamera view will now look like this:

level camera game view

Bridge, HorizontalPlatform, VerticalPlatform, StairsPlatform, and LJoint10 should use Layer 9: Level. Assign those and, again, choose Yes, Change Children for each.

Level Layer

Confirm that you’ve already assigned the Backdrop to Layer 8: Background.

With those actions complete, it’s time to move on to creating the camera stack itself.

Creating the Camera Stack

Camera stacking is a common technique to composite rendered elements at runtime. You’ll use it here to add challenge to your game.

Select the LevelCamera or PlayerCamera. A thumbnail preview of their respective layers appears in the Scene view. However, only the Backdrop remains visible in the Game view. This is because you have not yet composited the cameras to form a stack yet.

Next, you’ll create a camera stack so all the cameras render together. Select MainCamera and, at the bottom of the Inspector, locate the Stack.

Click the + button to add the LevelCamera and PlayerCamera, in that order. Cameras at the bottom of the stack draw last.

Camera stack

Everything now appears in the Game view in the correct rendering order.

Enter Play mode to confirm the SpinnerControl still works. The Bridge forms a partial Penrose triangle with the other platforms.

Draw Order Fixed

Now, you need to connect the various platforms so the Player can cross the level.

Adding a Graph, Nodes and Edges

The level consists of several blocks and ramps to reach the final Goal at the top of StairsPlatform.

Unity’s built-in navigation system does not adapt easily to this game’s tile-based design. Instead, the project includes a custom pathfinding solution. This allows your character to move in discrete steps along the polygon faces.

Before you start building, take a moment to familiarize yourself with some terminology from graph theory:

  • A waypoint, or node, forms one unit of the path. It describes a three-dimensional location.
  • A node connects to its neighboring nodes with structures called edges.
  • The network of nodes forms a graph that describes the possible pathways through the maze.

Later in this tutorial, you’ll build a click-to-move controller to navigate this graph.

Exploring Nodes and Edges

If you’re already familiar with these concepts, open Node.cs and Edge.cs and familiarize yourself with those classes. Otherwise, here’s some background.

Nodes are simply three-dimensional points tracked by their transform.position. This implementation includes some options for visualization, like Gizmo size and color.

Node component

The helper Edge class allows a node to connect to its neighboring nodes. You can toggle each edge on and off, which will be useful later.

You won’t use some of the fields and methods, like PreviousNode, GameEvent and EnableEdge, until you dive deeper into the project. Set those aside for now, but don’t forget about them!

In the Hierarchy, each box that comprises HorizontalPlatform has a child transform with a Node component.

Hierarchy showing the Node6 component under Box6

Under the Box6 transform, for example, select the Node6 child GameObject. Note that the numerical suffixes match the Node with the corresponding Box tile.

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.

Graph component in the Inspector

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.

Node6 in the Inspector, showing Node5 and Node7 as Edges

Select other Nodes under HorizontalPlatform. Each Node is represented by a Gizmo sphere with lines drawn to its neighbors.

Node6 Gizmo lines

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.

Ramp Stairs Edges

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.

Ramp Stairs Gizmos

To save time, the other vertical or diagonal Edges for the level platforms are already set up. Check the Gizmos to verify that.

Note: Though it won’t be used in this tutorial scene, ExcludedNodes can help you cull any unwanted automatic Edges.

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 the PreviousNode fields to start a new graph search.

Locate NodeGoal under the StairsPlatform and drag it into the Graph’s goalNode field.

Goal Node

Your Graph now has an end goal assigned for this level.

Node Goal Settings

Starting Pathfinding

You can create a path through the level with a graph search.

Add the Pathfinder component to the Graph GameObject.

Pathfinder component

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.

Node5 to Node10 path example

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.

Node5 to Node10 Gizmos

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.

Box Collider with a Clickable Script

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 called clickAction, 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.

PlayerController and PlayerAnimation scripts

When you’re in Play mode and you click anywhere on the HorizontalPlatform, your character now moves to the corresponding Node. Nice job!

Player moving in response to a mouse click

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 the clickAction defined in Clickable. When the user clicks a valid Collider, the PlayerController invokes OnClick
  • OnClick, in turn, calls Pathfinder’s FindBestPath method for all child nodes under Clickable.
  • If it finds a valid path, MoveToNodeRoutine lerps the character’s position to the destinationNode based on MoveTime. 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.

Player Controller script settings

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.

Player Animation

You can view the simple PlayerAnimController in RW/Animation by selecting Window ▸ Animation ▸ Animator.

Animation Controller

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.

Player showing a drawing glitch when it moves near the VerticalPlatform

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.

Holdout fixed so the VerticalPlatform is now opaque

Now that your character displays correctly, you’ll work on making it easier to navigate the game.

Visualizing the Clicks

Right now, users have no indication of exactly where they’re clicking when they move their character. For your next step, you’ll add visual feedback to highlight the mouse clicks.

Drag the Cursor prefab into the Hierarchy. This is a simple quad mesh with a texture. The Cursor script will automatically rotate the GameObject in 3-D space and aim it at the camera.

The Offset Distance pulls the polygon toward the camera to prevent intersecting the level geometry. The default of 5 units should work.

Select the Player. In the PlayerController, drag the Cursor object into the empty Cursor field.

At runtime, an animated icon appears over your mouse clicks. Cool!

Circle animation appearing around the mouse cursor when the player clicks

Building Impossible Paths

The “magic” in Monument Valley happens because the character can walk over impossible surfaces. But currently, your player can only move along the nodes of the HorizontalPlatform. Your next step is to build some impossible paths for the character to follow.

Impossible edges showing connections between nodes 10 and 15 and nodes 21 and 22

Connect the Bridge with the HorizontalPlatform by creating an Edge between Node10 and Node15.

Select Node10. Set the Size of the Edges to 1. Create an Edge with Node15 as the Neighbor. Select isActive.

Node10 Edges

Now, select Node15, which is already connected to Node16 with an edge. Add an additional Edge to Node10.

Node15 Edges

Likewise, connect Node21 from the Bridge to Node22 under the StairsPlatform. Repeat the process with Node22.

Make sure to fill out the Neighbor and isActive fields correctly. Otherwise, the Pathfinder will not connect the Nodes.

Note: In this implementation, edges are one-way. You must set them up on each node to allow back-and-forth movement. Automatically-generated edges, on the other hand, are bidirectional by default.

Enter Play mode to test your handiwork. Use the SpinnerControl to rotate the Bridge to 90 degrees in X.

Click on the Bridge or StairsPlatform and your player will now walk to your selected collider.

Keep the Scene view open as you click in the Game view and see what happens when you walk back and forth across the HorizontalPlatform and Bridge.

impossible paths

Notice how the character appears to transition seamlessly in the Game view. In the Scene view, however, it shoots across the gap from Box10 to Box15.

This is the benefit of using orthogonal cameras. Any movement along the local Z-axis of the camera is invisible in the Game view!

Linking the Bridge Rotation

The Pathfinder currently works regardless of the bridge’s rotation. To make the puzzle a bit trickier, you’ll change this so the player needs to move the spinner to the proper position to cross the bridge.

In Play mode, keep the bridge in its default orientation and click somewhere on the StairsPlatform. The player character “wall walks” and makes its way past the Bridge. Yikes!

Character wall-walking past the bridge

You need to be able to disable certain edges under specific conditions. In this case, if the bridge is at the wrong angle, you need to disconnect edges between the platforms. You’ll use another component to do this.

Select the SpinnerControl and add a Linker component. Set the size of the Rotation Links to 2.

In the first element, set the Linked Transform to Bridge and the Active Euler Angle to (X:90, Y:0, Z:0).

Enter Node10 and Node15 for Node A and Node B (or vice versa).

In the second element, use the same values for the Linked Transform and Active Euler Angle. Set Node A and Node B to Node21 and Node22.

Linker set up with the values listed above

Linker does not update automatically, so add a SnapEvent in the DragSpinner by using the + icon.

Fill in the empty field with the SpinnerControl and select Linker ▸ UpdateRotationLinks in the Action drop-down.


In Play mode, select one of those four nodes with the Game and Scene views active. Rotate the SpinnerControl.

The gizmo line switches to its active color when the X rotation is 90 degrees. Likewise, it deactivates when the X rotation is anything else.

Try to cross from the HorizontalPlatform to the Bridge when the links are inactive. The cursor appears, but the player doesn’t move. So you’ve successfully blocked the player from moving across the bridge when it’s in the wrong position.

Adding Node Events

Right now, the SpinnerControl is still active when the player crosses the bridge. That means that if you spin the bridge while the player is walking over it, you get a weird movement like this:

Character crossing the bridge even when it moves out from underneath it

To fix this, you’ll use a node’s GameEvent to set up custom behavior. You want to disable the SpinnerControl when the player reaches Node15 or Node22. This prevents the bridge from rotating while the player is walking across it.

Select Node15. Click the + icon twice in the GameEvent to create two new actions.

Drag the SpinnerControl into the empty field for both actions.

In the first action, select DragSpinner ▸ EnableSpinner. Make sure you’ve unchecked the checkbox for false.

In the second action, select Highlighter ▸ EnableHighlight. Again, be sure you’ve unchecked the checkbox for false.

Node GameEvent

Repeat these steps for Node21. When the player walks across the bridge, the SpinnerControl will now be inactive, avoiding any weird animation.

But don’t forget, you want to be able to move the bridge again once the character has finished crossing it. So mirror these steps for Node10 and Node22 to re-enable the SpinnerControl once the character has left the bridge. This time, select true for each checkbox.

Now, when the character walks to the bridge, the SpinnerControl disables itself. Walking off the bridge re-enables the Highlighter and Drag Spinner components.

Completing the Game Manager

With the platforms all connected, the player can now walk to the Goal at the top of the stairs. However, you’re not quite done yet. You still need a manager script to check for the win condition. When you add this, you’ll also provide a basic user interface to start and end the level.

Drag the GameManager prefab into the Hierarchy. Nested under the GameManager is a ScreenFader GameObject. This contains a component of the same name that fades in to provide a better starting transition when the game starts.

Game Manager setup

Update checks each frame if the player has reached the GoalNode. Test this by walking to the top of the StairsPlatform. GameManager invokes Win to end the level.

Level Complete and a reset button appear upon reaching the goal

Use the Restart button to play again.

Hooray! You’ve now created your own Monument Valley-style game.

Where to Go From Here?

Congratulations on finishing this tutorial! Click the Download Materials button at the top or bottom of the tutorial to explore the completed project.

Check out the final version of this project from the downloaded materials. It contains an AlternateLevel in Scenes that shows a slightly more advanced treatment of the bridge platform. Notice that its Clickable tiles have more than one node attached.

The player must now use the sides and bottom of the Bridge as walkable surfaces. This time, you’ll need a few more clicks to reach the goal.

Alternate Level

Of course, this prototype just scratches the surface of the game’s unique mechanics.

To see how ustwo games made the original Monument Valley, watch Peter Pashley’s excellent “Making Of” video. You can also take a deep dive into its art design.

Use everything included in the sample project to build a more challenging scene. If you’re comfortable with C#, feel free to extend the scripted components. With just a little work, you can make your own perspective-bending puzzler!

There are a load more tutorials on this site that cover how to make certain game types or mechanics. Start your search here and check them out!

Thanks so much for reading this tutorial. If you have any questions or comments, join the discussion below.




More like this