Create a Diablo Style Character Controller in Unity!

In just a few steps, learn how to make your very own Diablo Style Character controller in Unity. Practical explanation + code included.

Seemanta Debdas
Eincode
8 min readDec 2, 2021

--

Final Product

This tutorial will cover how to create a system to control our character using our Mouse. By the end, we'll have our character running around the world, seamlessly around objects with a complete blend tree animation that we will set up from scratch.
Controllers like this have been implemented in several infamous Hack-and-slash games like Diablo, Path of Exile, and recently released Indie Game Len's Island.

Let's Get Started!

Resources:

GitHub Repository: https://github.com/SeemantaDebdas/Blog-MouseMovement

Full Course: https://academy.eincode.com/courses/the-complete-unity-guide-3d-beginner-to-rpg-game-dev-in-c

Setting Up the Scene:

  • Creating The Project
    Open Unity Hub and Create a project using the 3D template. After the Project finishes loading, make a plane for our Player to stand on.
    To make a Plane,
    Right Click on the Hierarchy → 3D Object →Plane.
    Reset the transform if it's not at Position (0,0,0) globally.
    Scale up the Plane so that the values of the Scale property on the Transform Component reads (5,1,5)
  • Creating Our Player
    Right Click on the Hierarchy → 3D Object → Capsule.
    This is going to be the placeholder for our Player.
    Drag it up so that it sits on the Plane.
  • Creating Obstacles for Our Player
    For this project, we'll be using NavMesh(designated mesh in the Scene which specifies navigable areas in the environment) to control the environment our Player can walk on. To test out our NavMesh, let's place some placeholder obstacles for our Player to avoid. To do that, randomly add some cubes to our Scene for our Player to avoid.
    To add a Cube,
    Right Click on the Hierarchy → 3D Object → Cube.
    Right Click on its transform component and hit Reset to bring it to the Scene's center. Scale it up if you want and then select move tool or press 'W' to move the obstacle to any desired location on the Plane.
    Duplicate the cube by pressing 'Ctrl+D' and changing its transform to make other obstacles.
    Repeat this process until you're satisfied with the level. This is what my Scene looks like:

I have added materials to make it easier to differentiate between the GameObjects. To achieve a similar effect, follow the steps in the given link:
https://docs.unity3d.com/2019.3/Documentation/Manual/Materials.html

  • Baking the NavMesh
    The process of creating a NavMesh from the level geometry
    is called NavMesh Baking. The process collects the Render Meshes and Terrains of all Game Objects, marked as Navigation Static. It then processes them to create a navigation mesh that approximates the walkable surfaces of the level.
    For the obstacles in our Scene to register as obstacles for our NavMesh(also the ground plane), we need to set them as Navigation Static. Select a GameObject → In Inspector Panel, select the drop-down button beside "Static" → Select only "Navigation Static."
Select all obstacles and the ground as Navigation Static

To bake the NavMesh for your Scene, go to Windows → AI → Navigation.

Click on the Bake tab. This will open up the Navigation Panel. For now, we'll go with the default values. Click on Bake.

Baked Scene

Now that our Scene is all set up let's make our Player move!

Player Movement

To enable our Player to move along the NavMesh, our Player needs to have a NavMeshAgent component.

Add NavMeshAgent Component to our Player

Let's create a script, name it "Controller." We will use this script to manipulate the NavMeshAgent Component of our Player.

First, we need to cache the NavMeshAgent component in the Awake method since self-initialization and component references are best handled in Awake to avoid errors. Since the execution of the Start method happens after the Awake method, we can use it to cache reference other GameObjects or scripts.

Next, we use the following steps to move our Player:

  • Register where the user clicks on the screen and convert it into a Ray using a function called ScreenPointToRay()
  • Store the location where the Ray hits the NavMesh in a variable(RaycastHit hit).
  • Send that point to Player's NavMeshAgent.
  • NavMeshAgent(specifically a function called SetDestination())will make our Player move to that point.

More about
ScreenPointToRay():https://docs.unity3d.com/ScriptReference/Camera.ScreenPointToRay.html
Raycast:https://docs.unity3d.com/ScriptReference/Physics.Raycast.html
SetDestination():https://docs.unity3d.com/ScriptReference/AI.NavMeshAgent.SetDestination.html

Animation Our Player

Let's replace our Player placeholder with an actual character. I will be using an outstanding asset from the Unity Asset Store. You can use your custom model along with animations if you want.

Link to the free asset pack: https://assetstore.unity.com/packages/3d/characters/human-characters-free-sample-pack-181554

Go ahead and import your package into the Project.

Hit the "Import" button to import the file into the Project

Follow along if you're using the same pack as I am. If not, you'll get lots of tutorials online to set up your custom model.

Modifying existing player

From our Player, remove every component except Transform, Controller.cs, and NavMeshAgent. Under the Player Gameobject(as its child), drag in any desired character prefab you want from the project panel. I'll be using the following:

Male_01_V01 prefab is used in the Project

Once added, remove the Animator component from the character Prefab and add an Animator controller to the parent Player Gameobject.

Animator Component: Assigns animations to GameObjects through an Animator Controller.

Graphics(the character prefab) and Logic(the parent Player Gameobject) can be handled separately.

Player, after implementing the changes. Ignore the Controller and Avatar property of the Animator component for now.

Reduce the size and height of the NavMeshAgent to fit the character model(optional).

Importing animations from Mixamo

To import animations specific to the Model, we'll use a free source called Mixamo. Mixamo will take care of the rigging and animation of our Model. To get started, go to https://www.mixamo.com/#/ → sign up.

We will take advantage of the rich library of free animations Mixamo has to bring our character to life.

On the right, just beside the preview panel, click on UPLOAD CHARACTER and upload the Model (.fbx) on which you want to apply the animation.

Hit NEXT

Search "Walk" in the search area. From the list, select whichever animation you want. Ensure that the In Place check box is not set and hit Download.

Since we already have the Model in our Project, we'll download it without the skin from Mixamo. As for the format, we will be using FBX for Unity. Once all of the values have been set, hit Download.

Do the same for Idle and Run animations. We're going to be needing them later.

SETTING UP ANIMATIONS IN UNITY

In the Projects panel, make another folder called "Animations" and drag in the animations that we just downloaded.

For animations to affect our character, we need to reference our Avatar(Model) to the animations.

  • Select an animation from the Project → In the inspector, click on the "Rigs" tab → Select "Animation Type" as "Humanoid."
  • Select "Avatar Definition" as "Copy From Other Avatar"
  • In "Source," click on the circle button and browse for the Avatar you're using. In this case, "Male_01_V01Avatar"
  • In the Animation tab, select the "Loop Time" checkbox.

Do the same thing for all the imported animations.

To use the animations on our Player, we'll be needing an Animator Controller.

In the Project panel,
Right, Click→ Create → Animator Controller → Name it "PlayerAnim."

Animator Controller: Arrangement of animations and transitions (state machine that handles animation states)

Double Click on the Animator Controller to open it up on the "Animator" tab.

Creating Blend Tree

In the "Animator" tab,
Right Click → Create State → From New Blend Tree → Rename it as "Locomotion."

We'll use blend trees to seamlessly transition our animations based on parameter values that we'll be setting up later.

Blend Tree: Allows multiple animations to be blended together smoothly.

There's a Blend Tree inside. Double Click on it to open it up. Please select it and in the Inspector panel, add three motions fields.

These are going to hold our three animations. Idle, Walk and Run. Click on the circle button(selector) to browse the animations or drag them in.

On the top-left corner of the Animator Tab, click on "Parameters." Rename the existing parameter as "Velocity."

Now, drag the Animator Controller(Player Anim) into the Controller slot of the Animator component.
As for the Avatar slot in the Animator component, click on the Circle(Selector) button and find the Avatar for the Model (in this case, "Male_01_V01Avatar")

Syncing speed with animation

If we inspect each of the animations under Mirror Panel, we'll find that each animation has its Average Velocity. We'll be using the z-value to sync the speed with the animations.

To do this,
Select Blend Tree under Locomotion → In Inspector Panel→ Uncheck "Automate Thresholds" → Select "Velocity Z" under "Compute Thresholds"

This will update the Threshold Values.

Now, in the NavMeshAgent component of our Player, we can set the speed as the maximum Velocity Value(in this case 5.2).

That way, we can keep our animation and speed in sync with each other.

Final step: Script Update

  • Cache the Animator component in the Awake method to change the value of the Velocity parameter during runtime.
  • Use SetFloat() function to set the value of the Velocity parameter according to the velocity of our Player. This will be handled in the Update method.
Final Code

Test It Out!

Change the Camera height to point at the platform from somewhere above and then click anywhere on the platform.

Our Player will navigate to that exact location, dodging obstacles with synched animations.

If the character movement seems a bit off, fine-tune the values of the NavMeshAgent.

My Values

Conclusion

If this simple Diablo Style Character Controller piqued your curiosity, then you should consider opting for The Complete Unity Guide 3D- Beginner to RPG Game Dev in C# offered by Eincode. This course features among the most immersive and practical resources out there. Experienced software engineer Filip Jerga curates this course. This course starts with the fundamentals. Then, it progresses gradually to eventually take its subscribers through the journey of developing their own RPG game using Unity 2020 and C#.

Cheers!

Debdas

--

--

Seemanta Debdas
Eincode

Game Dev enthusiast contributing to the Gaming Industry!