Unity Doors: Open and Close With Animation, Triggers or Code

Illustration: a sci-fi sliding door opening with glowing light spilling out

Doors look simple until you build one: it needs to open the right way, not knock the player over, close again, maybe stay locked until you find a key. This guide covers three ways to make Unity doors that open and close: an Animator-driven door, a trigger door that opens as you approach, and a code-only door that rotates or slides smoothly. Then it adds locked doors and keys on top.

Set Up the Pivot First

A hinged door must rotate around its hinge edge. Most door meshes and Unity’s Cube have their pivot in the centre, so rotating them spins the door around its middle.

The fix is a parent:

  1. Create an empty GameObject called DoorHinge and place it exactly on the hinge edge of the door.
  2. Make the door mesh a child of DoorHinge.
  3. Rotate DoorHinge, not the mesh.

Set the Scene view handle to Pivot (not Center) in the toolbar to see the real pivot. If you model doors in Blender, set the origin on the hinge before export, and check the FBX export settings guide so the rotation imports cleanly.

Option 1: A Code-Driven Door (No Animator)

For a single swinging or sliding door, a script is simpler than an Animator. This one handles both and plugs into an interaction system:

using UnityEngine;

public class Door : MonoBehaviour
{
    public enum Kind { Swing, Slide }

    [SerializeField] Kind kind = Kind.Swing;
    [SerializeField] Transform moving;            // the hinge or the sliding panel
    [SerializeField] float openAngle = 90f;
    [SerializeField] Vector3 slideOffset = new Vector3(1.2f, 0f, 0f);
    [SerializeField] float speed = 4f;
    [SerializeField] bool startOpen;

    public bool IsOpen { get; private set; }

    Quaternion closedRot;
    Vector3 closedPos;
    float t;          // 0 closed, 1 open
    float direction = 1f;

    void Awake()
    {
        closedRot = moving.localRotation;
        closedPos = moving.localPosition;
        IsOpen = startOpen;
        t = startOpen ? 1f : 0f;
        Apply();
    }

    public void Open(float dir = 1f) { direction = dir; IsOpen = true; }
    public void Close() => IsOpen = false;
    public void Toggle() { if (IsOpen) Close(); else Open(direction); }

    void Update()
    {
        float target = IsOpen ? 1f : 0f;
        if (Mathf.Approximately(t, target)) return;
        t = Mathf.MoveTowards(t, target, speed * Time.deltaTime);
        Apply();
    }

    void Apply()
    {
        float eased = Mathf.SmoothStep(0f, 1f, t);
        if (kind == Kind.Swing)
            moving.localRotation = closedRot * Quaternion.Euler(0f, openAngle * direction * eased, 0f);
        else
            moving.localPosition = closedPos + slideOffset * eased;
    }
}

Using a 0 to 1 value t with SmoothStep gives ease-in and ease-out, and reversing mid-swing just works because it moves from wherever it is. Rotation is built from the closed rotation plus an offset, so you never accumulate errors from repeated small rotations. For background on why, see the quaternions guide and the Lerp guide.

Option 2: Automatic Trigger Door

Sci-fi sliding doors usually open when anyone gets close and close when the area is empty. Add a Box Collider with Is Trigger ticked in front of and behind the door, then:

using UnityEngine;

[RequireComponent(typeof(Collider))]
public class AutoDoorTrigger : MonoBehaviour
{
    [SerializeField] Door door;
    [SerializeField] LayerMask whoOpens;
    int inside;

    void OnTriggerEnter(Collider other)
    {
        if ((whoOpens.value & (1 << other.gameObject.layer)) == 0) return;
        inside++;
        door.Open();
    }

    void OnTriggerExit(Collider other)
    {
        if ((whoOpens.value & (1 << other.gameObject.layer)) == 0) return;
        inside = Mathf.Max(0, inside - 1);
        if (inside == 0) door.Close();
    }
}

Counting how many are inside stops the door slamming shut on the player when an enemy leaves. Remember that triggers need a Rigidbody or CharacterController on one side. If they don’t fire, see the OnTriggerEnter guide.

Objects destroyed inside the trigger never call OnTriggerExit, so the count can get stuck. If enemies die in doorways, track colliders in a HashSet and remove nulls instead of using a plain counter.

Option 3: Animator Door

Use an Animator when the door has a designed animation: a heavy vault door, a door with a latch that turns first, or a double door with timing. Setup:

  1. Create two clips: Open and Close, animating the hinge rotation. Untick Loop Time on both.
  2. In the Animator Controller add a bool parameter called IsOpen.
  3. Add states Closed (idle), Open, and Close. Transition Closed to Open when IsOpen is true, Open to Close when false. Turn off Has Exit Time on these transitions so they react instantly.
  4. From code: animator.SetBool("IsOpen", true).

A bool is better than triggers for doors, because the door’s state always matches the bool even if the player spams the button. If the door animates late or gets stuck, the Animator not playing guide covers the usual causes.

Opening Away From the Player

A swing door should open away from whoever opens it. Work out which side they’re on with a dot product, then pass the direction to Open:

public void OpenAwayFrom(Vector3 position)
{
    Vector3 toUser = position - moving.position;
    float side = Vector3.Dot(moving.forward, toUser) > 0f ? -1f : 1f;
    Open(side);
}

Add this method to the Door script. Depending on how your door model faces, you may need to swap the signs.

Locked Doors and Keys

A lock is just a condition before opening. If you use the IInteractable pattern from the Press E interaction guide, implement it on the door:

using UnityEngine;

public class LockedDoor : MonoBehaviour, IInteractable
{
    [SerializeField] Door door;
    [SerializeField] string requiredKey = "RedKey";
    [SerializeField] bool locked = true;

    public string Prompt => locked ? "Locked" : (door.IsOpen ? "Close" : "Open");

    public bool CanInteract(GameObject who) => true;

    public void Interact(GameObject who)
    {
        if (locked)
        {
            var keys = who.GetComponent<KeyRing>();
            if (keys == null || !keys.Has(requiredKey)) return; // play a rattle sound here
            locked = false;
        }
        if (door.IsOpen) door.Close(); else door.OpenAwayFrom(who.transform.position);
    }
}

KeyRing can be as simple as a HashSet<string> with Add and Has methods. For a real game, use ScriptableObject keys instead of strings so typos can’t break a door. The ScriptableObjects guide shows that pattern.

Physics and NavMesh

  • Don’t knock the player over. A door moved by transform won’t push Rigidbodies properly. Either make the panel a kinematic Rigidbody moved with MovePosition/MoveRotation in FixedUpdate, or put the moving panel on a layer that doesn’t collide with the player while animating.
  • Physics doors. For a door the player can push, use a Rigidbody with a Hinge Joint and angle limits.
  • AI and doors. Add a NavMesh Obstacle with Carve enabled to the door panel, so agents path around closed doors and through open ones. Or use NavMesh Links and open doors when agents arrive. The NavMesh tutorial covers obstacles.

🔧 Door and prop packs

Modular door packs with animations and sounds can fill a level quickly. Check our list of useful Asset Store tools before modelling everything yourself.

Troubleshooting

The door spins around its middle

The pivot is in the centre. Parent the mesh under an empty placed on the hinge edge and rotate the parent.

The door opens the wrong way

Flip the sign of the angle, or swap the dot product signs in OpenAwayFrom.

The door teleports instead of animating

You’re setting the final rotation directly. Interpolate a 0 to 1 value over time as in the Door script.

Animator door ignores the bool for a second

Has Exit Time is on. Turn it off on the transitions and set the duration short.

Player gets stuck or launched by the door

The door’s collider is pushing the player as it moves. Use a kinematic Rigidbody for the panel or disable collisions while it moves.

FAQ

Animator or code for doors?

Code for simple swings and slides; it’s less setup and easy to reverse mid-move. Animator when the door has a designed, multi-part animation.

How do I make double doors?

Use two hinges with opposite open angles, both driven by one script or one Animator bool.

How do I save whether a door is open?

Give each door a unique ID and store its IsOpen and locked values in your save data, then apply them on load.

Can I use Lerp instead of MoveTowards?

Yes, but MoveTowards on a 0 to 1 value finishes at an exact time, and SmoothStep adds easing. Lerp with a fixed fraction never quite arrives.

Why does my door not block the player when closed?

The panel needs a non-trigger collider, and the layers must collide in the physics matrix.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *