• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by lcn75 · Nov 18, 2011 at 09:00 AM · idgui.window

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?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
2

Answer by CHPedersen · Nov 18, 2011 at 10:59 AM

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[i].ID == id)
         {
             DialogWindows.RemoveAt(i);
             break;
         }
         else
             DialogWindows[i].ID--;
     }
 }

Whew. Done. That's pretty much the guts of my window system, actually... Hope it makes sense to you.

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image syclamoth · Nov 18, 2011 at 11:17 AM 0
Share

I never knew the windows used global ids! You learn a thing a day.

avatar image lcn75 · Nov 18, 2011 at 01:27 PM 0
Share

Thanks Christian!!

avatar image
0

Answer by Bunny83 · Nov 18, 2011 at 12:12 PM

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.

Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image CHPedersen · Nov 18, 2011 at 12:26 PM 0
Share

It's true, this is a simpler alternative to a proper window manager. :P I'd go with this if you know you're going to only have few windows, because it's more lightweight. But if you need many windows, this can quickly become cluttered, and an object-oriented solution would be preferable.

avatar image Bunny83 · Nov 18, 2011 at 01:19 PM 0
Share

@Christian-H-Pedersen: Well, it's still OOP. You have instances of a script which implement it's own window. I don't want to offence you in any way. I also used a more OOP way for my nodebased editor. Your solution needs a central point of managing your different windows (your DialogWindows list) which seems to by a member of one single script. If you have multiple scripts the manager has to be at least a singleton so every window can "register" / unregister itself.

Another thing is that it seems you change the window IDs of all windows when you close one. Windows ID shouldn't change during the lifetime of a window. The point of an identification number is to identificate something ;). Unity saves a lot things along with the ID (focus, z-order, events). If you change the IDs on the fly you scramble (or shift) this information between your windows.

Like all questions there's not just one right answer. It depends on how you implement your windows. You used your own event dispatch system. In "normal" scripts Unity dispaches the events (by calling OnGUI). The Component-strategy pattern is very useful, but you have to change some program$$anonymous$$g habits to smoothly integrate your stuff into it.

avatar image Bunny83 · Nov 18, 2011 at 01:21 PM 0
Share

Oh, btw in my node based editor every element that is drawn is derived from my NB_Base class which allocates an ID in awake. I have 200+ windows there ;)

avatar image lcn75 · Nov 18, 2011 at 01:28 PM 0
Share

Thanks guys!!...I'll try it!

avatar image CHPedersen · Nov 18, 2011 at 02:05 PM 1
Share

No worries, no offense taken. :) You're right I am saving the list in a single script. I have an empty gameobject onto which I've added a GUIScript that handles all the GUI drawing in the application. As the GUI grows, I find it easier to manage it if all GUI drawing has a common "starting point", namely the single OnGUI in that script. The list, naturally, is a member of that script.

I disagree about changing window IDs being a bad thing. I agree that IDs should be the same during the lifetime of a window, but Unity's GUI system isn't object oriented. Unlike the retained mode control system some might've experienced with WinForms and WPF, Unity's GUI is totally immediate mode - everything's drawn from scratch every time, and not just once per frame, but on every single GUI-event. If you draw a new window with a static function once every OnGUI anyway, the "life-time of a window" becomes a rather limited concept. Since they don't really exist as an object in any sense in Unity, I'm not sure I agree they have a lifetime at all. The Window class I've created can be thought of as an encapsulation around Unity's GUI to get back to something that resembles retained mode. Those objects do have a lifetime, indeed; they're "alive" for as long as the object is a member of the list from which they are drawn. The ID they're storing is used only to get it back from the list that contains them, so it shouldn't matter that it changes; I'm not aware that it has any meaning outside the class I've defined?

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

5 People are following this question.

avatar image avatar image avatar image avatar image avatar image

Related Questions

Texture 2D array to GUI.Window rows iOS 0 Answers

How can the "View ID AllocatedID: X not found during lookup. Strange behaviour may occur" error be ignored ? 4 Answers

how to give id to tag in xml 1 Answer

How do I check to see if animation layer is playing with hash id on Unity3d 0 Answers

GUI.DragWindow() doesnt work - cant figure out why 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges