Implementing a Click-To-Move System in Unity 2021
If you’ve ever wanted to create a “point and click adventure”, this article is for you!
Initial Scene Setup
Before we dive into the code, we’ll need to add a few things to our scene.
I’ve added a cube named “Floor” and resized it to suit my needs.
I’ve also created a cube named “Player” and attached a blue material to it for visibility purposes.
You’ll want to position the player cube so that it sits above the floor.
Now create an empty game object and name it “PlayerInputManager”.
Next, we’ll need to create 2 scripts.
First, you’ll need a player script.
As my project has several scenes with different variations of a player script, I’ve had to name it “ClickToMove_Player”.
Second, you’ll need a “MoveTo” script.
Assign the Player script to the Player game object.
Assign the MoveTo script to the PlayerInputManager game object.
The Player Script
Our Player script is where we will implement the actual movement of the player game object.
This script doesn’t care how a target location is determined, it just takes in the Vector3 value for a target position and then moves the game object to it.
This script will need a few class variables as highlighted above.
I’ll go through the methods in the order in which they are likely to occur.
That means we’ll start with this public method which our MoveTo script will call once it has a target location to hand off to this script.
In this method, we simply want to take in the target position Vector3, modify it so that the player won’t sink halfway through the floor, and set our _movePlayerActive flag to true.
Now that our _movePlayerActive Boolean is set to true, our Update() method can spring into action.
As a reminder, the Update() method runs every frame and is thus very useful for movement.
First, we’ll check that the distance from our Player game object to the target location.
If we try to do a direct does Player game object = target position, we are likely to always miss due to slight variations in the values of the Vector3.
By setting a reasonable distance of 0.05f units, we can make sure we stop at our destination.
While the distance is greater than 0.05f, we call our MovePlayer() method.
Once we are within 0.05f, we reset our _movePlayerActive flag.
Every frame that Update() runs when we meet our flag and distance requirements, the MovePlayer() method will move the Player game object towards the target position.
But first we must set a “step” value, which is essentially a distance-per-frame to move value.
By taking a _moveSpeed variable we can adjust as needed and multiplying by Time.deltaTime, we can fine-tune our movement speed.
Vector3.MoveTowards is purpose-built to make moving our game object from its current position to the desired position at the step variable speed very convenient.
The MoveTo Script
Now that we written the logic for actually moving the player game object, we need to write the logic that determines the destination.
Once again we will turn to the Physics.raycast system to accomplish this.
But first, we need to declare a couple class variables.
A reference to the Main Camera game object and to our Player game object’s player script component make our lives much easier.
Rather than coding a search function to “find” them, we can simply drag and drop in the Unity inspector.
In a larger project we would move the logic in our Update() method out to their own methods and call them from Update(), but for this example prototype it would be unnecessary.
Using the new Input System we check if the left mouse button was pressed this frame and then grab the Mouse.current.position.ReadValue from our Main Camera and assign it as the rayOrigin.
RaycastHit, as always, is incredibly valuable and useful so we create the hitInfo variable to store this information.
We then check if the Raycast is successful and the Ray derived from the mouse click intersected with a game object named “floor”.
If so we can use hitInfo.point to get a Vector3 location of where the player clicked and pass this on to the Player script we covered earlier.
And that is all it takes to get our “point and click adventure” prototype working!