Programming for opening and closing doors when interacted with?

Hello!

First time posting, I hope it’s formatted properly!

To start off I have only been using unity for a month so I am very new to scripting, I am using Unity version 2022.3.47f1 and Visual Studio 17.11.3 .

I am making a prototype for an escape room game, I need the player to be able to pickup coloured keys and unlock their respective locked door with animations (WORKING) and the player needs to pickup/move/drop items (WORKING), but I’m not understanding the programming for interacting with regular/non-locked doors and drawers with LMB…

using UnityEngine;
using System;
public class PickupController : MonoBehaviour
{
    [Header("Pickup Settings")]
    [SerializeField] public Transform holdArea;
    private GameObject heldObject;
    private Rigidbody heldObjectRB;

    [Header("Physics Parameters")]
    [SerializeField] private float pickupRange = 5.0f;    // Pickup range is 5.
    [SerializeField] private float pickupForce = 150.0f;    // Pickup force is 150.

    [Header("Zoom Settings")]
    [Space]
    private bool pickedUp;
    private bool positionSet;
    public LayerMask layerMask;

   
    private void Update()
    {
        if(Input.GetMouseButtonDown(0))    // Check if input is Mouse 0 down (LMB click), if it is...
        {
            if(heldObject == null)    // ... Then check if the held object is equal to null, in other words, if nothing is being held, then...
            {
                RaycastHit hitnew;    // ... Raycast hit (object detection)...
                

                if(Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hitnew, pickupRange))    // ...If Raycast detects object...
                {
                    PickupObject(hitnew.transform.gameObject);    // Pickup the object!
                }

                //if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hitnew, pickupRange, layerMask))    // ...If Raycast detects object...
                //{

                //    PickupObject(hitnew.transform.gameObject); // Pickup the object
            }

            else    // If the above conditions are not met...
            {
                DropObject();    // ... Then drop the object.
            }
            
        }
        if(heldObject != null)    // But if held object does not equal to null/ nothing (in other words, if object is being held)...
        {
            MoveObject(); // ... Then move object.
        }
        

        //this is the framework of detecting interaction.
        //WE want to ensure that the ray is detecting what we look at- NOT JSUT THE CAMERA
        // Once the ray is not detecting the camera. we can then add the if(input.getkeydown) to the script as its detecting the correct item.
        RaycastHit hit;
        if (Physics.Raycast(Camera.main.transform.position, Vector3.forward * 10, out hit, layerMask))
        {
           Debug.Log(hit.transform.gameObject.name);
            if (Input.GetMouseButtonDown(0))
            {
                Draw draw = hit.transform.gameObject.GetComponent<Draw>();

                draw.isOpen = true;
            }
            else
            {
                
            }
        }
    


        if(pickedUp)
        {
            Vector3 newPos = new Vector3(heldObject.transform.position.x, heldObject.transform.position.y, Mathf.Clamp(heldObject.transform.position.z, -0.18f ,01f));

            heldObject.transform.Translate(Camera.main.transform.forward * Time.deltaTime * 100 * Input.GetAxis("Mouse ScrollWheel"), Space.World);    // Enables the scroll wheel to zoom held items towards and away from the player.
            heldObjectRB.constraints= RigidbodyConstraints.FreezeRotation;    // When picked up, object totation is froze.
        }
        else    // If above conditions are not met...
        {
            return;    // ...Try again.
        }

      


        
    }


    void MoveObject()
    {
        if(Vector3.Distance(heldObject.transform.position, holdArea.position) > 0.1f)    // Checking distance between the object being held and the hold area, checking it is greater than 0.1f.
        {
            Vector3 moveDirection = (holdArea.position - heldObject.transform.position);    // Where the held object is going to move to: Held area MINUS the object position = how far object is going to need to move.
            heldObjectRB.AddForce(moveDirection * pickupForce);    // Object is static, so moveDirection causes it to be moved.
        }
    }


    void PickupObject(GameObject pickObj)
    {
        if(pickObj.GetComponent<Rigidbody>())    // Checks if the object has a rigidbody, if not the object can't be held.
        {
            pickedUp = true;
            heldObjectRB = pickObj.GetComponent<Rigidbody>();    // Adds the object as the players held item
            heldObjectRB.useGravity = false;    // Objects original gravity is now false so it will not fall down while being held.
            heldObjectRB.drag = 10;    // Drag force is 10.
            heldObjectRB.constraints = RigidbodyConstraints.FreezeRotation;    // Once picked up, object rotation is froze.
            heldObjectRB.constraints = RigidbodyConstraints.FreezePosition;    // Once picked up, object position is froze.
            heldObjectRB.isKinematic = true;    // When item is picked up, it is kinematic.
            heldObjectRB.transform.parent = holdArea;    // Want to parent picked up object to held area.
            heldObject = pickObj;    // Makes the held object attach to the pickup point.
        }

    }


    void DropObject()
    {
        pickedUp = false;
        heldObjectRB.useGravity = true;    // Held object gets its own gravity back.
        heldObjectRB.drag = 1;    // Held object gets its drag back to 1 (original).
        heldObjectRB.constraints = RigidbodyConstraints.None;    // No more rigidbody constraints.
        heldObjectRB.isKinematic = false;    // When item is dropped, it is not kinematic.
        heldObject.transform.parent = null;    // Held object is no longer a child object of the pickup holder.
        heldObject = null;    // Object not being held.
    }
}

The above is the programming for my ‘PickupController’ for pickup/move/dropping objects, this is working. Lines 53 to 70 is the framework for the opening of regular/non-locked doors and drawers

The person that is helping me with programming wanted to put the door/ drawer opening and closing in the same syntax. I guess another question is should this be separate?

Please let me know if you require more info or anything! Thank you!:slight_smile:

v v v
ETA for clarity


Here is a screenshot of the game, there are objects that can be picked up (boxes, books, colour coded keys…), but I need to make it so the (unlocked) drawers in the desk for instance, or a wardrobe door can be opened with LMB.

The programming above works for picking up, moving, and dropping objects, and another script I have that works to open locked doors, BUT I cannot seem to get programming to open an unlocked door, lol

I’ll attach my programming ‘Doors’ for unlocking doors with keys, which does work!

using UnityEngine;

public class Doors : MonoBehaviour
{
    public Animator animator;
    public string doorType;

    // Start is called before the first frame update
    void Start()
    {
        animator = GetComponentInParent<Animator>();    // Get animator component from child objects.
    }

    public void OnTriggerEnter(Collider collision)    // When collision is detected in a certaion area, then do something if conditions are met.
    {
        if (collision.gameObject.tag == "Green Key" && doorType == "green")    // If there is collision of green key and green door...
        {
            Debug.Log("Green Lock Opened");    // ...Then debug in console...
            animator.SetBool("open", true);    // ...And open!

            Destroy(collision.gameObject);
        }
        else

        {
            if (collision.gameObject.tag == "Blue Key" && doorType == "blue")    // If there is collision of blue key and green door...
            {
                Debug.Log("Blue Lock Opened");    // ...Then debug in console...
                animator.SetBool("open", true);    // ...And open!
            }

            else 
            {
                if (collision.gameObject.tag == "Red Key" && doorType == "red")    // If there is collision of red key and red door...
                {
                    Debug.Log("Red Lock Opened");    // ...Then debug in console...
                    animator.SetBool("open", true);    // ...And open!
                }

                else 
                {
                    return;
                }
            }
        }

       
    }

I was initially wanting to put the regular drawer/ door interactions here, but was told it should be under PickupController

Again, thank you!!

I think I can help. There is a bit of a “smell” here which should (ideally) be attended to sooner rather than later. I would say the issue is that your PickupController knows everything about the object. It can only hold/affect a single object. It knows it has a Rigidbody, it knows the settings the rb should have, etc.

All the the data/processing related to the held object should ideally be encapsulated in a class. It would know the settings, where the rb is, etc. The Controller wouldn’t reference a “GameObject” type but rather one of the held types. It could then check properties and call methods on it related to the actual object and not just generic GO stuff.

I wouldn’t add opening/closing logic to this class. Again that something can be opened and how to open it should be part of the drawer or door.

I’d also remove the empty else statements and other “noise”.

using UnityEngine;
using System;
public class PickupController : MonoBehaviour
{
    [Header("Pickup Settings")]
    [SerializeField] public Transform holdArea;
    private GameObject heldObject;
    private Rigidbody heldObjectRB;

    [Header("Physics Parameters")]
    [SerializeField] private float pickupRange = 5.0f;    // Pickup range is 5.
    [SerializeField] private float pickupForce = 150.0f;    // Pickup force is 150.

