Unity Physics & Collision 🧊


Rigidbody Component

Unity Physics is applied on components having the Rigidbody component. There is a version for 2D games: Rigidbody2D.

  • Game Object will be subject to gravity
  • Game Object will be colliding with other physical objects

⚠️ To move a character under physical constraints, we mustn't update its transform, but apply "forces" to it. Similarly, we use FixedUpdate instead of Update to update physics (running at a fixed 50 FPS).

RigidBody Update Example Script
using UnityEngine;

[RequireComponent(typeof(Rigidbody))] // or Rigidbody2D
public class RigidBodyExample : MonoBehaviour
{
    [SerializeField] private float speed = 5f;
    private Rigidbody _rb;
    private float _horizontalInput;
    private float _verticalInput;

    private void Start()
    {
        _rb = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        // Save input for use in Fixed Update
        _horizontalInput = Input.GetAxis("Horizontal");
    }

    private void FixedUpdate()
    {
        // Use attributes to calculate positions/velocity/...
        // Example of methods:
        _rb.AddForce(Vector3.left); // move to the left
        _rb.AddForce(Vector3.left * speed);
        _rb.AddForce(Vector3.left * speed, ForceMode.A_MODE_HERE);
        _rb.MovePosition(position);
        _rb.AddExplosionForce(power, explosionPos, radius, 3.0F);
    }
}

A few notes:

  • is kinematics: true: no forces, including gravity
  • use gravity: false: still apply forces but no gravity.
  • Game objects with a lower mass can be pushed back by others.
  • Increase "drag" will slow down the velocity (speed)
  • You can freeze an axis, such as the Rotation z

We turn on Interpolate for the player character to make collisions smoother (⚠️ two interpolated objects colliding may not be smooth at all).

πŸ“š Use Edit > Project Settings > Physics to change the gravity.


Colliders

A collider is an invisible component that will detect when a rigidbody enters a pre-defined space around the game object.

Simple colliders, e.g., with as few edges/vertices as possible, are more performant. According to what kind of games you are doing, try using the simplest collider as possible, even if there is some loss.

Click on a collider to edit it. You'll see some green dots to resize it.

Trigger Colliders

By toggling "is trigger," the collider will path through the other collider instead of colliding with it.

Layers and Colliders

By default, objects may collide even if they are not on the same layer. This is determined by the Layer Collision Matrix.

Use Edit > Project Settings > Physics to configure it.

Physical material

It's an asset that you may create "Create > Physic Material". You can set every friction to 0 and set combine to "minimum" to disable it.

Scripts

You can use these methods to find if game objects are colliding:

// 1. return every collider inside this sphere
Physics.OverlapsSphere(position, radius)
// only layers in mask (ex: mask = LayerMask.GetMask("Default", "Water"))
Physics.OverlapsSphere(position, radius, mask)
// 2. you can use Vector3.Distance (or Vector2)
// 3. you can use Bounds.Intersects
GetComponent<Collider>().bounds.Intersects(anotherBounds);
GetComponent<Renderer>().bounds.Intersects(anotherBounds);

Each Game Object has the methods OnTrigger* and OnCollision* that will be called according to what kind of game objects are colliding:

Collider Collider (isTrigger) Collider+Rigidbody Collider+Rigidbody (kinematic)
Collider x x OnCollisionEnter x
Collider (isTrigger) x x OnTriggerEnter OnTriggerEnter
Collider+Rigidbody OnCollisionEnter OnTriggerEnter OnCollisionEnter OnCollisionEnter
Collider+Rigidbody (kinematic) x OnTriggerEnter OnCollisionEnter x

Raycast

We can generate a ray in a direction and detect which game object is colliding with it. The ray has an origin which is a point where the ray is coming from. It could be from the camera position if we simulate the player eyes. ⚠️ Too much raycasting will cause performance issues.

var ray = new Ray(origin, direction)
Physics.Raycast(ray, out RaycastHit hit)
Physics.Raycast(ray, distance, layerMask)
Physics.Raycast(ray, out hit, distance)
Physics.RaycastAll(...)

➑️ See also: Camera.ScreenPointToRay.

For debugging purposes, we can draw a ray using:

Debug.DrawRay(origin, direction * distance, color)

πŸ‘» To-do πŸ‘»

Stuff that I found, but never read/used yet.

  • Rigidbody: one unit = one meter
  • Rigidbody2D: one unit = base sprite size, see Pixel Per Unit
  • Z-fighting: increase z by 0.01