Make a Puzzle Game with Unity – Part 1

Introduction

One of the most incredible things about game design is the ability to do things that are impossible in reality. A chief example of this is the idea of the orthographic view.

Unity orthographic view demonstration

Computers have the ability to construct a perspective that does not even exist in real life. This has some fascinating consequences when it comes to creating video games. Exploring ways to utilize this unique perspective will generate a quite intriguing game.  Not only will it be unique, but it also adds another dimension of design possibilities. This look is also quite similar to certain popular video games on the mobile app stores.

In this tutorial, we will learn how to use orthographic view to create a puzzle game in Unity. From generating an orthographic projection, to creating a character, we will explore all aspects of this novel perspective for game development. Simultaneously, we will also be learning about Unity’s Navigation component system and how we can manipulate that to work in this multi-faceted view.

So, without further ado, let’s get started and create an illusion game!

Want to jump ahead?  Check out Part 2 instead!

BUILD GAMES

FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.

Assets and Project files

You can download the completed project files for this tutorial here: source code.

We will also be using the Navigation Component system which can be obtained for free from Unity’s Github (https://github.com/Unity-Technologies/NavMeshComponents). Download it to your computer and save it in a file near your Unity projects. We are going to be accessing this later.

Creating the Illusion

Select your camera and change the perspective to “Orthographic.”

Switching the Camera to Orthographic in Unity

The way this view works is it takes all depth information and flattens it. This way, the size of objects in the scene is their “true size,” meaning that far away objects do not look smaller and closer objects do not look larger. This, you can imagine, is essential when it comes to creating this illusion.

We can create this illusion using only cubes, so go ahead and add one in.

Adding in a cube to the scene

Our camera isn’t in the right position; we need to fix that. The position isn’t as important as the rotation. We want it to be rotated around the X and Y axis so that we can essentially see three faces of the cube at the same time. Here are the final position and rotation I landed at:

The transform of the Camera in Unity

It is super important to never change the transform of the camera! Once you have a value, lock it in, do not change it. Do not write scripts moving the camera. Do not make animations moving the camera. In order for the illusion to work, we need the camera to always stay in one single position.

Once the camera is set up, we need to create an illusion we can work with. Scale the cube so that it is fairly long on one axis

A scaled cube and its transform

Next, you’re going to want to duplicate the cube and rotate the duplicate 90 degrees on the Y-axis. Position it so that it looks like this:

The cube duplicated and rotated

Bring it up above the original cube a good distance for reasons we will see later.

Repositioning the top cube

Reposition everything so that the final result looks like this:

View size on Orthographic camera

You may have to either change the position of both cubes or change the “Size” on our orthographic camera. Also, disable shadows on the directional light. This is another important part of creating the illusion.

Framing the objects in the scene

Cleaning Up The Environment

I have a couple of complaints with our current setup. First, everything is so bright! 

A material-less scene requiring sunglasses to avoid blindness

Let’s add in some materials. Create a new folder called “Materials” and create a new material called “Blue.” Change the Albedo value to blue and drag this material onto both of our cubes.

Adding a material to our cubes

That looks much better! The second thing we need to change is our hierarchy.

A disorganized and inefficient hierarchy

It looks so un-organized. Plus, the scale of our cubes will present problems later on if we try and animate them. So first, let’s create an empty game object called “Environment” and place our cubes in that.

The Environment object parenting all the cubes

Next, let’s further parent our cubes into empty game objects called “PlatformTop” and “PlatformOccluded” so that they’ll each have a uniform scale of 1.

The Cubes parented to newly created Gameobjects

The scale of each cube's parent

There we are! A nicely setup scene that we can work with. As a very final step, create a capsule called “Character” and place it on our cubes.

A capsule which will serve as our character

Since this is going to be our character, you might consider changing the size of the environment (change the scale of the children, not the parents which must stay at 1). The character should easily fit under both of these cubes

Properly placing objects to make space for the character

Setting Up Navigation

Agents and Nav-Meshes

In your project files, create a new folder called “Navigation Components.” Navigate to where you stored your downloaded Navigation Components from Github and drop the “NavMeshComponents” folder into your project tab (this is located in “NavMeshComponents-master -> Assets”). There are some examples in the entire folder that are worth checking out in a separate, dedicated Unity project. When the folder is done importing, you’ll notice the presence of a few more scripts in the “Navigation” category of the components.

Newly imported navigation scripts

If you’d like a more in-depth look at what exactly these components do, check out the tutorial on the Game Dev Academy about Unity’s native navigation tools (https://gamedevacademy.org/learn-unitys-navmesh-by-creating-a-click-to-move-game/). For now, assign a “Nav Mesh Agent” to our character capsule.

The navigation Agent component

A “Nav Mesh Agent” is a game object which traverses a nav mesh. Basically, this is the game object that’s going to be doing the path-finding. Most of the settings in the agent component are fairly intuitive. I suggest, in order to better understand what each of these settings do, fiddle with these values when we’ve got the character on a working nav mesh. What I mainly want to highlight here is the cylinder shape surrounding the capsule.

The Collider on our character

In addition to the capsule collider (which should be set to “trigger” by the way), we’ve got an extra boundary being created. The size and radius of this cylinder is the size and radius that Unity will think the navigation agent is. This isn’t a value we can’t edit this value on the Nav Mesh Agent, rather, when we get to it, we will actually be editing this on the nav-mesh itself.

But what is a Nav Mesh exactly? A nav-mesh is an actual mesh generated by Unity which allows the agent to make paths on its geometry. Any place where there is a nav-mesh is a place where the nav-mesh agent can generate a path. Having said that, we’re going to want a nav-mesh on both our cubes. Select the parent of each cube and add a “Nav Mesh Surface” component to each.

A Nav-Mesh Surface component added to each cube

The settings for this component are worth looking at. First, you’ll notice that there is an “Agent Type” field. This is set to “Humanoid” by default. The idea behind this value is that you could set different types of nav-mesh agents (things like animals, enemies, etc.) if you wanted them to path-find differently. Since “Humanoid” is what our character is, we’re going to leave this at the default value.

“Collect Objects” determines which surfaces are going to have a nav-mesh baked onto them. In our case, we want this set to “children” since that is where are cubes are. “Includes Layers” allows you to determine what render layers you want to be involved in the nav-mesh calculation. And “Use Geometry” allows you to set whether you want the geometry or the physics colliders belonging to the game object to be considered viable nav-mesh surfaces. Finally, there is a “Bake” and “Clear” button at the end of the component. Hit “Bake” for each of the components we created. Unity will then generate the nav-meshes and your viewport will look like this:

Our scene with baked nav-meshes

That little blue sliver is our nav-mesh. Let’s test to see if it’s working! Create a new folder called “Scripts” and create a new script called “PlayerController.”

A newly created script in a newly created folder

In that script, we’re going to write out a simple bit of code that sends out a ray-cast, checks to see if it’s on a nav-mesh, and sets the clicked position to be the destination of the agent if the click was indeed on a nav-mesh.

using System.Collections;
using System.Collections.Generic;
using UnityEngine.AI;
using UnityEngine;

[RequireComponent(typeof(NavMeshAgent))]
public class PlayerController : MonoBehaviour
{
    private NavMeshAgent agent;
    private RaycastHit clickInfo = new RaycastHit();

    // Start is called before the first frame update
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray.origin, ray.direction, out clickInfo))
                agent.destination = clickInfo.point;
        }
    }
}

