Unity freezes when trying to run my door opening script

Hi all!

I’m working on a game that requires the use of door.

I came up with this script:

using UnityEngine;

public class Door : MonoBehaviour
{
    [SerializeField] enum OpenDirection { HORIZONTAL, VERTICAL };
    [SerializeField] OpenDirection openDirection;

    [SerializeField] float angle;

    private Vector3 originalRotation;

    public bool open = false;

    private void Start()
    {
        originalRotation = transform.eulerAngles;

        Open();
    }


    public void Open()
    {

        switch (openDirection)
        {
            case OpenDirection.HORIZONTAL:
                while (transform.eulerAngles.z != angle)
                {
                    transform.eulerAngles = new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, Mathf.Lerp(transform.eulerAngles.z, angle, 2 * Time.deltaTime));
                }
                break;

            case OpenDirection.VERTICAL:
                while(transform.eulerAngles.x != angle)
                {
                    transform.eulerAngles = new Vector3(Mathf.Lerp(transform.eulerAngles.x, angle, 2 * Time.deltaTime), transform.eulerAngles.y, transform.eulerAngles.z);
                }
                break;
        }

        open = true;

    }

    public void Close()
    {
        while(transform.eulerAngles != originalRotation)
        {
            transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, originalRotation, 2 * Time.deltaTime);
        }

        open = false;
    }
}

However, Unity freezes whenever I call the Open method (I’m calling it in start not Update).
I don’t know why it’s doing this, as there really shouldn’t be a n infinite for-loop right?

Unity will lock up 100% of the time EVERY millisecond your scripting code is running.

Nothing will render, no input will be processed, no Debug.Log() will come out, no GameObjects or transforms will appear to update.

Absolutely NOTHING will happen… until your code either:

  • returns from whatever function it is running

  • yields from whatever coroutine it is running

As long as your code is looping, Unity isn’t going to do even a single frame of change. Nothing.

No exceptions.

“Yield early, yield often, yield like your game depends on it… it does!” - Kurt Dekker

Use animations for doors already, no need to do silly code tricks.

I want to refrain from using animations for doors as I really don’t want to make an animation for every door I in the game.
I really want a “universal door” script.

Is there any way I can fix this with code?

You’re doing animations wrong. The same animation would be used for all instances of a given door. You make a prefab and when it works perfectly, you use it for all doors.

Use a tweener then (perhaps DOTween?), unless you wanna write your own. :slight_smile:

Here was my door animation solution post:

A: Your while loop is going infinite because
B: Don’t compare floating point values for equality
C: Write a coroutine if you want things to happen over time.

1 Like

Oh yeah! I’ll try to come up with something using a coroutine. Idk why I didn’t think of this lol

Okay, I put everything in a coroutine, and even accounted for floating point precision. However, while my doors open now, they continue to spin forever seemingly ignoring the conditions of the while loop.

using System.Collections;
using UnityEngine;

public class Door : MonoBehaviour
{
    [SerializeField] enum OpenDirection { HORIZONTAL, VERTICAL };
    [SerializeField] OpenDirection openDirection;

    [SerializeField] float angle;

    private Vector3 originalRotation;

    public bool open = false;

    private void Start()
    {
        originalRotation = transform.eulerAngles;

        Interact();
    }

    public void Interact()
    {
        if (!open)
        {
            open = true;
            StartCoroutine("Open");
        }
        else
        {
            open = false;
            StartCoroutine("Close");
        }
    }

    IEnumerator Open()
    {
        print("Opening door");
        switch (openDirection)
        {
            case OpenDirection.HORIZONTAL:
                while (Mathf.Abs(transform.eulerAngles.z - angle) > 0.01) //for floating point precsion
                {
                    transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, angle), 2 * Time.deltaTime);
                    yield return null;
                }
                break;

            case OpenDirection.VERTICAL:
                while (Mathf.Abs(transform.eulerAngles.x - angle) > 0.01) //for floating point precsion
                {
                    transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, new Vector3(angle, transform.eulerAngles.y, transform.eulerAngles.z), 2 * Time.deltaTime);
                    yield return null;
                }
                break;
        

        }



    }

    IEnumerator Close()
    {
        print("Closing Door");
        while (transform.eulerAngles != originalRotation)
        {
            transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, originalRotation, 2 * Time.deltaTime);
            yield return null;
        }

    }
}

Because
A: You’re using lerp incorrectly. It is not correct to throw Time.deltaTime into lerp as that demonstrates a misunderstanding on how it works.
B: Euler angles are not reliable way to measure rotations.

Use something like Quaternion.RotateTowards: Unity - Scripting API: Quaternion.RotateTowards which won’t overshoot.

1 Like

What do you recommend then to use as the condition for the while loop?

Also, why is it not reamended to use Time.deltaTime in a lerp? if t is the percent we need to lerp between the two values, wouldn’t using Tim.deltaTime sync between frames?

Maintain your own start and end values, and maintain a value that moved between a and b. Your while loop can operate on your own value, and not the euler angles.

Rough example:

private IEnumerator ExampleDoorCoroutine()
{
    float startingAngle = 0f;
    float targetAngle = 90f;
    
    float t = 0f;
    
    while (t < 1f)
    {
        float angle = Mathf.Lerp(startingAngle, targetAngle, t);
        Quaterion angleRotation = Quaterion.AngleAxis(angle, Vector3.up);
        Quaterion finalRotation = Quaterion.identity * angleRotation;
        transform.rotation = finalRotation;
        t += Time.deltaTime;
        yield return null;
    }
}

As always it is easier to manage your own data than rely on other’s implementation details.