Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How do I reference a canvas?

Discussion in 'Scripting' started by Reject76, Jun 28, 2015.

  1. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    Hello people

    Ok, so I dont get why I cant reference my canvas. I want to do a simple "Press escape for menu"-routine. In my playerscript, attached to the player object, I declare "public Canvas EscCan;"

    Then, in the void start subroutine, I try to get the canvas object using this line: "EscCan = GameObject.Find ("EscapeCanvas");"

    However, that throws two errors:
    - Assets/Scripts/Playercontroller.cs(25,17): error CS0029: Cannot implicitly convert type `UnityEngine.GameObject' to `UnityEngine.Canvas'

    - Assets/Scripts/Playercontroller.cs(51,25): error CS0103: The name `EscapeCanvas' does not exist in the current context

    Ive checked and double checked - the canvas is in my hierarchy with the correct name ("EscapeCanvas"). The first error seems to imply that a canvas isnt a gameobject type...?!
     
  2. Velo222

    Velo222

    Joined:
    Apr 29, 2012
    Posts:
    1,437
    Hi Reject76,

    I think all you need to do is change "public Canvas EscCan" to "public GameObject EscCan". Then use "GameObject.Find("EscapeCanvas"); in your Start() function.

    Almost everytime (if not 100% of the time) when I need to grab something in or underneath my Canvas parent object, they are "GameObject" types. So I think you just need to be using GameObject instead of Canvas.

    Also note that the fact that you are using the "GameOjbect.Find" function means that you are searching for a GameObject specifically.
     
  3. ensiferum888

    ensiferum888

    Joined:
    May 11, 2013
    Posts:
    317
    It is indeed, GameObject.Find will only find and return GameObjects.

    Which is great because your Canvas lives on a GameObject You'll only need to change your code a bit:

    Code (CSharp):
    1. public Canvas EscCan;
    2.  
    3. void Start(){
    4.     //Find the object you're looking for
    5.     GameObject tempObject = GameObject.Find("EscapeCanvas");
    6.     if(tempObject != null){
    7.         //If we found the object , get the Canvas component from it.
    8.         EscCan = tempObject.GetComponent<Canvas>();
    9.         if(EscCan == null){
    10.             Debug.Log("Could not locate Canvas component on " + tempObject.name);
    11.         }
    12.     }
    13. }
     
  4. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    Top notch, that works :) Thanks for the example code too!

    Can I trouble you for some pointers as this yields another problem:

    With my canvas referenced as a gameobject rather than canvas, I cant seem to access its ".enabled" member. I've tried declaring is thus: public bool cannie = EscCan.GetComponent<enabled>();

    Unfortunately, this throws the error public bool cannie = EscCan.GetComponent<enabled>();
     
    Last edited: Jun 28, 2015
  5. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    EscCan.enabled

    enabled being a property of the Canvas component, not a component.
     
  6. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    EscCan.enabled doesnt work cause .enabled doesnt show up in the intellisense as a valid member of EscCan
     
  7. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    then it sounds like you are using it in the wrong place

    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class TestScript : MonoBehaviour
    7. {
    8.  
    9.     public Canvas canvas;
    10.  
    11.     void Start()
    12.     {
    13.             canvas = GetComponent<Canvas>();
    14.             Debug.Log(canvas.enabled);
    15.     }
    16. }
    17.  
    works perfectly fine
     
    topherbwell likes this.
  8. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    LeftyRighty: thanks for your time, but please see my first post. If I declare "public Canvas EscCan;" then I get this error: "
    Assets/Scripts/Playercontroller.cs(25,17): error CS0029: Cannot implicitly convert type `UnityEngine.GameObject' to `UnityEngine.Canvas'"

    If I instead declare "public GameObject EscCan;" I dont get the error, but then I cant call the .enabled member for some reason. Maybe because .enabled is a member of the canvas and Im declaring it as a gameobject?
     
  9. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    it would seem that you've missed that Find returns a GameObject, and then you need to get the component from that gameobject as set it as the canvas.

    I'd suggest having another look at @ensiferum888 code
     
    topherbwell likes this.
  10. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    This is because you were using Find. Use canvas = GetComponent<Canvas>(); as LeftyRighty suggested; this way you just drag the canvas object into the public field in the inspector and all of that part is sorted out. This way you can access the component and use things such as canvas.enabled.
     
  11. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    Cant get it to work. Seems my thick skull is unable to make sense of it. I'll let it rest til tomorrow and see if it comes to me after some sleep...

    Cheers for the help and input though!
     
  12. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    Ok, I kinda almost got it working now.

    Im using "public Canvas EscCan;" and "EscCan = GetComponent<Canvas> ();" in the script on the player object. This gives me a field in the script component in the Inspector where I can drag and drop my canvas.

    Then I use this small code:

    if (Input.GetKey ("escape")) {
    EscCan.enabled = true;

    For some reason though, my canvas will get detached from the player script component when I run my game and the console throws an error that it cant . I cant figure out why.

    The actual canvas is named "EscapeCanvas" in the hierarchy. Surely Ive missed something simple here?
     
  13. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    can we see your current code, and the whole text of the error? :)
     
  14. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    Thanks, of course. The current code:

    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;

    public class Playercontroller : MonoBehaviour {

    private Rigidbody rb;
    private GameObject ball;
    public Text countText;
    public Text winText;
    private int count;
    private int preroundcount;
    private Vector3 startpos;
    public bool levelover;
    public Text resetText;
    public Canvas EscCan;

    //-----

    void Start ()
    {
    levelover = false;
    PlayerPrefs.SetInt ("lvlreached", 0);
    rb = GetComponent<Rigidbody>();
    EscCan = GetComponent<Canvas> ();
    ball = GameObject.Find ("Player");
    startpos = ball.transform.position;
    count = 0;
    preroundcount = 0;
    SetCountText ();
    winText.text = "";
    resetText.text = "";


    }

    //-----

    void FixedUpdate ()
    {

    float moveHorizontal = Input.GetAxis ("Horizontal");
    float moveVertical = Input.GetAxis ("Vertical");
    bool rpress = Input.GetKeyDown ("r");

    if (rpress == true)
    {
    ball.transform.position = startpos;
    resetText.text = "";
    }

    if (Input.GetKey ("escape")) {
    EscCan.enabled = true;
    }



    Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);

    rb.AddForce (movement * 78);

    if (levelover == true) {
    bool spacepress = Input.GetKeyDown ("space");
    if (spacepress == true) {
    Application.LoadLevel ("Scene2");
    }
    }

    }



    //-----



    void OnTriggerEnter(Collider other)
    {
    if (other.gameObject.CompareTag("Finish"))
    {
    other.gameObject.SetActive(false);
    count = count + 1;
    levelover = true;
    SetCountText ();
    }
    if (other.gameObject.CompareTag("floorfall") && levelover == true)
    {
    resetText.text = "Press R to reset";
    bool rpress = Input.GetKeyDown ("r");
    if (rpress == true)
    {
    ball.transform.position = startpos;
    }
    }

    }

    //-----

    void SetCountText ()
    {
    countText.text = "Level: " + PlayerPrefs.GetInt("lvl reached") + 1; //+ count.ToString ();

    if (levelover == true) {
    winText.text = "You made it! Press SPACE to enter next map...";
    rb.mass = 500;
    }
    }

    }

    Error text:
    MissingComponentException: There is no 'Canvas' attached to the "Player" game object, but a script is trying to access it.
    You probably need to add a Canvas to the game object "Player". Or your script needs to check if the component is attached before using it.
    Playercontroller.FixedUpdate () (at Assets/Scripts/Playercontroller.cs:53)
     
  15. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    Sorry, one wee bump since Im still unable to solve this.
     
  16. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    the error clearly says that there isn't a Canvas on the same gameobject as the script... is the canvas component on another game object?
     
  17. Reject76

    Reject76

    Joined:
    Jun 12, 2015
    Posts:
    38
    No, it should be on the player game object. I can drag and drop the canvas onto the field in the script-component of the player object. But for some odd reason the field resets to (None) when I run the game :\
     
  18. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    if you are setting the EscCan reference from the inspector you do not need the GetComponent<>() function line (I'd give you the line number but you didn't use code tags :p)
     
  19. arty155

    arty155

    Joined:
    Apr 17, 2015
    Posts:
    22
    I don't know why you would need to have the canvas as part of the script component of the player object. Personally I would create a separate script that is called "GameMenu.cs" or "EscapeMenu.cs" something to that effect. Have this be the script that controls everything to do with the menu canvas.

    Have you tried type casting to help if you use EscCan as a GameOjbect and not a Canvas?
     
  20. Hanross

    Hanross

    Joined:
    Aug 11, 2018
    Posts:
    11
    EscCan = GameObject.Find("EscapeCanvas").GetComponent<Canvas>(); will also work
     
  21. ipmucci

    ipmucci

    Joined:
    Apr 28, 2014
    Posts:
    2
    using System.Linq;
    ...
    var canvas = GameObject.FindObjectsOfType(typeof(Canvas)).Cast<Canvas>().FirstOrDefault((c) => c.name == "CanvasName");
    ...

    works for me... ;)
     
  22. ipmucci

    ipmucci

    Joined:
    Apr 28, 2014
    Posts:
    2
    tho, if u have only one canvas in the scene:
    var canvas = (Canvas)GameObject.FindObjectsOfType(typeof(Canvas))[0]
     
  23. neoRiley

    neoRiley

    Joined:
    Dec 12, 2008
    Posts:
    158
    The solutions here will work for sure, but one issue that might crop up is timing issues - not to mention that FindObjectsOfType and FindObjectsByTag are expensive. What if the Canvas isn't available at the same time you make the call? What if you're using multiple scenes?

    The sample below will give you the canvas when it's available. If it's added to the manager prior to your call, it will be returned instantly to the observer. Otherwise, it will receive it once it's been added to the manager. This uses UniRX and the Observer pattern.

    NOTE: add UniRx to your unity project - it's free.
    NOTE: I don't like using statics, if you have injection, use that instead. But for the sake of making it easy to implement, welp, here it is in static form ;)

    2 classes:
    1. To add to your canvas object in the IDE -
      CanvasController
    2. The other is
      CanvasManager 
      - static class
    Sample Usage in whatever class you need the Canvas ref:
    Code (CSharp):
    1.  
    2. private Canvas _canvas;
    3.  
    4. private void Awake()
    5. {
    6.   CanvasManager.GetCanvas(CanvasId.LoadoutCanvas).Subscribe(canvas =>
    7.   {
    8.     if (canvas == null) return;
    9.     Debug.Log($"Canvas received for LoadoutCanvas");
    10.     _canvas = canvas;
    11.   }).AddTo(this);
    12. }
    CanvasController (to be added to your Canvas component in the IDE):
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class CanvasController : MonoBehaviour
    4. {
    5.   [SerializeField] private CanvasId _id;
    6.   [SerializeField] private Canvas _canvas;
    7.  
    8.   private void Awake()
    9.   {
    10.     CanvasManager.AddCanvas(_id, _canvas);
    11.   }
    12.  
    13.   private void OnDestroy()
    14.   {
    15.     CanvasManager.RemoveCanvas(_id);
    16.   }
    17. }
    CanvasManager:
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UniRx;
    4. using UnityEngine;
    5.  
    6. public enum CanvasId
    7. {
    8.   MainCanvas,
    9.   LoadoutCanvas
    10. }
    11.  
    12. public class CanvasManager
    13. {
    14.   private static Dictionary<CanvasId, BehaviorSubject<Canvas>> _canvasList = new();
    15.  
    16.   public static IObservable<Canvas> GetCanvas(CanvasId id)
    17.   {
    18.     if (_canvasList.ContainsKey(id)) return _canvasList[id].AsObservable();
    19.     var subject = new BehaviorSubject<Canvas>(null);
    20.     _canvasList.Add(id, subject);
    21.     return subject.AsObservable();
    22.   }
    23.  
    24.   public static void AddCanvas(CanvasId id, Canvas canvas)
    25.   {
    26.     _canvasList ??= new Dictionary<CanvasId, BehaviorSubject<Canvas>>();
    27.  
    28.     if (_canvasList.ContainsKey(id))
    29.     {
    30.       _canvasList[id].OnNext(canvas);
    31.       return;
    32.     }
    33.  
    34.     _canvasList.Add(id, new BehaviorSubject<Canvas>(canvas));
    35.   }
    36.  
    37.   public static void RemoveCanvas(CanvasId id)
    38.   {
    39.     if (_canvasList.ContainsKey(id)) {
    40.        _canvasList[id].OnNext(null);
    41.     }
    42.   }
    43. }
     
    Last edited: Dec 18, 2022