Unless you're planning on launching some kind of retro, text-based game resurgence, you're going to need to add movement in your game design.
Fortunately, Unity offers dozens of features for creating a full-fledged game right out of the box, including a variety of solutions for moving game objects within a scene.
Huzzah!
In this tutorial, we'll explore how to use both the Transform and RigidBody components for moving game objects, as well as look at why you might want to use one solution over the other.
Let's dive in!
Before we get into the exact steps of what to do and why, I wanted to also give you the link to check out my repository that ties into this guide.
This way you'll be able to follow along with this tutorial, and practice using these solutions.
Got it? Alright, let's get to work. As you can see below, I've prepared a basic project that looks something like this:
We have a basic cylinder object that'll represent the player, while the green shapes are walls for blocking the player's movement. It's not Zelda level graphics but it's good enough for us to understand these concepts and how they work!
There are two primary goals for this tutorial:
Now, before we can begin implementing movement, the keyboard strokes need to be captured. The good news is that this is simple enough to do.
In the core directory, you will find an input action that binds the WASD keys.
If you're unfamiliar with the Input System package in Unity, don't worry, as it's fairly easy to pick up. If you want a helping hand as well as a deep dive into how to use Unity and become a paid Game Developer, check out my Unity for 3D Game Design course.
Anyways, back to the tutorial. The input action is loaded on a game object called the Game Manager, and this object has the Player Input component.
We're going to use this component to handle the actions with events.
We'll revisit these methods in a moment, but before then, let's look at 2 solutions and when you might use them.
When dealing with game physics, you're going to come across two components:
Both play an integral role and each have their own pros and cons.
Rigid bodies force Unity to recognize that a game object has mass. This means that physics can be applied to these game objects, and then we can move them around or collide them with other game objects.
For example
If we wanted to create a game of Pool, we could apply this to the white ball so it moves when the cue hits it.
We don't want to only play pool with the white ball though. We want to be able to pot the other balls, and that's where Colliders come in.
Colliders are designed for detecting collisions with other game objects. You can also use this feature to help you perform additional actions, other than physics, during these events.
Colliders also carry some similar characteristics to rigid bodies. If an object has a collider, other objects will not be able to move through the object, even if it doesn't have a rigid body.
If we use the previous example again, then when we hit the white ball, it hits the other balls, and they move around the table. Simple!
In the Unity project I've provided, the floors and walls have collider components while only the player has a rigid body. This is because we don't need to move the floor or walls, only the player.
The configuration settings for these components are set to their default values other than the constraints. This is to prevent the game object from rolling around.
On the Player game object, two custom components are available called:
TransformMovement
, andPhysicsMovement
Let's go through the code in both of them.
In the scripts/TransformMovement.cs file, you'll find the logic for the component. Let's focus on this portion first.
private Vector3 movementVector;
public void HandleMovement(InputAction.CallbackContext context)
{
Vector2 input = context.ReadValue<Vector2>();
movementVector = new Vector3(input.x, 0, input.y);
}
The HandleMovement
method will run whenever the user presses any of the WASD keys.
In this example, we read the input by calling the ReadValue()
method. It'll return a Vector2
, which is converted into a Vector3
since we're dealing with 3D objects.
This information is stored in a property called movementVector
.
Important: Storing the vector outside the method is crucial as we'll need it in another part of our code.
[SerializeField] private int speed = 8;
private void Update()
{
transform.Translate(movementVector * Time.deltaTime * speed);
}
In the Update()
method, we're constantly moving the game object with the Transform component's Translate()
method. This then accepts the direction to move the game object.
For framerate independence, we're multiplying the vector with the Time.deltaTime
variable.
Using the Transform component to move game objects may seem like the obvious choice, but there's one massive pitfall with it.
If you were to attempt to move the game object, everything would go smoothly until you start interacting with other game objects.
This is because the Transform component ignores physics when interacting with other objects.
After the object has been moved with the Transform component, physics is applied, which causes the jittery behavior when pushing against the walls.
Using the transform component to move objects is a great option when you don't need to worry about how an object interacts with the world.
However, if you want to be able to stop objects from moving through obstacles, using physics is your best option.
The rigid body component contains methods for moving game objects, and unlike the transform component, it'll adhere to the laws of physics in our game.
First, we need to grab a reference to the component with the GetComponent
method. You'll find the following code in the PhysicsMovement.cs file.
private Rigidbody rb;
private void Start()
{
rb = GetComponent<Rigidbody>();
}
Next, we can start moving the object with the AddForce
method.
private void FixedUpdate()
{
rb.AddForce(movementVector * speed);
}
Just like before, we're passing in a vector. However, there's one huge difference worth noting, and that's the fact that multiplying the vector with time is not necessary.
Why? Well, it's all thanks to the FixedUpdate
lifecycle functions.
Let me explain.
If we look at the rigid body recommendations in the Unity documentation, it states that using Update
may cause our objects to move inconsistently.
But because we're applying movement in the FixedUpdate
method instead, while also using the Time.deltaTime
, it means that this variable is redundant.
Alright back to the tutorial.
Before testing the code, you should update the event to use the PhysicsMovement.HandleMovement
method, like so:
Here's what the final product looks like.
Unlike before, there's no jittering or movement through the walls. If the player collides with a wall, they're unable to move past it.
If you plan on moving objects, you should always use the correct component methods.
Hopefully this tutorial has helped you get a better grasp on how to use these 2 solutions. Try and set it up for yourself and have a play around!
Want to know something cool?
Although these methods both work, there's actually another way of moving game objects, which is through the use of AI.
It's too much to cover right here, but if you want to learn exactly how to move game objects with AI, check out my Unity 3D Game Development course on ZTM. I walk you through an alternative and more predictable solution for traversing objects across a large map, as well as how to build a complete RPG with Unity!