    [Header("Zoom Settings")]
    [Space]
    private bool pickedUp;
    private bool positionSet;
    public LayerMask layerMask;

   
    private void Update()
    {
        if(Input.GetMouseButtonDown(0))    // Check if input is Mouse 0 down (LMB click), if it is...
        {
            if (heldObject == null)    // ... Then check if the held object is equal to null, in other words, if nothing is being held, then...
            {
                if (Physics.Raycast(transform.position, transform.forward, out RaycastHit hit, pickupRange))    // ...If Raycast detects object...
                {
                    if (hit.transform.TryGetComponent<Draw>(out Draw draw))
                        draw.isOpen = !draw.isOpen; // toggle drawer open close status
                    else
                        PickupObject(hit.transform.gameObject);    // Pickup the object if we can
                }
            }
            else    // If the above conditions are not met...
                DropObject();    // ... Then drop the object.
        }

        if (heldObject)
        {
            MoveObject(); // ... Then move object.

            Vector3 newPos = new Vector3(heldObject.transform.position.x, heldObject.transform.position.y, Mathf.Clamp(heldObject.transform.position.z, -0.18f ,01f));
            heldObject.transform.Translate(Camera.main.transform.forward * Time.deltaTime * 100 * Input.GetAxis("Mouse ScrollWheel"), Space.World);    // Enables the scroll wheel to zoom held items towards and away from the player.
            heldObjectRB.constraints= RigidbodyConstraints.FreezeRotation;    // When picked up, object rotation is froze.
        }
    }


    void MoveObject()
    {
        if(Vector3.Distance(heldObject.transform.position, holdArea.position) > 0.1f)    // Checking distance between the object being held and the hold area, checking it is greater than 0.1f.
        {
            Vector3 moveDirection = (holdArea.position - heldObject.transform.position);    // Where the held object is going to move to: Held area MINUS the object position = how far object is going to need to move.
            heldObjectRB.AddForce(moveDirection * pickupForce);    // Object is static, so moveDirection causes it to be moved.
        }
    }


    void PickupObject(GameObject pickObj)
    {
        if (pickObj.GetComponent<Rigidbody>())    // Checks if the object has a rigidbody, if not the object can't be held.
        {
            pickedUp = true;
            heldObjectRB = pickObj.GetComponent<Rigidbody>();    // Adds the object as the players held item
            heldObjectRB.useGravity = false;    // Objects original gravity is now false so it will not fall down while being held.
            heldObjectRB.drag = 10;    // Drag force is 10.
            heldObjectRB.constraints = RigidbodyConstraints.FreezeRotation;    // Once picked up, object rotation is froze.
            heldObjectRB.constraints = RigidbodyConstraints.FreezePosition;    // Once picked up, object position is froze.
            heldObjectRB.isKinematic = true;    // When item is picked up, it is kinematic.
            heldObjectRB.transform.parent = holdArea;    // Want to parent picked up object to held area.
            heldObject = pickObj;    // Makes the held object attach to the pickup point.
        }
    }


    void DropObject()
    {
        pickedUp = false;
        heldObjectRB.useGravity = true;    // Held object gets its own gravity back.
        heldObjectRB.drag = 1;    // Held object gets its drag back to 1 (original).
        heldObjectRB.constraints = RigidbodyConstraints.None;    // No more rigidbody constraints.
        heldObjectRB.isKinematic = false;    // When item is dropped, it is not kinematic.
        heldObject.transform.parent = null;    // Held object is no longer a child object of the pickup holder.
        heldObject = null;    // Object not being held.
    }
}

This gave me an error message:(
image

Sorry, I am new to unity and programming so I am not too sure what it means!

ETA
Updated main post with more info if that helps!

Hello!

I completely understand on the “smell”, lol. As this specifically is for a university task where I have to justify each action, that is why there are a lot of comments here! (Helps to keep my word count down in the document as there is a lot to talk about haha)

What would you suggest instead for the programming in a class? Sorry, very new to this stuff:)

Thank you!!

ETA
Updated main post with more info if that helps!

I’ve no idea what the cause of the error message is. To be honest I didn’t even test the code I was just showing you a better place for your drawer open/close code. I’ve looked at the code again and I see there’s more issues. It seems you’re trying to move a kinematic rigidbody with AddForce. That won’t work. And it shouldn’t be necessary to move the carried object once it’s a child of your player because it will move with the parent anyway.

Updated:

using UnityEngine;
public class PickupController : MonoBehaviour
{
    [Header("Pickup Settings")]
    [SerializeField] public Transform holdArea;
    private GameObject heldObject;
    private Rigidbody heldObjectRB;

    [Header("Physics Parameters")]
    [SerializeField] private float pickupRange = 5.0f;    // Pickup range is 5.
    [SerializeField] private float pickupForce = 150.0f;    // Pickup force is 150.

    [Header("Zoom Settings")]
    [Space]
    private bool pickedUp;
    private bool positionSet;
    public LayerMask layerMask;

   
    private void Update()
    {
        if(Input.GetMouseButtonDown(0))    // Check if input is Mouse 0 down (LMB click), if it is...
        {
            if (heldObject == null)    // ... Then check if the held object is equal to null, in other words, if nothing is being held, then...
            {
                if (Physics.Raycast(transform.position, transform.forward, out RaycastHit hit, pickupRange))    // ...If Raycast detects object...
                {
                    if (hit.transform.TryGetComponent<Draw>(out Draw draw))
                        draw.isOpen = !draw.isOpen; // toggle drawer open close status
                    else
                        PickupObject(hit.transform.gameObject);    // Pickup the object if we can
                }
            }
            else
                DropObject();
        }

        if (heldObject)
        {
            Vector3 newPos = new Vector3(heldObject.transform.position.x, heldObject.transform.position.y, Mathf.Clamp(heldObject.transform.position.z, -0.18f ,01f));
            heldObject.transform.Translate(Camera.main.transform.forward * Time.deltaTime * 100 * Input.GetAxis("Mouse ScrollWheel"), Space.World);    // Enables the scroll wheel to zoom held items towards and away from the player.
            heldObjectRB.constraints= RigidbodyConstraints.FreezeRotation;    // When picked up, object rotation is froze.
        }
    }

    void PickupObject(GameObject pickObj)
    {
        if (pickObj.GetComponent<Rigidbody>())    // Checks if the object has a rigidbody, if not the object can't be held.
        {
            pickedUp = true;
            heldObjectRB = pickObj.GetComponent<Rigidbody>();    // Adds the object as the players held item
            heldObjectRB.isKinematic = true;    // When item is picked up, it is kinematic.
            heldObjectRB.transform.parent = holdArea;    // Want to parent picked up object to held area.
            heldObjectRB.interpolation = RigidbodyInterpolation.None;
            heldObject = pickObj;    // Makes the held object attach to the pickup point.
        }
    }

    void DropObject()
    {
        pickedUp = false;
        heldObjectRB.useGravity = true;    // Held object gets its own gravity back.
        heldObjectRB.drag = 1;    // Held object gets its drag back to 1 (original).
        heldObjectRB.constraints = RigidbodyConstraints.None;    // No more rigidbody constraints.
        heldObjectRB.isKinematic = false;    // When item is dropped, it is not kinematic.
        heldObjectRB.interpolation = RigidbodyInterpolation.Interpolate;
        heldObject.transform.parent = null;    // Held object is no longer a child object of the pickup holder.
        heldObject = null;    // Object not being held.
    }
}

This code does work, but I didn’t have an issue with the picking up, holding or dropping of objects with the code I already had. Though, I do appreciate your words of wisdom for a newbie to c sharp as myself! :slight_smile:

My post is me just wondering how to implement the opening/ closing of doors (which do not have locks on them). I have “open” and “close” bool parameters under the door open and door close animations, but no idea how I program the mechanic of, essentially, 'when object clicked with LMB, set bool to “open”/ “close” ’

You have a variety of goals which can conflict. If just completing it is the main one then you can continue to “hammer away” until the code behaves. I assume that will yield a grade of “completed” but it won’t get you closer to completing the next assignment.

I’ve mentioned “a million times” that I don’t write applications. I write code that can be used to write applications. The difference is I’d write a door module (for instance) that could be used in this assignment and pretty much any time I would need a door. It would be testable without it being part of a game. In other words there doesn’t need to be bunch of click events and such to make it work. The UI part of the game is an “overlay” not the program itself.

Imagine we didn’t have eyes and only could speak. Then we might say “unlock door” and it should still unlock. So send a “signal” to unlock and don’t tie unlocking to the way the signal is generated.

Probably too much food for thought but it is separation of the business logic (game logic in this example) from the UI that drives it.

Your class is named Doors (plural) and should by convention be Door (singular). You can have many of them but each of them is a single door.

If you review your OnTriggerEnter method you will see that there are 3 tests and all pretty much do the same thing. For some reason you destroy the gameObject if it is green key.

That BTW is where your comment belongs not // …And open! we can pretty much tell because it set open to true. Nobody can infer why the object was destroyed here.

In any case (again more food for thought) you shouldn’t place all the door logic in OnTriggerEnter. That is the mechanism that you have chosen here but what if there are other ways to open the door (a timer or something)?

OpenDoor seems like a method you should have and the OnTriggerEnter could simply call it. Then you could call OpenDoor from a test framework or from within your code and see what it does.

Any way… you’re making progress so keep at it. Class assignment or not write clean code that you can understand and you’ll make more progress, faster.

Thank you, but I am only 4 weeks into using Unity so my code doesn’t exactly have to be pretty right now. I was just asking for help with this mechanic:)

1 Like