Drag and drop this script on to our capsule character. Hit play and watch the magic!

The character navigating the occluded cube

Our character now moves where we click! Note this, because we have the nav-mesh as a component, it exists on that game object meaning we can move the game object and our nav-mesh follows. This will be handy when it comes to animating the platforms.

Off Mesh Links

The entire purpose of this illusion was to make it so that surfaces that seem connected can be navigated by the player. As such, we want these two surfaces to be connected somehow.

An apparent surface connection

This is where Off-Mesh Links come in. Just like their name implies, they are a way to link two nav-mesh surfaces. Create a new empty game object called  “Link1” and place it in the Environment.

Link in the hierarchy

Add a “NavMeshLink” component (not an “Off-Mesh Link”) to this game object.

Off-mesh link component

As you can see, we get two transforms. These are, intuitively, the start and end positions of our link. We need to place each transform on the occluded cube and another on the top cube. You will see a circle form when the transform is over a viable nav-mesh surface.

A viable off-mesh link

Now here is where the uniqueness of the illusion comes in. We need to strategically place these off-mesh links in such a way so that they align in the game view. This causes a disconnect in world space but an apparent connection in orthographic space.

The illusion preserved in the game view

If you don’t see any of this in the game view, enable “Gizmos” in the top right corner. Our player now has the ability to navigate both cubes!

Navigation with links

Cleaning things up

Right now, the mechanic works. Our player can navigate two disconnected surfaces. However, it looks all wrong. There is too much of a translational delay. We need it to be as seamless as possible. Now, we realize that it won’t look 100% seamless simply because we’re using a navigation system that wasn’t really built for what we’re using it for. This link system was designed for things like ladders or teleportations, not necessarily illusion upholding linkage. Nevertheless, we can make an exponential improvement to this atrocious way of crossing platforms. As soon as the player is on a link, we don’t want it to interpolate between the positions (causing a delay). Instead, we can make it instantaneously teleport through the link. To do this, we’re actually going to write a bit of code with our own method of crossing links. In the “PlayerController” script, we need to add this if-statement to our update function,

void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray.origin, ray.direction, out clickInfo))
                agent.destination = clickInfo.point;
        }

        if (agent.isOnOffMeshLink) //New logic statement
        {
            agent.CompleteOffMeshLink();
        }


    }

“CompleteOffMeshLink” is a method we call on the navigation agent. It will instantaneously move the character through the off-mesh link whenever it is called. In our case, we’re calling this method whenever we’re on an off-mesh link.

To test it out, make sure that “Auto Traverse Off Mesh Link” is disabled.

Nav-Mesh Agent with "auto traverse" disabled

Hit play and let’s have a look!

An improved interpolation between platforms for the illusion

This actually is only a slight improvement. It’s much too sudden. It looks weird because the character just appears right in the middle of the platform. This is because our navigation mesh is right in the center of our platform.

A nav-mesh surface only covering part of the cube

We can fix this by changing the agent size of the nav-mesh agent. Go to the Navigation tab and change the radius of the character to be a very small value.

Agent radius in Unity

Go over our nav-mesh surfaces and rebake each of them. This will make the navigation surface cover the top of most of the cubes.

A rebake of the nav-mesh so that it covers everything

Notice, however, we have an error message.

A warning message on the nav-mesh surface component

Basically, it’s saying that the navigation surface may not be accurate for such a small agent radius. To fix this, open “Advanced Settings,” check “Override Voxel Size.” Set it to the recommended size of 0.022. We need to do this on each surface component and then bake it as well. The error will be cleared when each surface has finished baking.

We need this navigation surface to cover as much of the platform as possible. To increase the precision of our linkage system, we can scale the top cube ever so slightly to be underneath all of the navigation surface.

The navigation surface covering all of the top of the cube

And now, it’s just a matter of strategically placing the links so that one of them is on the edge of the top cube.

Repositioning the link on the edge

Now, hit play and test it!

Our final interpolation system

As you can see, this isn’t 100% seamless but it is an improvement to the previous look. To improve the “seamlessness” of the link, try repositioning the start and end points. It’s just a matter of fiddling with values. Even though it doesn’t completely uphold the illusion, it will certainly suffice for prototyping and level testing.

Conclusion

Whew!  We’ve covered a lot of ground here and learned quite a bit about using Unity’s Navigation component system and orthographic views to create a game.  Not only did we get our systems set up for our illusion game, but we also got our character moving!

However, while, we’ve got the ability to navigate disconnected platforms, it isn’t as seamless we can get.

In the next tutorial, we’re going to write a custom algorithm for interpolating between points. We’re also going to be animating platforms and giving the player the ability to trigger these animations from in-scene actuators. This might all sound a bit complicated, but worry not as we will walk you through each step to get it done!

Let’s continue this exploration of perspective as we…

Keep making great games!