Unity: Physics Movement

  • Requirement: Unity
  • Language: C#
  • Difficulty: Easy
  • Written by: Lee Zhi Eng
  • Last update: 1 Sep 2019

Introduction

We have learned how to move and rotate our game object/character in our previous tutorials. Those movements are very basic and the game object does not interact with its surrounding, which is not very cool.

In this tutorial, we will learn how we can create a more convincing movement that allows the game character to interact with its surround using the physics system provided by Unity.

Rigid Body

Let’s get started by creating a cube and a plane to our scene. Then, move the cube slightly above the plane, like this:

After that, apply a Rigidbidy component to the cube by first selecting the cube, then go to Component 🡒 Physics 🡒 Rigidbody. If you press the ► Play button now, you will see the cube start falling down until it hit the plane and stop. Congratulations, you have now added physics to your game!

You can then duplicate the cube into several units by using Ctrl D and place them around the scene. Press the play button again and behold, you have successfully created a cool physics demo similar to the video shown below! It’s really that simple!

Movement Conflict

However, we’re not quite done yet. Let’s make one of the cubes red by applying a custom material to it. Go to Assets 🡒 Create 🡒 Material. Drag and drop the new material to the cube and set its Albedo color to red. You should get something like this:

If you try and apply the movement script from our previous tutorial to the red cube, you may get the following result:

As you can see, the result looks very unnatural and you can hardly control the cube’s movement. This is because we are directly moving the game object’s position and the physics system is not aware of what is going on, thus having conflict with the movement.

Kinematic Body

To solve this issue, we first have to make sure the Is Kinematic property is checked on the rigid body:

This will turn the rigid body into a kinematic body, which gives you more control on the physics object. Even though the movement behaves a lot better now, but we can still see some intersections when colliding with other cubes:

Subtle but noticeable glitches

This is because we are manually moving the game object around and the physics body couldn’t catch up with the movement, causing some delays on the collision detection.

Kinematic Body Movement

The ultimate fix is by changing all the movement code to use only methods provided by the physics system. Let’s take a look at the new movement code:

public Rigidbody body;

void Update()
{
    float speed = 10;
    if (Input.GetKey(KeyCode.W))
    {
        body.MovePosition(body.position + transform.forward * speed * Time.deltaTime);
    }
    if (Input.GetKey(KeyCode.S))
    {
        body.MovePosition(body.position -transform.forward * speed * Time.deltaTime);
    }
    if (Input.GetKey(KeyCode.A))
    {
        body.MovePosition(body.position -transform.right * speed * Time.deltaTime);
    }
    if (Input.GetKey(KeyCode.D))
    {
        body.MovePosition(body.position + transform.right * speed * Time.deltaTime);
    }
}

Comparing the code above with the movement code used in our previous tutorial, the only difference is that we used the MovePosition function provided by the Rigidbody component to move our game object instead of directly manipulating the position data of the Transform component. This guarantees the accuracy of the collision detection and eliminates ugly intersections.

Make sure that the Rigidbody variable is set to public, otherwise it won’t appear on the GUI. Then, drag and drop the red cube from the hierarchy window to the Body input on its own movement script so that it will link its Rigidbody component to the Movement component. The movement script will then know which rigid body we’re going to use when calling the MovePosition function:

The result speaks for itself:

Accurate collision detection

Kinematic Body Rotation

As for rotation, we use Rigidbody’s MoveRotation function instead of Transform’s RotateAround:

float rotateSpeed = 100;
if (Input.GetKey(KeyCode.Q))
{
    body.MoveRotation(transform.rotation * Quaternion.Euler(new Vector3(0, -rotateSpeed, 0) * Time.deltaTime));
}
if (Input.GetKey(KeyCode.E))
{
    body.MoveRotation(transform.rotation * Quaternion.Euler(new Vector3(0, rotateSpeed, 0) * Time.deltaTime));
}

The MoveRotation function only accepts input variable in the form of Quaternion. Therefore we used the Euler function from the Quaternion class to convert our rotation speed, which is in Vector3 format into Quaternion.

Constraint

If for some reason you don’t want your game object to move or rotate along certain axis (i.e. you’re making a side scrolling game), you can unhide the Constraints options on the Rigidbody component and check on the axis that you don’t want it to move or rotate along:

Apply Force

What if you want your game object to behave in a physically correct manner, instead of what we have just done in the previous examples? Let’s say we are making a physics puzzle game that involves a sphere as our game “character”. We can’t produce a convincing movement if we use our previous movement code because an actual sphere wouldn’t move in such a rigid way.

To demonstrate how we can create a more convincing movement that involves physics, let’s get rid of all the cubes and create a 3D sphere instead, by going to GameObject 🡒 3D Object 🡒 Sphere:

Make sure you have added a Rigidbody component to the sphere, then create a C# script called sphere.cs and add the following code to its Update function:

public Rigidbody body;

void Update()
{
    float force = 100;
    if (Input.GetKey(KeyCode.W))
    {
        body.AddForce(new Vector3(0, 0, force) * Time.deltaTime);
    }
    if (Input.GetKey(KeyCode.S))
    {
        body.AddForce(new Vector3(0, 0, -force) * Time.deltaTime);
    }
    if (Input.GetKey(KeyCode.A))
    {
        body.AddForce(new Vector3(-force, 0, 0) * Time.deltaTime);
    }
    if (Input.GetKey(KeyCode.D))
    {
        body.AddForce(new Vector3(force, 0, 0) * Time.deltaTime);
    }
}

From the code above, we applied force to the rigid body instead of directly moving its position. This way, the game object will behave more naturally just like in the real world. The code above produces the following result:

Source Code

Cube Movement:

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

public class movement : MonoBehaviour
{
    public Rigidbody body;

    // Start is called before the first frame update
    void Start()
    {
        
    }
    // Update is called once per frame
    void Update()
    {
        float speed = 10;
        if (Input.GetKey(KeyCode.W))
        {
            body.MovePosition(body.position + transform.forward * speed * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.S))
        {
            body.MovePosition(body.position -transform.forward * speed * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.A))
        {
            body.MovePosition(body.position -transform.right * speed * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.D))
        {
            body.MovePosition(body.position + transform.right * speed * Time.deltaTime);
        }

        float rotateSpeed = 100;
		if (Input.GetKey(KeyCode.Q))
        {
            body.MoveRotation(transform.rotation * Quaternion.Euler(new Vector3(0, -rotateSpeed, 0) * Time.deltaTime));
        }
        if (Input.GetKey(KeyCode.E))
        {
            body.MoveRotation(transform.rotation * Quaternion.Euler(new Vector3(0, rotateSpeed, 0) * Time.deltaTime));
        }
    }
}

Sphere Movement:

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

public class sphere : MonoBehaviour
{
    public Rigidbody body;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        float force = 100;
        if (Input.GetKey(KeyCode.W))
        {
            body.AddForce(new Vector3(0, 0, force) * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.S))
        {
            body.AddForce(new Vector3(0, 0, -force) * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.A))
        {
            body.AddForce(new Vector3(-force, 0, 0) * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.D))
        {
            body.AddForce(new Vector3(force, 0, 0) * Time.deltaTime);
        }
    }
}

Leave a Reply