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.
Step 2: Download StarterAssets - First Person Pack
Visit StarterAssets - First Person .
Click Add to my Assets and then Open in Unity
- Download and import to your project
- Now open the playground scene from the Starter Pack ( Assets > StarterAssets > FirstPersonController > Scenes)
- If you Press Play you can roam through scene in FPS
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
- Lets create a simple door with ProBuilder:
- 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
Make mesh collider IsTrigger = true
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;
}
}
}
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);
}
}
}
}
}
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();
}
}
}
}
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
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
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
Add Use Action and set the property to "E" button (the key that will open the door)
Also add the Usable layer on the door object so the PlayerAction works
- And Finaly Add DoorTrigger.cs at the DoorTrigger GameObject if you want the door to open automatically when you get close.
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