0

I've run up against a little problem, and I can't seem to find the solution.

I'm putting together a game in Unity3d that includes these gauntlets that can control the environment, i.e. raise downed bridges, open doors, etc. First, I'm just trying to allow the gauntlets to access objects from afar, and I found a telekinesis tutorial online that I am trying to pseudo-repurpose.

This script is supposed to raise a bridge (hence BridgeMover) until it hits a specific position and then lock it in place. Unfortunately, I'm throwing a null reference exception when the bridge hits the "lockbox" object, which is a small cube on the edge of the bridge ramp leading up to the bridge. I know this is simple, but I can't quite conceptualize it, and I've just been staring at it for a few days. This is my big issue, and any help would be appreciated.

In other news, I'm also trying to have the bridge rise as a flat plane instead of following the rotation of the cast ray, and would appreciate any advice about that as well, but I would understand if y'all want me to suffer through on that one. I'm pretty sure it has something to do with the Lerp, which I still don't really understand.

In the hierarchy, I have a handhold object, which is a child of the gauntlet object, which is a child of the camera, which is a child of the player. So

Player
    Camera
        Gauntlet
            handHold

The lockbox object is a child of the ramp that it's attached to. So:

Ramp
    Lockbox

Also, this isn't a criticism of the tutorial; I'm just swimming slightly out of my depths here. The tutorial, for those interested, can be found here.

public class HandController : MonoBehaviour
{

    public static BridgeMover instance;
    public Camera mainCamera;
    Rigidbody rbOfHeldObject;
    GameObject heldObject;
    public GameObject handHold;
    bool holdsObject = false;
    float interactionDistance = 100;
    float slideSpeed = 2;
    Quaternion currentRotation;

    private void Start()
    {
        mainCamera.GetComponent<Camera>();
    }

    private void Update()
    {
        //HandMove();
        if (Input.GetMouseButtonDown(0) && !holdsObject)
            Raycast();
        if (Input.GetMouseButtonUp(0) && holdsObject)
            ReleaseObject();
        if (holdsObject && instance.LockPos())
            LockObject();
        if(holdsObject)
        {
            if (CheckDistance() >= 0.1f)
                MoveObjectToPosition();
        }
    }

    private void Raycast()
    {

        Ray ray = mainCamera.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, interactionDistance))
        {
            if(hit.collider.CompareTag("Bridge"))
            {
                heldObject = hit.collider.gameObject;
                heldObject.transform.SetParent(handHold.transform);

                holdsObject = true;

                rbOfHeldObject = heldObject.GetComponent<Rigidbody>();
                rbOfHeldObject.constraints = RigidbodyConstraints.FreezeAll;
            }
        }
    }

    public float CheckDistance()
    {
        return Vector3.Distance(heldObject.transform.position, handHold.transform.position);
    }

    private void MoveObjectToPosition()
    {
        currentRotation = heldObject.transform.rotation;
        heldObject.transform.position = Vector3.Lerp(heldObject.transform.position, 
            handHold.transform.position, slideSpeed * Time.deltaTime);
        heldObject.transform.rotation = currentRotation;
    }

    private void ReleaseObject()
    {
        rbOfHeldObject.constraints = RigidbodyConstraints.None;
        heldObject.transform.parent = null;
        heldObject = null;
        holdsObject = false;
    }

    private void LockObject()
    {
        rbOfHeldObject.useGravity = false;
        heldObject.transform.parent = null;
        heldObject = null;
        holdsObject = false;
    }
}

And then in the BridgeMover class:

public class BridgeMover : MonoBehaviour
{
    public static BridgeMover instance;
    bool lockPos = false;

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "LockBox")
        {
            lockPos = true;
        }
    }

    public bool LockPos()
    {
        return lockPos;
    }
}

Any help would be appreciated. Also, if any further information is needed, let me know and I'll add it.

  • Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) –  May 26 '22 at 03:34
  • Quite simply because you never set the `public instance` field. (BTW why is it public settable?). Also, there are certain things you need to be mindful of when creating singletons in Unity, it's possible but care is required. For one, Unity is a **CLR Host** it can and will **zap out the Primary App Domain along with all your objects inc singletons** not just during runtime but also whilst using the **Editor**. https://docs.unity3d.com/Manual/ConfigurableEnterPlayModeDetails.html and https://support.unity.com/hc/en-us/articles/210452343-How-to-stop-automatic-assembly-compilation-from-script –  May 26 '22 at 03:39
  • ...and _[Domain Reloading](https://docs.unity3d.com/2019.3/Documentation/Manual/DomainReloading.html)_ (can't believe I forgot to post that one) –  May 26 '22 at 06:48

1 Answers1

0

It looks like you're trying to implement a singleton instance for BridgeMover. Unfortunately, you don't have your instance defined. Here's the standard Unity method for creating a singleton implemented in your BridgeMover code:

public class BridgeMover : MonoBehaviour
{
    public static BridgeMover instance;
    bool lockPos = false;

    void Awake()
    {
        if(instance == null)
        {
            instance = this;
        }
        else
        {
            Destroy(this.gameObject);
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "LockBox")
        {
            lockPos = true;
        }
    }

    public bool LockPos()
    {
        return lockPos;
    }
}

Additionally, in your HandController class you should not declare a static BridgeMover. Instead, reference the static instance properly:

public static BridgeMover instance;

if (holdsObject && BridgeMover.instance.LockPos())
Erik Overflow
  • 2,220
  • 1
  • 5
  • 16