Door open & Close in C#

Hi all,

Im in my veeeeery early days of learning to script and im doing so by trying to create functions for things that id like to use now and in the future…

my scene:
cube(player) and a door.
when the player walks into the doors trigger the door rotates by 90degrees to open.
(theres extra stuff in the code about activating a button(ignore for now) as i have a button popup which will control the door opening and closing.

heres what i have so far:

public class playerScript : MonoBehaviour {

    public GameObject DoorButton;
    public GameObject Door;
    public float smooth;

    private Quaternion DoorOpen;
    private Quaternion DoorClosed;

    void Start() {

        Door = GameObject.Find("Object001");
        DoorButton = GameObject.Find("DoorButton");
        DoorButton.SetActive(false);
    }

    void OnTriggerEnter(Collider cube)
    {

        if (cube.tag == "DoorButton")
            DoorButton.SetActive(true);
            Debug.Log("button activated");

            DoorOpen = Door.transform.rotation = Quaternion.Euler(0, -90, 0);
            DoorClosed = Door.transform.rotation;

            Door.transform.rotation = Quaternion.Lerp(DoorClosed, DoorOpen, Time.deltaTime * smooth);
            Debug.Log("Door Opened");
    }

This code works but what im having an issue with is this Quaternion madness…i cant seem to get the animation to smooth out, it just pops from 0-90.

anyone know why this is happening?

Thanks

1 Like

I’d recommend you look up “coroutine” and “lerp”. The idea being the trigger starts a coroutine which lerps the rotation.

From the phrasing I’d say you’re on the edge of the “usual” newbie pitfall of trying to get something to occur “overtime” within a trigger function which is all completed within a single frame.

I am not a big one for coroutines, I am much more comfortable using Update. So if anything changes, then you dont have to cancel some coroutine.

using UnityEngine;
using System.Collections;
 
public class DoorController : MonoBehaviour {
    // the door from this object
    public GameObject Door;
    // the door copied and rotated/moved to be the opened door
    public GameObject DoorOpen;
    // this will be a copy of the original door so that we have some numbers to work with.
    private GameObject DoorClosed;
    // this controls if the door is opened or closed.
    public bool isOpened = false;
   
    // this is the movement rate (if movemnt is applied to the door)
    public float moveSpeed = 3;
    // this is the rotation rate (if rotation is applied to the door)
    public float rotationSpeed = 90;
 
    void Start() {
        // copy the door to keep its position
        DoorClosed = Instantiate(Door, Door.transform.position, door.transform.rotation);
        // hide both the open and closed door
        DoorClosed.SetActive(false);
        DoorOpen.SetActive(false);
    }
   
    void Update(){
        // every frame, move the door towards the Open/Closed door
        var target = isOpened ? DoorOpen : DoorClosed;
        // these actually do the moving/rotating
        Door.position = Vector3.MoveTowards(Door.position, target.position, moveSpeed * Time.deltaTime);
        Door.rotation = Quaternion.RotateTowards(Door.rotation, target.rotation, rotateSpeed * Time.deltaTime);
    }
 
    void OnTriggerEnter(Collider cube)
    {
        // whenever anything enters the trigger, open the door
        isOpened = true;
    }
   
    void OnTriggerExit(Collider cube){
        // whenever anything exits the trigger, close the door.
        isOpened = false;
    }
}

Thank for your reply, i took a look at coroutines and it just baffled me. Will need to spend some time getting my head around that one…

Bigmisterb do you mind explaining what you changed in the code and why? Trying to learn so I’d rather know what I’m doing than just copy your code.

Thanks :slight_smile:

1 Like

I updated the code and documented what everything does.

There is an actual problem with it, where the logic that says “if anything enters (exits) Open (close) the door” There of course should be a collector which finds if anything is currently inside the trigger.

The setup is simple though. Create a trigger, create a door, set the Door property with that door, copy the door, and rotate/move it to its open position and assign the DoorOpen with that one. Put the script on the trigger.

For replication sake, attack all that to an empty game object.

so for the actual bug in @_ahmedfawaz_1 's post:

DoorOpen = Door.transform.rotation = Quaternion.Euler(0, -90, 0);
DoorClosed = Door.transform.rotation;

Look at the first line there: You’re setting the rotation to -90, and then assigning it to DoorOpen. What you want is:

DoorOpen = Quaternion.Euler(0, -90, 0);
DoorClosed = Door.transform.rotation;

That should fix your code.

Note that your code only works in one rotation; if you place the same script on a door frame that starts out rotated differently, the door will open too much or in the wrong direction. If the door is a child of the door frame, you can use it’s local rotation instead - that’ll make it work no matter how you’ve rotated the entire thing.

1 Like

Thank you for all your help its really appreciated, think im starting to get a grasp on this now.

Baste i will try your modification tomorrow when im at work as that scene is on my work pc.

At home ive created a new scene to play around with it more and try a different solution using an animator. Please let me know what you think. is it an efficient solution or is this a bad way of doing things…

heres what it does:

code:

public class interact : MonoBehaviour
{
    public bool open = false;
    Animator anim;

    void Start ()
    {
        anim = GetComponentInChildren<Animator> ();
        Debug.Log("GOT THE ANIMATOR");
    }

    void OnTriggerEnter (Collider other)

        {
        if (other.CompareTag("Player"))
        {
            anim.SetBool ("open", true);
            Debug.Log ("OPENING THE DOOR");
        }

    }

    void OnTriggerExit (Collider other)

        {
        if (other.CompareTag("Player"))
        {
            anim.SetBool ("open", false);
            Debug.Log ("OPENING THE DOOR");
        }
    }
}

The animation seems like a good idea. It might fail if you rapidly enter and exit the door - do post if that becomes a problem.

I would suggest using the animator. Still here is how to do it with coroutines:

 public Transform door; //the door
    public float endRotation; //the end rotation
    public float startRotation; //the start rotation
    public float speed; //the speed the door opens
    IEnumerator OpenDoor () //declares a coroutine
    {
        while (door.transform.rotation.y < endRotation) //while the current rotation is less than the end we will continue
        {
            door.Rotate(0, speed * Time.deltaTime, 0); //rotates towards the end rotation
            yield return null; //returns null and will start up here again next frame, yet it will just restart the while loop
        }
    }
IEnumerator CloseDoor ()
    {
        while (door.transform.rotation.y > startRotation)
        {
            door.Rotate(0, -speed * Time.deltaTime, 0);
            yield return null;
        }
    }

Then simply call:

StartCoroutine("OpenDoor");

To open and:

StartCoroutine("CloseDoor");

To close.

Also in OnTriggerExit/Enter just use:

StopAllCoroutines();

Before you start the coroutine you want to then use.

Best of luck! :slight_smile:

Sorry for bringing back such an old thread. But I haven’t seen other better way to making open and closing door with coroutine.
But I’m having trouble with the IEnumerator OpenDoor ()
The operation while (door.transform.rotation.y < endRotation) seems not to work. My door keeps rotating and never ends. But when triggering the CloseDoor coroutine, the door indeed come back to the origin.
Could it be that door.transform.rotation.y is not the same variable as the float of endRotation? or someting like that?

Yeah, that code is just plain wrong. You don’t do rotations like that.

Rotations are stored internally as Quaternions. They have four coordinates (x, y, z, w), which has nothing to do with the x,y,z you see in the inspector - that’s “euler angles”, which is just a conversion.

The code in the earlier posts in the thread show how to handle rotations properly. I’d suggest using @bigmisterb 's code, with a marker object for the start and end position/rotation. You can do the same thing in a coroutine.

1 Like

Dang, little me had no idea how to write code (I still probably don’t lol). That was probably the worst way to do it.

I made a game last week for Ludum Dare, here’s how I did it there:

private IEnumerator DoorAnimation(int targetAngle, int animationSpeed) //animates the door to [targetAngle] using amount of frames [animationSpeed] 
    {
        for (int r = 0; r < animationSpeed; r += 1)
        {
            door.localEulerAngles = new Vector3(0, Mathf.LerpAngle(door.localEulerAngles.y, targetAngle, 5f / animationSpeed), 0);
            yield return null;
        }
    }

Thanks both for your answers. I managed to get @RavenOfCode to work using eulerAngles.y
I like more the “script” aproach for animations than the animator, since I will have lot’s of rotations with triggers and I think animators would not be as flexible. (I’m learnig so I could be totally wrong tho).

But it works only on “positive” rotations. I having trouble to make it work on “negative” rotations. I mean if the idle state is (0 0 0) and I need it to rotate to (0 240 0) but going “counterclockwise”.
I tried Quaternion.Inverse with no luck.

Animations can be more flexible (more options for smaller details and a less robotic look), however I find it easier to use code when doing simple projects like Ludum Dare.

As far as getting negative angles to work just input a negative number. So instead of 240 do -120. This way it knows to rotate counterclockwise (negative angle) rather than clockwise (positive angle).

1 Like

Hey @diego_ger ! So I actually have a free door asset on the store that you could check out if you’re interested.

https://www.assetstore.unity3d.com/en/#!/content/38694

Here is the core rotation code of the asset.

// Move Function
 
    public IEnumerator Move()
    {
        RotationPending = true;
        AnimationCurve rotationcurve = AnimationCurve.EaseInOut(0, 0, 1f, 1f);
        float TimeProgression = 0f; 

        Transform t = transform;

        if (RotationSide == SideOfRotation.Left)
        {
            InitialRot = Quaternion.Euler(0, -InitialAngle, 0);
            FinalRot = Quaternion.Euler(0, -InitialAngle - RotationAngle, 0);
        }
 
        if (RotationSide == SideOfRotation.Right)
        {
            InitialRot = Quaternion.Euler(0, -InitialAngle, 0);
            FinalRot = Quaternion.Euler(0, -InitialAngle + RotationAngle, 0);
        }
 
        if (DoorScale == ScaleOfDoor.Unity3DUnits && PivotPosition == PositionOfPivot.Centered)
        {
            t = hinge.transform;
            RotationOffset = Quaternion.identity;
        }
 
        if (TimesRotated < TimesMoveable || TimesMoveable == 0)
        {
            // Change state from 1 to 0 and back (= alternate between FinalRot and InitialRot 
            if (t.rotation == (State == 0 ? FinalRot * RotationOffset : InitialRot * RotationOffset)) State ^= 1;
 
            // Set 'FinalRotation' to 'FinalRot' when moving and to 'InitialRot' when moving back
            Quaternion FinalRotation = ((State == 0) ? FinalRot * RotationOffset : InitialRot * RotationOffset);
 
            // Make the door/window rotate until it is fully opened/closed
            while (TimeProgression <= (1 / Speed))
            {
                TimeProgression += Time.deltaTime;
                float RotationProgression = Mathf.Clamp01(TimeProgression / (1 / Speed));
                float RotationCurveValue = rotationcurve.Evaluate(RotationProgression);
 
                t.rotation = Quaternion.Lerp(t.rotation, FinalRotation, RotationCurveValue);
 
                yield return null;
            }
 
            if (TimesMoveable == 0) TimesRotated = 0;
            else TimesRotated++;
        }
 
        RotationPending = false;
    }

As you can see I also use an animation curve that then gets evaluated. This provides me with the smoothness and flexibility of animations, while still having the benefits of physical rotations.

I hope you find this interesting, good luck!

Alex

1 Like

just an idea:
using Animation system?

And if you use a raycast and quaternion.
This code can help you, you just need to put it on the door, not on the character’s camera.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Systemdoor : MonoBehaviour
{
    public bool DoorOpen = false;

    public float doorOpenAngel = 95f;
    public float doorCloseAngel = 0.0f;
    public float OpenSmooth = 5.0f;
    public float CloseSmooth = 6.0f;

    public float angularx = 0f;
    public float angularz = 0f;

    //SOUND
    public AudioClip sonido;
    public AudioClip sonido2;
    private AudioSource sound;

    private bool reproducido;
    private bool reproducido2;

    //ACCESS
    public Transform PlayerCamera;
    public GameObject door;
    public float MaxDistance = 3f;

    void Start()
    {
        sound = GetComponent<AudioSource>();
    }


    void Update()
    {

        RaycastHit doorhit;

        if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out doorhit, MaxDistance))
        {
            if (doorhit.collider.gameObject.name == door.name)
            {
                if (Input.GetKeyDown(KeyCode.E))
                {
                    DoorOpen = !DoorOpen;
                    if (!reproducido)
                    {
                        sound.PlayOneShot(sonido);
                        reproducido = true;
                        reproducido2 = false;


                    }

                    else if (!reproducido2)
                    {
                        sound.PlayOneShot(sonido2);
                        reproducido2 = true;
                        reproducido = false;


                    }

                }
            }
        }

                if (DoorOpen)
                {

                    Quaternion targetRotation = Quaternion.Euler(angularx, doorOpenAngel, angularz);
                    transform.localRotation = Quaternion.Slerp(transform.localRotation, targetRotation, OpenSmooth * Time.deltaTime);

                }

                else
                {

                    Quaternion targetRotation2 = Quaternion.Euler(angularx, doorCloseAngel, angularz);
                    transform.localRotation = Quaternion.Slerp(transform.localRotation, targetRotation2, CloseSmooth * Time.deltaTime);

                }
         
     
    }
}

Would this code also apply in my case, a simulation VR?

I would like to open and close the door. Maybe if you could when I grab the handle, rotate it slightly and the door animation is applied.

This then I would like to apply to the interior doors I have. Maybe modifying just something based on the rotation it has to make.

What do you think?

Please don’t necro post. Nobody is going to set up and run your code exactly like you have.

Steps to success:

  • give it a try
  • see if it works
  • if it does not, START A NEW POST to report your issue. It’s FREE!

How to report your problem productively in the Unity3D forums:
http://plbm.com/?p=220

This is the bare minimum of information to report:

  • what you want
  • what you tried
  • what you expected to happen
  • what actually happened, especially any errors you see
    - links to documentation you used to cross-check your work (CRITICAL!!!)

If you post a code snippet, ALWAYS USE CODE TAGS:

How to use code tags: https://discussions.unity.com/t/481379

1 Like