Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Calling an Editor Window From a MonoBehavior script

Discussion in 'Scripting' started by hoperin, May 29, 2018.

  1. hoperin

    hoperin

    Joined:
    Feb 9, 2014
    Posts:
    52
    I'm getting into extending the Editor and I'm totally fine with calling windows from the menu or other EditorWindows, but now I want a window to be called when one of my Monobehaviour scripts reaches a certain point. Right Away I'm running into this error where my Ed_StopWindow is marked red and I'm getting error:

    The type or namespace name `Ed_StopWindow' could not be found. Are you missing an assembly reference?



    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Ink.Runtime;
    5. using UnityEngine.Timeline;
    6.  
    7. #IF UNITY_EDITOR
    8. using UnityEditor;
    9. #ENDIF
    10.  
    11. [ExecuteInEditMode]
    12. public class Scr_InkCrawler : MonoBehaviour {
    13.  
    14. [...]
    15.  
    16. void BringUpWindow(){
    17.  
    18.         Ed_StopWindow window = EditorWindow.GetWindow<Ed_StopWindow>();
    19. }
    20.  
    21.  
    Ed_StopWindow itself is extremely basic and following the same template as all my other Ed_Window scripts. I've tried replacing it with others and I'm not able to call on any of them.


    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using UnityEngine.UI;
    4.  
    5. public class Ed_StopWindow : EditorWindow {
    6.        
    7.     public static void ShowWindow(){
    8.         GetWindow<Ed_StopWindow>("Done Crawling!");
    9.     }
    10.  
    11.     void OnGUI(){
    12.         EditorGUILayout.LabelField ("Done Crawling!");
    13.     }
    14.  
    15. }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,599
    Check out this little blurb about compilation order:

    https://docs.unity3d.com/Manual/ScriptCompileOrderFolders.html

    Quote: The basic rule is that anything that is compiled in a phase after the current one cannot be referenced. Anything that is compiled in the current phase or an earlier phase is fully available.
     
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,738
    This is due to script compilation order. At the time that your runtime scripts are being compiled, your editor scripts literally don't exist as far as the compiler is concerned.

    But there is a way around it: delegates. Basically, in your runtime script, you create a delegate, and call it. In the static initializer of your editor script (which can see your runtime classes just fine), you assign this delegate to one of its own methods. (The static constructor gets run as soon as the scripts are finished compiling) Now, your runtime script can call an editor-level method without ever needing to know that that method actually exists.

    Code (csharp):
    1. public class Ed_StopWindow : EditorWindow {
    2.     [InitializeOnLoadMethod]
    3.     static void Init() {
    4. Scr_InkCrawler.OnBringUpWindow = ShowWindow; // note: there is no () on this
    5. }
    6.  
    7.     public static void ShowWindow(){
    8.         GetWindow<Ed_StopWindow>("Done Crawling!");
    9.     }
    10. }
    11.  
    12. public class Scr_InkCrawler {
    13. public delegate void BringUpWindowDelegate();
    14. public static BringUpWindowDelegate OnBringUpWindow;
    15.  
    16. void BringUpWindow() {
    17. if (OnBringUpWindow != null) OnBringUpWindow();
    18. }
    19.  
    20. }
    21.  
    Sidenote: This is a useful trick in many situations to reduce two-way script dependency. For example, your character control script should, ideally, never actually call anything in your UI, but there are definitely times when you want the UI to respond to stuff your character does. So create a delegate on your character, and have the UI add itself as a listener. If you delete/replace your UI script (say, when you port it to a new platform, for example) your game will still run.
     
    JasonC_ and AndrewChewie like this.
  4. hoperin

    hoperin

    Joined:
    Feb 9, 2014
    Posts:
    52
    Ahh thank you both of you! Looks like it's time for me to hunker down and do some reading, both on Script Compilation and more in depth with the concept of delegates.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,599
    Don't let delegates throw you. They're simply object-oriented type-safe function callbacks. They are a way to specify a bit of code/data that you want someone else to call on your behalf, and then hand that "thing" over to them and they can call it. It is the foundation of asynchronous operations in C#.

    Here's a handy way to think of it:

    If a passing an integer to a function is like giving someone your phone number, then passing a delegate to a function is like giving that same person a custom-built cel phone that can ONLY call you, they never know your number, and there is just one button on the phone that says "Invoke" and when they push it, it calls you.
     
    CheekySparrow78 and hoperin like this.
  6. hoperin

    hoperin

    Joined:
    Feb 9, 2014
    Posts:
    52

    This is a really good analogy, thank you! Funnily enough right after learning about delegates from your post and doing a bit more reading, I ran into another situation where they really helped and now I can really see the power of it!
     
    Kurt-Dekker likes this.