DEV Community

Cover image for How to Open Doors Like a Gentleman in Unity
EneasLari
EneasLari

Posted on • Originally published at eneaslari.com

How to Open Doors Like a Gentleman in Unity

Introduction:

Doors are essential elements in many games, allowing players to navigate between different areas. Today, I’ll show you how to create a simple, yet functional, door that opens and closes when the player approaches and interacts with it. This easy-to-follow tutorial is perfect for beginners looking to add more interactivity to their Unity projects.

Prerequisites

  • Unity 2020.3.0f1 or later.
  • Basic understanding of C# scripting.
  • A simple 3D model of a door (In this tutorial we will use ProBuilder).

Step 1: Crate a new Unity Project

  • Create a new Unity project.

alt text

Step 2: Download StarterAssets - First Person Pack

alt text

  • Download and import to your project

alt text

  • Now open the playground scene from the Starter Pack ( Assets > StarterAssets > FirstPersonController > Scenes)
  • If you Press Play you can roam through scene in FPS

alt text

alt text

Step 3: Create a door with ProBuilder

  • Next we need ProBuilder to make a simple door. Or you can download a door model from Asset store.

  • In order to download Probuilder

  • Click on Window > Package Manager > In packages select Unity Registry then search for probuilder

alt text

  • Lets create a simple door with ProBuilder:

alt text

alt text

  • As you can see from the image above you need two different object to make a door , frame and the actual door

For the auto opening and close door you will need an trigger also

alt text

alt text

Make mesh collider IsTrigger = true

alt text

Step 4: Lets add some scripts

  • We will need three scripts
    • Door.cs
    • PlayerActions.cs
    • DoorTrigger.cs

Lets Create them on by one:

  • Door.cs:
using System.Collections;
using UnityEngine;

public class Door : MonoBehaviour
{
    public bool IsOpen = false;
    [SerializeField]
    private bool IsRotatingDoor = true;
    [SerializeField]
    private float Speed = 1f;
    [Header("Rotation Configs")]
    [SerializeField]
    private float RotationAmount = 90f;
    [SerializeField]
    private float ForwardDirection = 0;
    [Header("Sliding Configs")]
    [SerializeField]
    private Vector3 SlideDirection = Vector3.back;
    [SerializeField]
    private float SlideAmount = 1.9f;

    private Vector3 StartRotation;
    private Vector3 StartPosition;
    private Vector3 Forward;

    private Coroutine AnimationCoroutine;

    private void Awake()
    {
        StartRotation = transform.rotation.eulerAngles;
        // Since "Forward" actually is pointing into the door frame, choose a direction to think about as "forward" 
        Forward = transform.right;
        StartPosition = transform.position;
    }

    public void Open(Vector3 UserPosition)
    {
        if (!IsOpen)
        {
            if (AnimationCoroutine != null)
            {
                StopCoroutine(AnimationCoroutine);
            }

            if (IsRotatingDoor)
            {
                float dot = Vector3.Dot(Forward, (UserPosition - transform.position).normalized);
                Debug.Log($"Dot: {dot.ToString("N3")}");
                AnimationCoroutine = StartCoroutine(DoRotationOpen(dot));
            }
            else
            {
                AnimationCoroutine = StartCoroutine(DoSlidingOpen());
            }
        }
    }

    private IEnumerator DoRotationOpen(float ForwardAmount)
    {
        Quaternion startRotation = transform.rotation;
        Quaternion endRotation;

        if (ForwardAmount >= ForwardDirection)
        {
            endRotation = Quaternion.Euler(new Vector3(0, StartRotation.y - RotationAmount, 0));
        }
        else
        {
            endRotation = Quaternion.Euler(new Vector3(0, StartRotation.y + RotationAmount, 0));
        }

        IsOpen = true;

        float time = 0;
        while (time < 1)
        {
            transform.rotation = Quaternion.Slerp(startRotation, endRotation, time);
            yield return null;
            time += Time.deltaTime * Speed;
        }
    }

    private IEnumerator DoSlidingOpen()
    {
        Vector3 endPosition = StartPosition + SlideAmount * SlideDirection;
        Vector3 startPosition = transform.position;

        float time = 0;
        IsOpen = true;
        while (time < 1)
        {
            transform.position = Vector3.Lerp(startPosition, endPosition, time);
            yield return null;
            time += Time.deltaTime * Speed;
        }
    }

    public void Close()
    {
        if (IsOpen)
        {
            if (AnimationCoroutine != null)
            {
                StopCoroutine(AnimationCoroutine);
            }

            if (IsRotatingDoor)
            {
                AnimationCoroutine = StartCoroutine(DoRotationClose());
            }
            else
            {
                AnimationCoroutine = StartCoroutine(DoSlidingClose());
            }
        }
    }

    private IEnumerator DoRotationClose()
    {
        Quaternion startRotation = transform.rotation;
        Quaternion endRotation = Quaternion.Euler(StartRotation);

        IsOpen = false;

        float time = 0;
        while (time < 1)
        {
            transform.rotation = Quaternion.Slerp(startRotation, endRotation, time);
            yield return null;
            time += Time.deltaTime * Speed;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This script handles the logic for a door that can either rotate or slide when opened or closed. The door's behavior is determined by the IsRotatingDoor flag, and the animation is controlled by coroutines to smoothly transition between states. The Open and Close methods can be triggered by other scripts or events in your game, such as player input or triggers.

  • PlayerActions.cs:
using UnityEngine;

public class PlayerActions : MonoBehaviour
{
    [SerializeField]
    private Transform Camera;
    [SerializeField]
    private float MaxUseDistance = 5f;
    [SerializeField]
    private LayerMask UseLayers;

    public void OnUse()
    {
        if (Physics.Raycast(Camera.position, Camera.forward, out RaycastHit hit, MaxUseDistance, UseLayers))
        {
            if (hit.collider.TryGetComponent<Door>(out Door door))
            {
                if (door.IsOpen)
                {
                    door.Close();
                }
                else
                {
                    door.Open(transform.position);
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This script allows a player to interact with doors in the game by checking if a door is within a certain distance and then opening or closing it based on its current state.

using UnityEngine;

public class DoorTrigger : MonoBehaviour
{
    [SerializeField]
    private Door Door;

    private void OnTriggerEnter(Collider other)
    {
        if (other.TryGetComponent<CharacterController>(out CharacterController controller))
        {
            if (!Door.IsOpen)
            {
                Door.Open(other.transform.position);
            }
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.TryGetComponent<CharacterController>(out CharacterController controller))
        {
            if (Door.IsOpen)
            {
                Door.Close();
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This script uses a trigger collider to automatically open and close a door when a character enters or exits the trigger zone. The OnTriggerEnter method opens the door when the character enters the trigger, and the OnTriggerExit method closes the door when the character leaves the trigger.

Step 5: Attach the scripts

Lets Attach the scripts:

  • Attach the Door.cs script to the Door GameObject

alt text

As you can see from the inspector there is a flag IsRotatingDoor which if its true the door will open rotating if it's false it will slide.

After there are configuration variables for rotation and for sliding.

  • The RotationAmount variable sets the angle that the door will open
  • The ForwardDirection variable sets the direction that the door will open.
  • The SlideDirection variable sets the axis and the direction of the sliding. In the picture the door will open in the z axis and it will slide in the opposite direction.
  • The SlideAmount sets the amount of sliding. Here it will move in the z direction by 1.9

  • Attach the PlayerAction.cs script to the PlayerCapsule GameObject and set the Inspector as seen in the picture bellow.

  • Set camera to MainCamera

  • Create Layer Usable and set the new layer in the inspector of PlayerActions

alt text

In order to make the OnUse function you have to map the Action to the input system. Click on StarterAssets(Input Actions Asset) and open the asset

alt text

alt text

Add Use Action and set the property to "E" button (the key that will open the door)

alt text

Also add the Usable layer on the door object so the PlayerAction works
alt text

  • And Finaly Add DoorTrigger.cs at the DoorTrigger GameObject if you want the door to open automatically when you get close.

alt text

Assign the Door GameObject in the inspector

Now Make a Auto door and a sliding door changing the Variables in the inspector of the Door.cs or Attach the DoorTrigger.cs to the DoorTrigger GameObject

Conclusion:
You've now added a functional door to your Unity project, enhancing its interactivity. Experiment with different door designs and behaviors to create a unique experience in your game.

6. Call to Action

  • Encourage readers to comment, share, or try more tutorials. Example: "Have fun with your new door system! If you have any questions or want to see more tutorials like this, leave a comment below or share your thoughts on social media."

7. Additional Resources

Top comments (0)