Search Unity

eDriven Q&A

Discussion in 'Assets and Asset Store' started by dkozar, Aug 22, 2012.

  1. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    I'll definitelly alow the horiz scroll for containers in general, but for ComboBox or List control they are unnecessary.
     
  2. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48

    I tried the MouseEventDispatcher as you suggested since the new objects won't be exposed until 1.12 but as you can see from my previous post, the MouseEventDispatcher way isn't working. Either it is not in eDriven.Gui or if i use eDriven.Gui.Managers I can then access MouseEventDispatcher and MouseEventDispatcher.Instance but there is no MouseEventDispatcher.Instance.MouseTarget.

    Maybe I need to mention again that i am using the designer. When i try looking at eDriven.Gui.Managers.MouseEventDispatcher in an OnMouseEnter on a 3D object, i don't get a null but instead get a reference to Stage.
     
  3. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    I did find one way to test but it seems like a bit of a hack.

    Code (csharp):
    1.         // script attached to a 3D game object
    2.         using eDriven.Gui.Managers;
    3.         using UnityEngine;
    4.          
    5.         public class Demo : MonoBehaviour {
    6.             void OnMouseDown () {
    7.                 if (MouseEventDispatcher.MouseTarget != null)
    8.             if (MouseEventDispatcher.MouseTarget.ToString() != "eDriven.Gui.Containers.Stage")
    9.                 return;
    10.                  // Otherwise GUI not under the mouse. Do stuff with a game object.
    11.                 }
    12.             }
    13.            
    14.         }
    I'm having to specify "eDriven.Gui.Managers" instead of just "eDriven.Gui" and I can't refer to "MouseEventDispatcher.Instance.MouseTarget" but have to use "MouseEventDispatcher.MouseTarget". Again, this is with a GUI set up in the designer. Am I missing something or is this how it has to be done?
     
  4. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Sorry, it seems to be a static property then:

    Code (csharp):
    1. eDriven.Gui.Managers.MouseEventDispatcher.MouseTarget
    ps don't know what's going on, with me it works well. :)
     
  5. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Code (csharp):
    1. public class Demo : MonoBehaviour {
    2.     void OnMouseDown () {
    3.         if (MouseEventDispatcher.MouseTarget == null)
    4.             return;
    5.         if (MouseEventDispatcher.MouseTarget is eDriven.Gui.Containers.Stage)
    6.             // this works only if stage.MouseEnabled is true
    7.             print("Mouse over stage");     
    8.         }
    9.         else {
    10.             print("Mouse over GUI component: " + MouseEventDispatcher.MouseTarget);    
    11.         }
    12.     }
    13. }
     
  6. Hays

    Hays

    Joined:
    Mar 5, 2013
    Posts:
    19
    System.Xml just works fine, and System.Reflection helps a lot
     
  7. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    I'm using eDriven.Gui for a Unity WebPlayer app. Everything looks great in dev mode but when i try to run it in the web player (in Mac or Windows on Firefox, Safari and Chrome) it looks like everything is covered with a grey layer. It's still responsive to the mouse but it's very dim and hard to read. In the attached image you can see what it looks like when i "play" it in the Unity dev environment. On the bottom is how it looks once compiled and run in the web player. Any ideas?

    $eDrivenCrazy.jpg
     
  8. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Yeah, this happens when Unity Editor sporadically switches to "Fastest". This compromises the rendering quality and I didn't do any deeper research about the cause.

    Just go to Quality options and choose "Fast" for a WebPlayer.

    Cheers! :)

    $quality2.png
     
  9. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    Ah! Thank you! That did the trick.
     
  10. Hays

    Hays

    Joined:
    Mar 5, 2013
    Posts:
    19
    font mapper can not be persistent?

    and what's this suppose mean?
     
  11. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Oh, seeing this post just this moment. Sorry!

    Seems you passed a null style to your combo box, either by calling combo.SetStyle("yourStyle", null) or via the style mapper (I'm not sure how the second could happen though).

    PS Font mappers, sound mappers and all the mappers requiring to attach scripts or fonts are currently not persisted (when stopping the play mode). So you have to attach them in edit mode.
     
  12. unitymatrix

    unitymatrix

    Joined:
    Dec 29, 2009
    Posts:
    120
    NullReferenceException: Object reference not set to an instance of an object
    eDriven.Gui.Components.DisplayObject.Render ()
    eDriven.Gui.Components.DisplayObjectContainer.Render ()
    eDriven.Gui.Components.DisplayObject.Draw ()
    eDriven.Gui.Containers.Container.c09d2f86acda59cf28908c01b3d2f5ec5 (eDriven.Gui.Components.DisplayListMember )
    System.Collections.Generic.List`1[eDriven.Gui.Components.DisplayListMember].ForEach (System.Action`1 action) (at /Applications/buildAgent/work/3df08680c6f85295/mcs/class/corlib/System.Collections.Generic/List.cs:361)
    eDriven.Gui.Containers.Container.Render ()
    eDriven.Gui.Components.DisplayObject.Draw ()
    eDriven.Gui.StageManager.cec32b77c5afcee188a8dc5170a0f53b6 (eDriven.Gui.Containers.Stage )
    System.Collections.Generic.List`1[eDriven.Gui.Containers.Stage].ForEach (System.Action`1 action) (at /Applications/buildAgent/work/3df08680c6f85295/mcs/class/corlib/System.Collections.Generic/List.cs:361)
    eDriven.Gui.StageManager.Render ()
    eDriven.Gui.StageManager.cdb406e1c9ef9c39b28563a9e7bb7c724 (System.Object[] )
    eDriven.Core.Signals.Signal+c7820c161fd196f768022240ebda6bd48.c92dab2f1d9395d786386b01188a7723e (eDriven.Core.Signals.Slot )
    System.Collections.Generic.List`1[eDriven.Core.Signals.Slot].ForEach (System.Action`1 action) (at /Applications/buildAgent/work/3df08680c6f85295/mcs/class/corlib/System.Collections.Generic/List.cs:361)
    eDriven.Core.Signals.Signal.Emit (System.Object[] parameters)
    eDriven.Core.Managers.SystemManager.ProcessInput ()
    eDriven.Core.Mono.SystemManagerOnGuiInvoker.OnGUI ()
     
  13. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    I'm still fighting with this combobox. Just to cover my bases, in designer i created events on my combobox for the following:
    • selectedIndexChange
    • selectedIndexChanged
    • SelectedIndexChange
    • SelectedIndexChanged
    • open
    • opening
    • close
    • closing
    • click


    I sent them all to one function that just does a print of e.toString(). I then clicked the drop-down arrow which fired:
    • opening
    • open
    • click


    I then clicked on an item in the list which then fired:
    • closing
    • close
    • close


    Notice that "close" is in there twice and "selectedIndexChanged" or any variation of it is not. It never fires any kind of "selectedIndexChanged" event. Is something mapped incorrectly?
     
  14. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    I also receive this error after the combobox closes:

     
  15. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Hi! Finally got some time for answering this issue. :)

    I reproduced your test case and indeed, found the same bugs:

    1. selectedIndexChanged never fires, since I forget to dispatch an event
    2. close fires twice, because of the bug in the animation

    I just fixed those bugs them and the fix will be available with eDriven.Gui 1.12.

    However, until then you can rely on the "closing" event (since it fires properly), and then read the selected index from the combo:

    Code (csharp):
    1. public void ClosingHandler(Event e) // map it to "closing" event type
    2. {
    3.     //print(e); // prints event
    4.     ComboBox combo = (ComboBox) e.Target; // cast the event target to ComboBox
    5.     print("   SelectedIndex = " + combo.SelectedIndex);
    6. }
    Hope this helps!
     
  16. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    This seem snot to be related with combo box closing, rether with designer. However, I need more info. Thanks!
     
  17. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    Will the update be released soon? And will it resolve the issue with the first item in a list not being selectable?

    With the listbox it seems there are two problems. If you add items individually it only allows selecting the first item but the others are greyed out. You suggested assigning a list to the DataProvider. This seems to work but when there are too few items in the list to cause the scroll bars to be visible, you cannot select the FIRST item in the list. I am trying to use this in a Unity WebPlayer app for a client and need to resolve some of these issues soon.
     
  18. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Actually I'm rewriting the list and containers because of the new scrollbars (which aren't buggy as original Unity's scrollview). So pls don't expect the update this week still. Thanks!
     
  19. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    Ok. Looking forward to the list and container rewrite.

    Question- I've created a few TextFields and would like the player to be able to hit the TAB and ShiftTab keys to move between them. This does not seem to be a built-in funtion, is that correct?


    Issue- The TextFields don't have a limit on characters and when i type a long string, the text goes outside of the TextField and is displayed on top of the parent. It should clip at the borders automatically or have a "clip content" checkbox like the "Container". It would also be helpful if there was a character limit.
     
  20. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    There is a TabManager plugin. You should add it as a plugin of a parent container and setup components that should be involved in tabbing. Look at the ExampleControl source. You should override the GetTabChildren method and add this plugin to the parent.

    For designer components, you should attach the script to the container and set this up inside the ComponentCreated handler (see the manual for details).

    You should handle the clipping via the text field style you're using (see wordWrap and clipping).

    The character limit (and all the other checks) could easily be done via the event handler subscribing to "textChanging" event (TextFieldEvent.TEXT_CHANGING). If event canceled, the text will not be changed (and "textChange" won't fire). So check for length here (TextFieldEvent has the OldText and NewText properties).
     
  21. WhiteBatGames

    WhiteBatGames

    Joined:
    Apr 24, 2013
    Posts:
    1
    Hi,

    I'm currently evaluating eDriven.gui for a project and it looks good.

    Just wondering how I do a few things:

    All of my interface is created using separate images for each button and I was wondering what the best way of doing this is. Should I create a gui style for every button in the game from code with the image specified? that's a lot of styles! I have tried creating a subclass of button and overriding the Render call. Is this a suitable way to go?

    Also, is it possible to create a scrollview which can scroll itself even if the drag started inside one of its child elements?

    thanks a lot for your help.
     
  22. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Hi!

    You should use the Image class.

    It is the GUIContent-only class, meaning that it renders only the texture and no border.

    See the LoadImages demo (source here).

    Yes, eDriven is the stronges in that area: you can intercept any GUI event by listening for it in the capture phase and then cancel its propagation.

    Meaning: you can listen for "click" or "mouseMove" on scrollable container, and then (based on your decision) cancel it:

    Code (csharp):
    1. event.CancelAndStopPropagation();
    If you cancel the event at the parent level, it will never get to a child (the image control).

    The AddEventListener method of EventDispatcher has the overload that excepts the phases to listen to (the third parameter).

    By default the listener is added for target and bubbling phase, and if you want to listen in the capture phase, you need to add it here.

    If you are using the designer, you should check the first of the three toggle buttons available (as described in the manual).

    Cheers!
     
  23. ScrappyRMH

    ScrappyRMH

    Joined:
    Apr 11, 2011
    Posts:
    48
    I have a toolbar and have "checkbox" toggle buttons. Using the stylemapper for checkbox I can change the style but they always look like they are in the "up" state. No matter what image i put in the actual style that the mapper is pointing to, the "active" state only shows when the mouse is clicked and not when they should just stay "active". As a result, I can't tell which are toggled up or down. Thanks for your help.
     
  24. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    First try to get it work as a simple GUI.Toggle. Read this thread for the answer.
     
  25. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    Hi Danko,

    I was just wondering how we should handle it if we want multiple GUI hierarchies? Should it just be different scripts on the same gameobject heirarchy, or can we instantiate multiple GUI heirarchies / destroy GUI heirarchies at runtime?

    Are there any methods i need to call before disabling a component that inherits from the Gui monobehaviour?
     
    Last edited: May 18, 2013
  26. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Each hierarchy = a separate Gui script (or a separate Stage object in the designer).

    Gui scripts have nothing to do with hierarchies, you just have to stick it to any game object (should be enabled though).

    You don't need to instantiate anything in runtime, just make it hidden in the first place and unhide it later. Gui class has the reference to Stage, so you should hide it, but after the stage has been created (for that you might override OnInitialize handler of the Gui class):

    Code (csharp):
    1. public class MyGui : Gui
    2. {
    3.     protected override void OnInitialize()
    4.     {
    5.         Stage.Visible = false;
    6.     }
    7. }
    eDriven.Gui components do not extend MonoBehaviour, instead they extend EventDispatcher class.

    However, for being able to use them visually in designer, adapters are used.

    Gui class is a special kind of adapter for adapting GUIs created programatically. It adapts Stage, and has a reference to it.

    In designer mode, there are multiple adapters - one per component, so you could drag&drop components into the hierarchy (here is where the game object hierarchy meters, because it specifies a parent-child relationship of components).
     
  27. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    Okay, thanks for this detailed answer but unfortunately I'm confused about new things now (EDIT: resolved myself)

    So my understanding is that I should have all these scripts attached to GUI -> Scripts gameobject, and set the unneeded ones to have Stage.Visible = false in the initialization method. Good so far.

    Lets say I'm managing GUI control in a singleton monobehaviour called ClientGUI. I want to be able to say from other scripts something like ClientGUI.ShowSelectMenu(), ClientGUI.ShowLogin(), ClientGUI.ShowNewAcct(), etc.

    I've successfully used the Component Manager to get instances of text fields by ID, but it doesn't look like the Stage Manager has that functionality when I looked through the API. How do I lookup stages by ID programmatically in order to change their Visible property? How can I save a reference to those stages in my singleton monobehaviour managing classes?

    edit: for anyone wondering, it looks like stage can be found through Component Manager by ID
     
    Last edited: May 18, 2013
  28. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    I believe Stage could also have the ID given. Then, via the ComponentManager, you could get to Stage instance.

    But instead of looking them up from other classes, you could do this inside the Gui script, which hosts the stage. In your OnInitialize or even in OnCreationComplete handler you could register the reference to Stage to some central place in your application (your ClientGUI Singleton).

    Something like:

    Code (csharp):
    1. public class MyGui : Gui
    2. {
    3.     protected override void OnInitialize()
    4.     {
    5.         ClientGUI.Instance.RegisterStage(Stage);
    6.     }
    7. }
    Don't use the StageManager, because its an internal class used not for looking up stages, but for rendering them.
     
  29. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    For stuff like this, create your menu, login etc. as classes extending a simple Container (not Gui nor Stage).

    Then use the PopupManager.AddPopup for popping them up. PopupManager uses the special Stage, always available and rendered on top of everything else:

    Code (csharp):
    1. var popup = new MyClassExtendingContainer();
    2. PopupManager.Instance.AddPopup(popup); // adds the popup on top of all other popups
    3. PopupManager.Instance.CenterPopup(popup);// centers the popup
    4.  
    5. // later...
    6. PopupManager.Instance.RemovePopup(popup); // removes the popup
     
  30. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    just incase anyone is wondering how to do this, here is my solution for controlling different gui scripts from a singleton monobehaviour and works fantastically:

    Code (csharp):
    1.  
    2. // setting up a Gui component (one of many)
    3.  
    4. public class LoginGUI : Gui
    5. {
    6.     protected override void OnInitialize()
    7.     {
    8.         Stage.Visible = false;
    9.         Stage.Id = "login";
    10.     }
    11.  
    12.     protected override void CreateChildren() // blah blah
    13.  
    and then my monobehaviour

    Code (csharp):
    1.  
    2. public class ClientGUIManager : MonoBehaviour
    3. {
    4.     public bool debugEnabled;
    5.  
    6.     private Dictionary<string, Stage> stages;
    7.  
    8.     private static ClientGUIManager instance;
    9.     public static ClientGUIManager Instance
    10.     {
    11.         get
    12.         {
    13.             if (instance == null)
    14.             {
    15.                 instance = GameObject.FindGameObjectWithTag("Application").GetComponent<ClientGUIManager>();
    16.                 if (instance == null)
    17.                 {
    18.                     instance = GameObject.FindGameObjectWithTag("Application").AddComponent<ClientGUIManager>();
    19.                 }
    20.                 instance.Init();
    21.             }
    22.             return instance;
    23.         }
    24.     }
    25.  
    26.     private void Init()
    27.     {
    28.         stages = new Dictionary<string, Stage>();
    29.         if (debugEnabled) Debug.Log("ClientGUIManager : Initializing");
    30.         stages.Add("startMenu", ComponentManager.Instance.Get("startMenu") as Stage);
    31.         stages.Add("login", ComponentManager.Instance.Get("login") as Stage);
    32.         stages.Add("newAcct", ComponentManager.Instance.Get("newAcct") as Stage);
    33.     }
    34.  
    35.     public static void ShowStartMenu()
    36.     {
    37.         foreach(KeyValuePair<string, Stage> pair in Instance.stages)
    38.         {
    39.             if (pair.Key != "startMenu") pair.Value.Visible = false;            
    40.         }
    41.         Instance.stages["startMenu"].Visible = true;
    42.     }
    43.  
    44.     public static void ShowLogin()
    45.     {
    46.         foreach (KeyValuePair<string, Stage> pair in Instance.stages)
    47.         {
    48.             if (pair.Key != "login") pair.Value.Visible = false;
    49.         }
    50.         Instance.stages["login"].Visible = true;
    51.     }
    52.  
    53.     public static void ShowNewAcct()
    54.     {
    55.         foreach (KeyValuePair<string, Stage> pair in Instance.stages)
    56.         {
    57.             if (pair.Key != "newAcct") pair.Value.Visible = false;
    58.         }
    59.         Instance.stages["newAcct"].Visible = true;
    60.     }
    61. }
    62.  
    And finally, the incredibly simple implementation within my main application class!

    Code (csharp):
    1.  
    2.     IEnumerator StartMenu_EnterState()
    3.     {
    4.         if (debugEnabled) Debug.Log("Login Application : Start Menu");
    5.         ClientGUIManager.ShowStartMenu();
    6.         yield break;
    7.     }
    8.  
    9.     IEnumerator Login_EnterState()
    10.     {
    11.         if (debugEnabled) Debug.Log("Login Application : Login");
    12.         ClientGUIManager.ShowLogin();
    13.         yield break;
    14.     }
    15.  
    16.     IEnumerator CreateAcct_EnterState()
    17.     {
    18.         if (debugEnabled) Debug.Log("Login Application : Create New Account");
    19.         ClientGUIManager.ShowNewAcct();
    20.         yield break;
    21.     }
    22.  
    here is a working demo Webplayer
     
    Last edited: May 19, 2013
  31. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    I'm not used to this reverse way of thinking yet! Where the object registers itself rather than the application grabbing it. But I can see its power so I want to learn to use it.
     
  32. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Just realized that I didn't expose the Id property to Gui class at all. :)

    I did it right now - will be available in v1.12. ;)

    This ID would be passed to Stage upon the instantiation:

    $id.png
     
  33. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    1 more Q... any advice on how to set up tabbing between text fields?
     
  34. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Look at the FormDemo. You need to add a plugin to a container, and implement GetTabChildren() method on it, so it returns the children you want to get tabbed (in exact order).

    As for hiding the Stages, you could easily tween components to/from screen them instead of hiding the parent stage. Look for the TweenFactory usage across demos.
     
  35. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Btw Form component has already implemented the GetTabChildren method internally:

    Code (csharp):
    1.  
    2. // Form internals
    3. // Copyright (c) Danko Kozar 2010-2013
    4. override public List<DisplayListMember> GetTabChildren()
    5. {
    6.     List<DisplayListMember> list = new List<DisplayListMember>();
    7.     _fieldGroup.Children.ForEach(delegate(DisplayListMember child) // form items
    8.     {
    9.         Container container = child as Container; // form item
    10.         if (null != container)
    11.         {
    12.             list.AddRange(
    13.                 container.ContentChildren.FindAll(delegate(DisplayListMember child2)
    14.                 {
    15.                     InteractiveComponent c = child2 as InteractiveComponent;
    16.                     return null != c  c.Enabled  c.Visible  c.FocusEnabled;
    17.                 })
    18.             );
    19.         }
    20.     });
    21.     return list;
    22. }
    Additionaly, there are 2 properties that need to be tweeked by developer:

    Code (csharp):
    1. CircularTabs = true; // true for looping with tabs
    2. CircularArrows = false; // true for looping with arrow keys
    So, what's left is to add the TabManager plugin to your component:

    Code (csharp):
    1. override protected void CreateChildren()
    2. {
    3.     base.CreateChildren();
    4.     Plugins.Add(new TabManager());
    5. }
     
  36. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    1 more quick q, how did you get the webplayer to automatically go fullscreen like on www.edrivenunity.com??
     
  37. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
  38. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    Do you know if this is a Pro Only thing to do? I noticed here on this page http://docs.unity3d.com/Documentation/Manual/WorkingwithUnityObject.html that some of the values you set are Pro Only (like setting the images).

    I was able to download the template and build it, but my browser only shows it with the default resolution that I put in the player settings (just like the default templates). When I edited out the Pro Only parameters in the html template I just got a "Install Unity Webplayer" button when I play the build.
     
    Last edited: May 23, 2013
  39. eventropy

    eventropy

    Joined:
    Oct 4, 2012
    Posts:
    254
    Has there been any thought towards supporting runtime assembly reloads? That is, if I start my game in the editor, change any script file, then go back to unity, unity will recompile the project, serialize out the current state of the game, destroy the game in memory, recreate from scratch, then try and serialize the state back in (Unity had a nice blog post talking about this here: http://blogs.unity3d.com/2012/10/25/unity-serialization/). This works for the parts of my project that I've written, but the eDriven Gui appears to break (stops rendering)
     
  40. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    It's not a Pro thing. It should work with a free version of Unity.
     
  41. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    I see. The thing is I didn't have time to do it yet, but I'm totally aware of it. It happens because eDriven heavily relies on the moment the app is being started (various lookups and processing), but I agree it would be a nice to have feature.
     
  42. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Regarding the Tim Cooper's blog post link, of course I've read it (many times) because I used this stuff throughout the editor. :)

    So it's not that I don't kow about Unity serilization, but eDriven is much more complex that one could imagine. ;)
     
  43. eventropy

    eventropy

    Joined:
    Oct 4, 2012
    Posts:
    254
    Yes, I know there's some work involved in maintaining the ability to do assembly reloads, it would be an amazing feature though. Happy to hear that you plan to add support for this at some point :)
     
  44. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    With v1.12 I'm already processing the assembly reload 'event', but for another purpose. More on this when 1.12 is out. ;)
     
  45. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    another Q for you danko,

    How do I setup a component to monitor for changes outside of the component itself? For example, creating an Inventory container that inherits from Panel or something like that.

    Right now I'm doing something like this:

    Code (csharp):
    1. public class Inventory : Panel
    2. {
    3.     private GameObject localPlayer;
    4.     private RPGPlayerCharacter localPlayerData;
    5.  
    6.     public Inventory(GameObject player) : base()
    7.     {
    8.         localPlayer = player;
    9.         localPlayerData = player.GetComponent<RPGPlayerCharacter>();
    10.     }
    11.  
    12.     protected override void CreateChildren()
    13.     {
    14.         base.CreateChildren();
    15.  
    16.         Label label = new Label()
    17.         {
    18.             Text = "" + localPlayerData.PlayerData.Id + ", " + localPlayerData.Inventory.Items.Count + " items"
    19.         };
    20.         AddChild(label);
    21.     }
    22.  
    And then when I create children I can use the local player data to put in labels and things.

    But when my inventory updates, I need to rebuild the children. Whats the best way to force the children components to be recreated? I looked through the manual but the things I found there like Measure() and those related methods you mentioned are for calling internally in the framework.

    Is there a recommended method to rebuild children from the outside?
     
    Last edited: May 25, 2013
  46. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    My suggestion would be that a GUI component really shouldn't know anything about the outside world. The purpose of a GUI is just the interaction with a user.

    You should have create other (manager) component that takes care of scanning the player object and then pushes this change to GUI.

    The interaction with GUI should be implemented using the "properties in - events out" principle.

    So, you should create properties like PlayerId and ItemsCount.

    Those properties should be the setters, but using the invalidation (deferred applying of the change to a child Label):

    Code (csharp):
    1. private bool _playerIdChanged;
    2. private string _playerId;
    3. public string PlayerId
    4. {
    5.     get {
    6.         return _playerId;
    7.     }
    8.     set
    9.     {
    10.         if (value == _playerId)
    11.             return;
    12.  
    13.         _playerId = value;
    14.         _playerIdChanged = true;
    15.         InvalidateProperties();
    16.     }
    17. }
    Code (csharp):
    1. protected override void CommitProperties()
    2. {
    3.     base.CommitProperties();
    4.  
    5.     if (_playerIdChanged || _itemsCountChanged)
    6.     {
    7.         _playerIdChanged = false;
    8.         _itemsCountChanged = false;
    9.         _label.Text = _playerId + " " + _itemsCount; // plus your other stuff...
    10.     }
    11. }
    This in turn applies using the member variable for the created label, so it could be referenced from any of component's methods:

    Code (csharp):
    1. private Label _label;
    2.  
    3. protected override void CreateChildren()
    4. {
    5.     base.CreateChildren();
    6.  
    7.     _label = new Label()
    8.     {
    9.         Text = "Some default text"
    10.     };
    11.     AddChild(label);
    12. }
    This is as cleacest as it could be, but you can of course do as you did - push the LocalPlayer and LocalPlayerData into GUI, but these must be the invalidating properties.

    The invalidation is so common with eDriven that I made some ReSharper live templates which give me complete snippets with a few keystrokes:

    $invalidating.png

    $invalidating2.png

    I would of course be pleased to share my ReSharper live templates with anyone using ReSharper. :)
     
  47. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Note that the invalidation "delays" the usage of children properties until the moment the children are created.

    Important:

    1. All the children should be created within the CreateChildren method.
    2. All the changes to children should be applied withing the CommitProperties method (when CommitProperties is being run, eDriven has already instantiated all the children).

    Having this in mind, consider this: assuming you made a child label public, you must not do the following:

    Code (csharp):
    1. Panel p = new Panel();
    2. p.ChildLabel.Text = "Some text"; // null reference!
    This code will fail because after the constructor is run (1st line) the label hasn't been created yet, so by the configuration time (2nd line) you'll get a null reference!

    So, you should always reference your child components from within CommitProperties, ChildrenCreated and similar method (if within the component code).

    If outside of the component, you should subscribe to the CREATION_COMPLETE event of the component, and only then reference its children.
     
    Last edited: May 25, 2013
  48. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    As for the completely rebuilding the children, you should do it from CommitProperties (only the initial creation should be done within a CreateChildren method):

    Code (csharp):
    1. private bool _listOfTitlesChanged;
    2. private List<string> _listOfTitles;
    3. public List<string> ListOfTitles
    4. {
    5.     get {
    6.         return _listOfTitles;
    7.     }
    8.     set
    9.     {
    10.         if (value == _listOfTitles)
    11.             return;
    12.  
    13.         _listOfTitles = value;
    14.         _listOfTitlesChanged = true;
    15.         InvalidateProperties();
    16.     }
    17. }
    Code (csharp):
    1. protected override void CommitProperties()
    2. {
    3.     base.CommitProperties();
    4.  
    5.     if (_listOfTitlesChanged)
    6.     {
    7.         _listOfTitlesChanged = false;
    8.  
    9.         RemoveAllChildren(); // removing all the children
    10.         foreach (string title in _listOfTitles)
    11.         {
    12.             Label label = new Label {Text = title};
    13.             AddChild(label);
    14.         }
    15.     }
    16. }
    ps. Check out the AddChildDemo.
     
  49. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    When recreating a large amount of children (like in DataGrid and similar components), there is an advanced concept called "Object pooling".

    Rather then destroying all the children and recreating them using the "new" operator (thus allocating memory etc.) you can use the ObjectPool class provided by eDriven.

    Code (csharp):
    1. private readonly ObjectPool<Label> _labelPool = new ObjectPool<Label>();
    2.  
    3. protected override void CommitProperties()
    4. {
    5.     base.CommitProperties();
    6.  
    7.     if (_listOfTitlesChanged)
    8.     {
    9.         _listOfTitlesChanged = false;
    10.  
    11.         // 1. remove children
    12.         for (int i = Children.Count - 1; i >= 0; i--)
    13.         {
    14.             var label = (Label) Children[i];
    15.             _labelPool.Put(label); // return to a pool
    16.             RemoveChild(label);
    17.         }
    18.  
    19.         // 2. recreate children
    20.         foreach (string title in _listOfTitles)
    21.         {
    22.             Label label = _labelPool.Get(); // do not create new, use the pool instead
    23.             label.Text = title;
    24.             AddChild(label);
    25.         }
    26.     }
    27. }
    Object pool drastically reduces a performance hit (it creates a new instance of the object internally, but only if there are no objects on the stack already created).
     
  50. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    I just solved the assembly reload problem - and it was not hard as I expected. :)

    And it works fine with both programmatic (Gui) and designer approach (component adapters) :)

    Now I can - while in play mode - modify a Gui script (add children etc.). Then, when back in the editor, the editor blinks for a moment (because of the assembly reload) and rebuilds the GUI (with changes present). It stays in Play mode - no errors, no warnings. :)

    Same with a designer approach. The only thing I still didn't handle is to accumulate changed values to survive multiple assembly reloads (eDriven designer currently counts on the application being stopped, so he could reapply the values when that happens). However, when stopping the Play mode manually, the values are being persisted.

    The warnings I had before are not related to this issue, but I falsely thought they are.

    The main part of the solution was moving the stage instantiation code from Start method (which runs only on initial Play mode start) to OnEnable (which runs initially and after each assembly reload).

    Interesting stuff... Gives the whole system a more robust feeling. :cool: