unity-game-enginevrtkunity-components

Using components from a linked gameObject in a script


Sorry if this is too simple, but i've not been able to find an answer on this. Usually either the question is too complicated to answer or too simple to not know.

THIS IS PARTIALLY A UNITY VR/VRTK ISSUE (maybe...)

Anyways, my goal is to set up triggers for my Canvas to be able to start a video player and have it enable and appear in the scene when an object is being picked up. As of now, everything is in place except for my ability to trigger both those actions when the player interacts with the game object.

Within my script, I have:

public VideoPlayer videoPlayer;
public Canvas canvas;
public GameObject asset;
    void Awake ()
{
    videoPlayer.GetComponent<VideoPlayer>();
    canvas.GetComponent<Canvas>();

}

private void Start()
{
    videoPlayer.Pause();
    canvas.enabled = !canvas.enabled;


}

Simple, but everything gets linked within unity and its alot of fun. My problem is that im trying to reference a script that the GameObject has on it so that I can use its triggers in order to enable the video player to do its thing. Theres a simple bool within the Interactible script attached to it that I think might work or maybe i can write my own, problem is i have no idea how to reference from the GameObject components. Maybe im just not understaning it but ive looked for a while and now I turn to you, the collective for help...

help :(

-Thanks to all in advance, there's more pieces to the project but i thought these would be the most relevant. Let me know if i missed something needed.


Solution

  • I don't really understand what is your final goal yet but those two lines

    videoPlayer = videoPlayer.GetComponent<VideoPlayer>();
    canvas = canvas.GetComponent<Canvas>();
    

    don't make much sense. You either have the references already (e.g. from the inspector) than you don't need to get the components again or you want to get them from a GameObject

    In case you want the components attached to the same GameObect as the provided script instead use

    videoPlayer = GetComponent<VideoPlayer>();
    canvas = GetComponent<Canvas>();
    

    Or in case you rather want to get the components from the asset GameObject use

    videoPlayer = asset.GetComponent<VideoPlayer>();
    canvas = asset.GetComponent<Canvas>();
    

    Than you can add a public method to call it whenever "interacting" (however that looks like) with the GameObject

    public void ReactToInteraction()
    {
        // E.g.
    
        canvas.enabled = true;
        videoPlayer.Play();
    }
    

    From your comment

    My main problem revolves around trying to access a script thats attached to a different game object. In the script im writing, I have it attached to a canvas but I want it to be triggered by a componemt that is inside of a different game object that isn't associated with this canvas.

    I understand you somewhere have a "triggering event" lets say on a GameObject ObjectA in a class TriggeringBehaviour e.g.

    void OnTriggerEnter(Collider col)
    {
        //...
    }
    

    and you want a method ReactToInteraction be called on your canvas whenever the trigger is called.

    There are many ways to do so but I personally would prefere one of following:

    Solution 1: direct call

    If you "know" your references, either referencing in the inspector or have some form of finding them using e.g. Singleton or FindObjectOfType, Find (by name) etc you could just directly call the method from TriggeringBehaviour:

    // todo get this somehow with the known methods
    public CanvasBehaviour ReferenceToYourCanvasScript;
    
    void OnTriggerEnter(Collider col)
    {
        //...
    
        ReferenceToYourCanvasScript.ReactToInteraction;
    }
    

    Solution 2: event Action

    You can implement custom events in the TriggeringBehaviour by using e.g.

    public event Action OnTriggered;
    
    void OnTriggerEnter(Collider col)
    {
        //...
    
        if(OnTriggered!=null)
        {
            OnTriggered.Invoke();
        }
    }
    

    Than you would have to register listeners to those events in your CanvasBahviour

    private void Start()
    {
        //TODO somehow get the according reference e.g.
        var triggerBahviour = FindObjectOfType<TriggeringBahviour>();
    
        // and add the listener
        // it is save to remove the listener first -> makes sure it is only registered once
        triggerBahviour.OnTriggered -= ReactToInteraction;
        triggerBahviour.OnTriggered += ReactToInteraction;
    
        // after that whenever OnTriggered is invoked, ReactToInteraction will be executed
    }
    

    Alternatively you can also make a static class for handling those events

    public static class TriggerEvents
    {
        // I leave this as example on how to give it parameters
        public static event Action<TriggerBehaviour> OnTriggeredBy;
    
        public static event Action OnTriggered;
    
        public static void InvokeOnTriggered()
        {
            if(OnTriggered!=null) OnTriggered.Invoke();
        }
    
        public static void InvokeOnTriggeredBy(TriggeringBehavior by)
        {
            if(OnTriggeredBy!=null) OnTriggeredBy.Invoke(by);
        }
    }
    

    With this way it is a bit easier to register and invoke the events:
    in TriggerBehaviour

    void OnTriggerEnter(Collider col)
    {
        //...
        TriggerEvents.InvokeOnTriggered();
    
        // or if you want to also check who triggered
        // TriggerEvents.InvokeOnTriggeredBy(this);
    }
    

    and in CanvasBehaviour

    void Start()
    {
        TriggerEvents.OnTriggered -= ReactToInteraction;
        TriggerEvents.OnTriggered += ReactToInteraction;
    
        // or if you want to checl who triggered
        // TriggerEvents.OnTriggeredBy -= ReactToInteractionBy;
        // TriggerEvents.OnTriggeredBy += ReactToInteractionBy;
    }
    
    // In case you need the ReactToInteractionBy
    // has to have the same signature as defined by the Action
    void ReactToInteractionBy(TriggeringBehaviour triggerer)
    {
        // ...
    }
    

    This is very flexible and can also pass as many parameters you want/need (In this case none anyway). However you still have to somehow get the according reference to the correct component.
    One big flaw is that registering and especially destroying something without unregistering can lead to errors and be quite frustrating.

    Solution 3: UnityEvent (My personal favourite)

    Alternatively you can also implement a UnityEvent in TriggeringBehaviour

    public event Action OnTriggered;
    
    void OnTriggerEnter(Collider col)
    {
        // ...
    
        OnTriggered.Invoke();
    }
    

    The big advantage is that this UnityEvent will be shown and configurable in the Inspector! (You'll recognize it from the UI Button onClick event)
    So you can just reference any other component from the inspector and don't have to register listeners at runtime (You still can do so via triggerBahviour.OnTriggered.AddListener()).
    However UnityEvent is kind of limitted. E.g. it is not that simple to have different Methods called with different signatures that have multiple or non-standart parameters (such as custom class references).