multi GUI.Windows: update IDs when mouseDown

I all,
I have this script assigned to many objects on my scene…(I want to use one script for all objects on the scene)

    //private var mouseStop : MouseSTOP;
    
    private var windowOpen : boolean;
    private var windowZoom : boolean;
    private var Dialog : boolean;
    
    var aTexture : Texture;
    private var aTextureWidth : int;
    private var aTextureHeight : int;
    
    private var posX : int = 10;
    private var posY : int = 10;
    
    private var windowRect : Rect; 
    private var windowRectZoom : Rect;
    
    //private var myCustomSkin : GUISkin; 
    var myCustomSkin : GUISkin; 
    
    private var button : int;
    
    // hide the window
    function Start() {
    	Dialog = false;
    	Hide();	
    }
    
    function Update () {
    	
    	if(Dialog){            
          //GetComponent("MouseLook").enabled = false;            
          GameObject.Find("MainCamera").GetComponent("MouseLook").enabled = false;      
        }
    
        else {
          //GetComponent("Mouse Look").enabled = true; 
          GameObject.Find("MainCamera").GetComponent("MouseLook").enabled = true;      
        }
    }
    
    
    function OnMouseDown () {
        Show();
    }
    
    
    // if I click the 3D object on the scene, the variable is true and...
     function Show() 
     {
      Dialog = true;	  
      windowOpen = true;
      windowRect = Rect (posX, posY, (aTexture.width/2+10), (aTexture.height/2+25));
      windowRectZoom = Rect (posX, posY, (aTexture.width+10), (aTexture.height+25));	
    }
    
    // ... set the custom skin, window rect and start WindowFunction
     function OnGUI () 
     {
      GUI.skin = myCustomSkin;
      if (windowOpen) 
       {
       	 GUI.color.a = 0.8;
         windowRect = GUI.Window (1, windowRect, WindowFunction, "My Window");
       }
      if (windowZoom) 
       {
       	 GUI.color.a = 0.8;	
         windowRectZoom = GUI.Window (2, windowRectZoom, WindowZoomFunction, "My Window Zoom");
       }
     }
    
    // create a X button for close the window and drag the window with texture
    function WindowFunction (windowID : int) {
      GUI.DrawTexture(Rect((posX-5),(posY+10),aTexture.width/2,aTexture.height/2), aTexture, ScaleMode.ScaleToFit, false, 0f);
      
      if (GUI.Button (Rect ((posX-4),22,22,20), "X")) {
      	Dialog = false;
        Hide();    
      }
      if (GUI.Button (Rect ((posX+20),22,25,20), "Z+")) {
      	ZoomIn();
      }
      
      GUI.DragWindow (Rect ((posX-5),(posY+15),aTexture.width/2,aTexture.height/2));
    }
    
    function WindowZoomFunction (windowID : int) {
    	Hide();
    
      	GUI.DrawTexture(Rect((posX-5),(posY+10),aTexture.width,aTexture.height), aTexture, ScaleMode.ScaleToFit, false, 0f);
      	
      	if (GUI.Button (Rect ((posX-4),22,22,20), "X")) {
      		Dialog = false;
        		HideZoom();    
      	}
      	
      	if (GUI.Button (Rect ((posX+20),22,25,20), "Z-")) {
        		back();    
      	}
      	
        GUI.DragWindow (Rect ((posX-5),(posY+15),aTexture.width,aTexture.height));
      }
    
    // hide function for close the window (boolean=false)
    function Hide() {
    	GameObject.Find("MainCamera").GetComponent("MouseSTOP").enabled = false;
    	windowOpen = false;	
    }
    
    // hide function for close the window (boolean=false)
    function HideZoom() {
    	GameObject.Find("MainCamera").GetComponent("MouseSTOP").enabled = false;
    	windowZoom = false;
    }
    
    function back() {
    	windowOpen = true;
    	windowZoom = false;
    }
    
    // hide function for close the window (boolean=false)
    function ZoomIn() {
    	windowOpen = false;
    	windowZoom = true;
    }
    
 }

when I click on an object it shows a GUI.window…but I can’t show more windows at same time because the IDs still remain the same for all objects…I can substitute the IDs with variables but I don’t know how to make them different every time an object was clicked…any suggestions?

Calling GUI.Window with explicit numbers like you’re doing here is a definite no-no, as I’m sure you’ve noticed - When multiple instances of this script are attached to different objects, each object can potentially open a window with an ID designated 1 or 2, and that seriously screws up Unity’s idea of which window is which. The problem here is that because Unity handles the rendering of Windows in a global way, the management of integer Window-IDs also needs to be global. This means you need some kind of construct that keeps track of how many windows are currently open at all times throughout your entire project.

I faced this challenge myself some time ago, and decided on a solution where all calls to GUI.Window was made in a single base class, from which all other windows inherit, and whose contents an inheriting class can override and specialize as needed. Here is the base class, gutted and shortened down for ease of reading, just to give you the general idea:

public abstract class WindowBase
{
    public Rect WindowRect { get; set; }
    protected string Title { get; set; }
    public int ID { get; set; }

    public WindowBase(Rect window, string title, int id)
    {
        this.WindowRect = window;
        this.ID = id;
        this.Title = title;
    }

    protected abstract void DrawWindowContents();

    public void OnGUI()
    {
        WindowRect = GUILayout.Window(ID, WindowRect, DrawWindowOutline, Title, GUILayout.Width(WindowRect.width), GUILayout.Height(WindowRect.height));
    }

    private void DrawWindowOutline(int id)
    {
        GUILayout.BeginVertical(GUI.skin.box, GUILayout.Height(WindowRect.height));
        DrawWindowContents();
        GUILayout.EndVertical();

        GUI.DragWindow();
    }
}

Then, you define new classes that inherit from this one, and in the child classes, you override the method DrawWindowContents. This ensures you can specialize the window contents of all childclasses and individualize them while retaining the one thing they have in common - namely their nature as Windows. This is the overall, general idea behind inheritance, by the way.

Then, in your GUI script, you declare a collection of windows like so:

public List<WindowBase> DialogWindows = new List<WindowBase>();

All child windows fit into that list, because they all inherit from WindowBase, so they’re all the same base type. Then, in your main OnGUI method, you run through the list and draw all current items:

    foreach (WindowBase dialog in DialogWindows)
    {
        dialog.OnGUI();
    }

When you want to open a window, you simply instantiate a new object of that particular window’s type, and here’s the trick: Pass in the list’s current .Count property as the window’s ID. So, let’s imagine that MessageBox is a class that inherits from WindowBase. Then, you’d open and display one like this:

        DialogWindows.Add(new MessageBox(new Rect(x, y, width, height), "Title", DialogWindows.Count));

There. Now all windows always have unique IDs, because the assigned ID depends on how many open windows are in existence already, based on the item count in the global list. There is of course the problem that new windows might get assigned IDs that are currently in use. Consider this: You add windows 0, 1 and 2. Then you close 1, so it needs to be removed from the list to stop it rendering. Now the list contains windows with IDs 0 and 2. Then you add another window, but it will get ID = 2 as well, because the .Count property is now 2 again. That’s no good, since ID = 2 is in use. Therefore, whenever you remove a window from the list, you need to update all the IDs of open windows to reflect the change. This method accomplishes that:

public void CloseWindow(int id)
{
    for (int i = DialogWindows.Count - 1; i >= 0; i--)
    {
        if (DialogWindows*.ID == id)*

{
DialogWindows.RemoveAt(i);
break;
}
else
DialogWindows*.ID–;*
}
}
Whew. Done. That’s pretty much the guts of my window system, actually… Hope it makes sense to you.

If you don't want to hassle around with ID management, the easiest way to assign a unique Id is to use a local int variable for each window and static variable as "manager".

public static class WindowIDManager
{
    private static int m_NextWindowID = 0;
    public static int GetWindowID()
    {
        return m_NextWindowID++;
    }
}

In every script where you need to use a window just create a local variable to hold the ID for your window and initialize it with `WindowIDManager.GetWindowID()`.

private int m_WinID;
void Awake()
{
    m_WinID = WindowIDManager.GetWindowID();
}
void OnGUI()
{
    GUI.Window(m_WinID, ....);
}

This method will not "free" unused IDs when a script instance get's deleted but a true ID management is far more compilcated. This "manager" (how can i call it a manager...) just increments it's local counter. It works almost like auto_increment for SQL databases ;)

As far as i can tell having unused ID's in between doesn't affect Unity in any way...

edit If you want to get rid of unused IDs you can simply hold them in a list:

public static class WindowIDManager
{
    private static int m_NextWindowID = 0;
    private static List<int> m_UnusedIDs = new List<int>();
    public static int GetWindowID()
    {
        if (m_UnusedIDs.Count > 0)
        {
            int ID = m_UnusedIDs[0];
            m_UnusedIDs.RemoveAt(0);
            return ID;
        }
        return m_NextWindowID++;
    }
    public static void FreeWindowID(int ID)
    {
        m_UnusedIDs.Add(ID);
    }
}

This little change would reassign old, unused IDs before it generate a new ID. You have to call FreeWindowID when you destroy your script / window in OnDisable.