Search Unity

[RELEASED] Data Bind for Unity

Discussion in 'Assets and Asset Store' started by coeing, Feb 16, 2015.

  1. SidarVasco

    SidarVasco

    Joined:
    Feb 9, 2015
    Posts:
    163
    Alright, good to know.

    Thanks!
     
  2. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    @coeing

    I have purchased your asset, but I don't think it does what I was looking for.

    I have a GameObject in a scene. That GameObject has a script as a component. That script is a MonoBehavior with a field named Score that is an int.

    I have an asset that is a scoreboard with a property named text that is what is displayed on the scoreboard.

    So, when Score changes, I want the scoreboard to update.

    How would I do that with this asset? From the examples I looked at, and the docs I read, there is nothing on how to do this. All there is info on is creating some additional asset that is a context.

    My GameObject with the script is a network instantiated prefab. The score field is a SyncVar. I want to keep it that way so the score is properly handled on all the clients. I have no idea how to use your asset to do what I want.

    I looked at your LabelSetter example, but you don't use a standard click event. You use some custom event. I don't want to have to change the behavior of all my buttons. You also only set the context property on initialization.

    As you can see, I am all confused. Can you help me out here?
     
  3. SidarVasco

    SidarVasco

    Joined:
    Feb 9, 2015
    Posts:
    163
    Ill agree that it was confusing for me too first.

    Ill explain to you how to do a simple 1 score update on a text field:

    Create your context class first ( the one with all the properties).
    In your component script create an instance of this context.

    Code (csharp):
    1.  
    2. public class ScoreContext : Context{
    3.      readonly Property<int> playerScoreProperty = new Property<int>();
    4.  
    5.       public int PlayerScore{
    6.            get{return playerScoreProperty.Value;}
    7.            set{playerScoreProperty.Value = value;}
    8.       }
    9. }
    10.  
    11. public class MyScoreComponent : MonoBehavior{
    12.  
    13.        ScoreContext  scoreContext = new ScoreContext();
    14.        public int Score;
    15.  
    16.        public function AddScore(){
    17.                    Score  += 1;
    18.  
    19.                    //Auto updates your ui
    20.                    scoreContext .PlayerScore = Score;
    21.        }
    22.  
    23. }
    24.  
    When your score changes in your component ALSO change the property in your context.

    ------------------
    //To display your score

    On the root of your UI object add component Context Holder. Set the context to the context class you created ( Data bind will automatically find your class and adds it to the drop down list )

    On the text object add a [db] Text Setter
    In this component set the path to one of the properties ( they should automatically show if your text object is a child of the gameobject containing the Context holder ).

    Now back to your component that changes score :
    You have to reference the Context holder somehow and then set the context property on it to the instance of your context class you instantiated in the score component.

    Code (csharp):
    1.  
    2.     void Start(){
    3. // If you don't do this your ui won't be updated.
    4.        MyObjectContextHolder.GetComponent<ContextHolder>().context = scoreContext;
    5.     }
    6.  
    If all goes well your ui will update.

    If your contexts contain public functions you can map those to a button command ( add those to your gameobject with a button component )

    ----------------------------------------------------------------------------------------------
    ----------------------------------------------------------------------------------------------
    ----------------------------------------------------------------------------------------------


    Extra Displaying leaderboards:

    Be sure to read the manual on collection context first!

    For this Im using the collection context. It takes two Context classes. One is the context of the item we are showing ( containing score and name, profile pic ). The other handles the Group Layout list.

    This handles the list.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using Slash.Unity.DataBind.Core.Data;
    5.  
    6. public class HighScorecollection : Context {
    7.     readonly Collection<HighScoreItemContext> items =new Collection<HighScoreItemContext>();
    8.  
    9.     public Collection<HighScoreItemContext> Items
    10.     {
    11.         get
    12.         {
    13.             return this.items;
    14.         }
    15.     }
    16.  
    17.     public void Clear()
    18.     {
    19.         items.Clear();
    20.     }
    21.  
    22.     public void AddItem(string name, int score, int rank)
    23.     {
    24.         items.Add(new HighScoreItemContext { Rank = rank, Score = score, Name = name});
    25.     }
    26.  
    27.     //Called by async profile downloader
    28.     public void UpdateImage(int index, Texture t)
    29.     {
    30.         items[index].Profilepic = t;
    31.     }
    32.  
    33. }
    34.  
    35.  

    This is the context for the item displaying score data:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using Slash.Unity.DataBind.Core.Data;
    5.  
    6. public class HighScoreItemContext : Context
    7. {
    8.  
    9.     readonly Property<int> scoreProperty = new Property<int>();
    10.     readonly Property<int> rankProperty = new Property<int>();
    11.     readonly Property<string> nameProperty = new Property<string>();
    12.     readonly Property<Texture> profilepicProperty = new Property<Texture>();
    13.  
    14.     public int Score{
    15.         get { return scoreProperty.Value; }
    16.         set { scoreProperty.Value = value; }
    17.     }
    18.  
    19.     public int Rank
    20.     {
    21.         get { return rankProperty.Value; }
    22.         set { rankProperty.Value = value; }
    23.     }
    24.  
    25.     public string Name
    26.     {
    27.         get { return nameProperty.Value; }
    28.         set { nameProperty.Value = value; }
    29.     }
    30.  
    31.     public Texture Profilepic
    32.     {
    33.         get { return profilepicProperty.Value; }
    34.         set { profilepicProperty.Value = value; }
    35.     }
    36. }
    37.  
    38.  
    39.  

    The manual explains how to do a setup, how to setup a prefab as well.
    Im using Cross Platform Native Plugins ( asset store ) for my google/ios highscores and this is what my function looks like when it's done authenticating and loading the leaderboards:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using Slash.Unity.DataBind.Core.Presentation;
    5. using VoxelBusters.NativePlugins;
    6. using System;
    7.  
    8. public class HighScoreHandler : MonoBehaviour {
    9.  
    10.     public ContextHolder Context;
    11.     HighScorecollection hsc = new HighScorecollection();
    12.     Leaderboard HighScore;
    13.  
    14.  
    15.     // Use this for initialization
    16.     void Start () {
    17.  
    18.         NPBinding.GameServices.LocalUser.Authenticate(OnAuthencity);
    19.         Context.Context = hsc;
    20.  
    21.  
    22.     }
    23.  
    24.     private void OnAuthencity(bool _success, string _error)
    25.     {
    26.  
    27.         if (_success)
    28.         {
    29.  
    30.             HighScore = NPBinding.GameServices.CreateLeaderboardWithGlobalID("Mileage");
    31.             HighScore.MaxResults = 10;
    32.             HighScore.UserScope = eLeaderboardUserScope.GLOBAL;
    33.  
    34.             HighScore.LoadTopScores(OnLoadDone);
    35.  
    36.         }
    37.     }
    38.  
    39.     private void OnLoadDone(Score[] _scores, Score _localUserScore, string _error)
    40.     {
    41.         if (_scores != null)
    42.         for (int i = 0; i < _scores.Length; i++)
    43.         {
    44.            ///Call on my collection context,
    45.           // publicvoidAddItem(stringname, int score, intrank)
    46.             hsc.AddItem(_scores[i].User.Name, (int)_scores[i].Value, _scores[i].Rank);
    47.  
    48.           //Async update image
    49.            //adding lambda function to keep track of which profile pic is done downloading and changes the image property for the current item
    50.             _scores[i].User.GetImageAsync((y, x) => {
    51.                 int index = i;
    52.                 hsc.UpdateImage(index, y);
    53.             });
    54.  
    55.         }
    56.     }
    57. }
    58.  
    59.  

    It's also good to note that the property names have a naming convention. Make sure you got these right otherwise your ui won't update.

    Edit 2:

    Man I just realized there is a code snippet for VS to create properties with correct names. Doesn't seem to work properly in VSCommunity 2015 =[
     
    Last edited: Nov 25, 2015
  4. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I want to thank you for your helpful reply I can tell it took some time. However, as you say, I would need to make changes in two places. That is what I was doing before, making this no better. I want to change it in one place and have it automatically update the display.

    I too was bummed when it didn't work for me. I thought I did something wrong.
     
  5. SidarVasco

    SidarVasco

    Joined:
    Feb 9, 2015
    Posts:
    163
    The difference is that once you've setup your context and their respective data, all you need to do is link the context to a context holder ( root of your gui object ). As long the context is in place you can use it on any Context Holder. This makes it flexible. Even if you're not using a gui your object will still work.

    In my project I was capable of showing player stats in different gui elements. Saved me a ton of time from writing custom classes for each aspect and to drag and drop all references around.

    If I need to add a new GUI screen I can just setup the paths for each element and plugin my context and it works.
    Don't forget there are also formatters, so you can completely change how the output will look.

    Easiest way of updating your data in "one place" is by using properties(getters/setters)

    Code (csharp):
    1.  
    2. public int Score{
    3.     get{return score;}
    4.     set{score = scoreContext.Score = value;}
    5. }
    6.  
    Either way you are bound to notify your gui somehow so you will be writing data to two different spots anyways. Data Bind just abstracts a large portion of it for you.

    edit:

    Reading your statement again, I think you might have the wrong idea ? You just have the context instance in your component (note that Context is not of Monobehavior ). So technically you still change data in one place.

    The Properties in your context make the magic happen once you change them.
     
    Last edited: Nov 25, 2015
  6. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Hey @Tinjaw!

    I'm very sorry for the late reply, just saw that you posted in the thread, didn't get a mail :/

    Thanks a lot @SidarVasco for your quick and elaborate explanations!

    The way we used Data Bind in our projects was as a layer between your game logic and the presentation side, ie. the things you instantiate and show in Unity.

    In your game logic you may have some data that may go into a scoreboard, e.g. a collection of scores and the players that achieved those.

    On the other side you have a screen where the scoreboard is visualized. At the point where the user enters the screen (e.g. via a button click) you open the window, create the ScoreboardContext from the data in your game logic and put the context on the ContextHolder of the root game object of the window. This way you'll have the data available in the whole UI window. If you change the context after you opened the screen, the changes will automatically be adopted to the UI.

    Of course you don't have to recreate the context each time, you can just store it for the next time the user opens the scoreboard.

    Hope this helps with your problem! It might seem like a bit of overhead with the extra layer, but this gives a clear separation and makes sure that your logic and UI/presentation are not too dependent on each other.

    Cheers
    Christian
     
  7. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I believe I am still confused.

    If I have a Game Object that has a field and I want that to be linked to the UI, what I need to do is create a context and manually sync the field and the context, and that will update the UI.

    It sounds to me like the only think that can sync to UI elements are basically global variables. If I am using OOP with member fields, I don't see any benefit to using your code; obviously only if am understanding things correctly.

    If I am understanding things correctly your asset is meant to be used for something like a score for the whole game, and not scores for individual players. Is that correct?

    Thanks for the help.
     
  8. SidarVasco

    SidarVasco

    Joined:
    Feb 9, 2015
    Posts:
    163
    Again, the difference is that you can setup different UI setups without touching any code that needs to reference each element. And they aren't global variables, Contexts are objects you instantiate and how you reference each Context/Contextholder is up to you.

    No it's nothing like that. It's to separate your code from your ui. It's simply to detach your code from UI elements.
    Once you've setup your context you can use them in ANY UI setup. Each object can have its own context instance.

    Let's say you have some player stats you show in your player hud. But now we hit pause and a completely different gui pops up. In this other gui I want to show the same stats but with a different layout/graphics. All I have to do is setup the root of my elements with a ContextHolder pass in my Context and set paths for each element. The Context on my player will continue to update and any element matching a path will update too.
     
    coeing likes this.
  9. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    If I get your descriptions right, your game logic is implemented with mono behaviours, is that correct? You can't link a property of a context automatically to a field/property of a mono behaviour.
    But maybe you don't need the field/property of the mono behaviour anyway. You could work on the context directly and use them as data structures in your game logic.

    It would be cleaner to have some events from your logic to indicate that something changed and update the contexts in the handlers of those events. So your logic would be independent of the contexts. But to start with you can try to work on the contexts directly.
     
  10. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    That's the crux of my issue. You answered my question.

    Yes, I am using MonoBehaviours.

    If you would indulge me...

    In the attached project, there is a player GameObject with a hitPoints field. There is a plus button and a minus button to increment or decrement the hitPoints. There is a text object to display the hitPoints. For demonstration purposes, there is a line in the Update method of the player to display the hitPoints.

    Please modify (as simply as possible) the project to use this asset to keep the score updated when the current Update function is removed.

    If I am understanding things correctly, you will create a context. And you will have to add code to the Player MonoBehaviour to update the context as the hitPoints change. That context will keep the display current.

    If what I said is correct, I might as well just use the code you will be writing to update the context and just have it update the display.

    (And to @SidarVasco , I understand that with a context I can have ten displays and the context will update all ten with no added effort.)
     

    Attached Files:

  11. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    @Tinjaw: I hope to find some time tomorrow to look into your sample project, thanks for putting it up! :)

    In the mean time: Versoin 1.0.5 was just accepted in the Asset Store!

    https://www.assetstore.unity3d.com/en/#!/content/28301

    It contains a lot of additional scripts that we used in our current projects already, so they have proven to be useful :)

    As always, feel free to give me feedback and suggestions for further additions and bugs, so I can put them high on the priority list for the next version.

    Here is the complete changelog:

    • ADDED: Editor - Showing context data in inspector during runtime.
    • ADDED: Editor - Added object field in inspector for data binding if type is Reference.
    • ADDED: Operations - Added tween operation to change a number value over time.
    • ADDED: Objects - Added simple number object, mainly for testing.
    • ADDED: Operations - Added module operation to ArithmeticOperation.
    • ADDED: Setters - Added setter for the interactable flag of a CanvasGroup.
    • ADDED: Commands - Added base commands for UnityEvents with parameters and multiple target callbacks.
    • CHANGED: Setters - Moved ImageMaterialSetter from foundation to UI/Unity.
    • ADDED: Setters - Added possibility to hide empty slots and to shift items on remove to SlotItemsSetter.
    • ADDED: Setters - Added setter to set the context of a specified context holder.
    • ADDED: Setters - Added setter to enable/disable a behaviour.
    • ADDED: Operations - Added mapping from string to game object.
    • ADDED: Getters - Added provider for transform.position.
    • CHANGED: Getters - ComponentSingleGetter overrides OnEnable/OnDisable and calls base method of DataProvider.
    • ADDED: Formatters - Added StringToUpperFormatter.
    • ADDED: Commands - Catching exception when invoking command to log more helpful message.
    • ADDED: Setters - Added setter for the sprite of a sprite renderer.
    • ADDED: Setters - Added setter for the material of an image.
    • ADDED: Setters - Added setter for a trigger parameter in an animator.
    • ADDED: Providers - Added lookup for data dictionary.
    • ADDED: Core - Added DataDictionary to have a simple data mapping in contexts.
    • ADDED: Context - Added context node which points to an item in an enumerable.
    • ADDED: Core - Added constructor for Collection to initialize from IEnumerable.
    • CHANGED: Core - Data provider doesn't listen to value changes when not active.
    • CHANGED: Setters - SingleSetter catches cast exception to provide more helpful log message.
    • ADDED: Core - Added DataBindingType "Reference" to reference a Unity object. Catching cast exception when getting value of data binding. IsInitialized value is set before setting value to be set already on callbacks.
    • CHANGED: Formatters - Added bindings for symbols of the DurationFormatter, so it is more generic and can be localized.
    • ADDED: Setters - Added items setter for fixed number of provided slots.
    • ADDED: Commands - Adding default values for missing parameters when invoking command.
    • ADDED: Switches - Added NumberRangeSwitch and base class for range switches.
    • ADDED: Providers - Added ColorObject to provide a single color value.
    • ADDED: Converters - Added Texture2DToSpriteConverter and base DataConverter.
    • ADDED: Context Holder Initializer - Added Reset method to automatically search for context holder on same game object on creation/reset.
    • ADDED: Providers - Added BooleanSwitch to provide a different data value depending on a boolean value.
    • FIXED: Reflection - Using platform-independent implementations of IsEnum and BaseType.
    • ADDED: Core - Added ContextChanged event to ContextHolder.
     
  12. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    @Tinjaw: Here you go, I added some basic functionality how you could connect your game objects to data bind contexts and how they are linked to the UI.

    Some notes:
    • The PlayerContext is initialized with your Player object and observes it for data changes
    • The Player object got an event when its hit points changed
    • The InitGame script creates the contexts that are used in the game and initializes them with the objects they should observe
    • The InitGame script also puts those contexts in the ContextHolders of the UI
    The contexts are plain C# classes, so you are free to create, initialize and store them wherever it suits you. If your project grows it makes sense to not have one big Init script, but have your features separated as much as possible. This takes some experience, so just try some things and use what works for you :)
     

    Attached Files:

  13. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    @coeing: Thank you so very much. That example was exactly what I needed.

    I am a self-taught programmer that is trying to learn how to do things as they are done in larger formal programs. One of the things I am trying to do now is adopt TTD and thus unit testing. Hence the desire to move to a more MVC-/MVP-/MVVM-ish architecture than what I usually use as a one-man shop.

    This morning I did some reading on MVVM after looking at the example you just made. Now I am going to relook at the included examples and take a stab at your blog posts.
     
  14. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    You're welcome :) If you are trying to do test driven development with Unity, I would strongly suggest to move your whole game logic into pure C# instead of using MonoBehaviours for it. Even Unity suggests this due to better testability: http://blogs.unity3d.com/2014/06/03/unit-testing-part-2-unit-testing-monobehaviours/
     
  15. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I am wondering if Contexts can be integrated with UNet, or ForgeNetworking, or something similar, to provide synchronizing of a Context across the wire. I tried making Context derive from UnityEngine.Networking.NetworkBehaviour, but it throws an error when the Context is instantiated. Any suggestions how this might be done?
     
    Hodgson_SDAS likes this.
  16. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Hi @Tinjaw

    I haven't tried synchronizing contexts over the network. But I would try to capsule the context to synchronize in another class instead of deriving it from NetworkBehaviour. The Context class itself isn't a mono behaviour, just a pure C# class, so it can easily passed around and modified. How do you want to do the synchronization? Do you have something generic in mind or just a solution for a specific use case?
     
  17. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I want to have the server instantiate a game board with a context that contains the data about the state of the game board. I would use Data Bind Setters to display the state of the game board -- things like set the color of a button, or the state of a toggle switch on the game board. I would want the contexts on the clients to be sync'd with the server, so they can adjust the display of the game board and things change on the (authoritative) server.

    Does that make sense?
     
  18. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    How do you send the data to the clients when something changes? Do you always send the whole state? Or do you send messages like "ButtonMoved", etc. and adjust the client states accordingly?

    I would write a system which has access to the local context and changes it depending on the messages that arrive. The scripts that use the context will react on the changes.
     
  19. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I have made progress. I have a question on getting a hook into the Context. I have a scene with an empty GameObject. That GameObject has the ContextHolder and flagged for creation. In that scene the host instantiates and then (network) spawns a prefab. I have setter scripts in the prefab. How do I have the setters get a hook into the Context that is in the Context Holder of the pre-existing GameObject?
     
  20. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Do you mean you wrote some custom Setters like the TextTextSetter or the ImageSpriteSetter? In this case the DataBinding class will do all the work with finding the correct context and informing its listeners when the monitored value changed. You can use the ComponentSingleSetter class as your base class.

    If you really want to get your hands on the context of a specific game object, just get its ContextHolder component. You can access the context via the Context property. You'll get the context as an object, so you will have to cast it to the type of context you expect before you can use it.
     
  21. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I am using stock everything. I have a UI Canvas with a label that has a TextTextSetter attached. The prefab is created and it has the ContextHolder and creates the context. I need the TextTextSetter to be linked to the Context created by the ContextHolder on the prefab.


    **UPDATE**
    I put a ContextHolder on the UI Canvas and tagged the Canvas. In the prefab Start() I find the Canvas and set the ContextHolder. Is this the best way to do this?
     
    Last edited: Jan 26, 2016
  22. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    This sounds okay :) You can have multiple ContextHolder that use the same context.

    What we do in general when working with prefabs that have contexts: On the root of the prefab is a ContextHolder without a context (but with the ContextType property set). This way you can easily choose the context path, e.g. in the TextTextSetter via the drop down.
    The prefab gets initialized during runtime and the context of its ContextHolder is set. You can do this manually or use the GameObjectItemSetter or (for multiple instantiated game objects, e.g. a list or grid) the LayoutGroupItemsSetter/SlotItemsSetter.

    Speaking about UI, I just published a new blog post about UI windows and data bind contexts, maybe you find some helpful information there as well: http://unity-coding.slashgames.org/ui-window-management-in-unity-5-3-with-data-bind-part-3/
     
  23. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    Could you whip up a minimal example showing how to use the GameObjectItemSetter? I can't find any documentation on it.
     
  24. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    I adjusted the ContextProperty example from the data bind package to use the GameObjectItemSetter now. Just import the package to your project, it should overwrite UntiyUtils and the ContextPropertyExample.unity scene and add "Selected Item Unity.prefab"

    If you don't have to instantiate a prefab you can also use the ContextHolderContextSetter instead of the GameObjectItemSetter.
     

    Attached Files:

  25. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I am looking at your Collection example. The CollectionExampleContext does not follow what you have provided in the DataCollection.snippet.

    The example code doesn't use Property<T>, but the code snippet does when working with the collection.
     
  26. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Thanks for the hint! Made a note, I will fix it with the next release :)
     
  27. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    @coeing Which is the best practice? The snippet or the code example?

    Thanks
     
  28. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Ah, I see! Sorry, I thought you were talking about the collection example in the package :)

    You can use both, but the one from the snippet is more general. You can also replace the whole collection via the property setter, which you can't in the example. It doesn't even have a setter, but even if it would have one, the listeners wouldn't be informed about the change.

    So in short: Use the data snippet :) I will adjust the example.
     
  29. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I was. ;-) The collection example included with the package I got from the Asset Store is like on your wiki. It doesn't use the template used in the snippet.

    I'm trying to wrap my head around syncing a NetworkBehavior object with a Context that has collections in it. If I don't get something working by the end of the work day, I may be asking for some help.
     
  30. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    I will correct the example in the package as well as I did with the one in the documentation :)

    Sure, let me know if you have any problems I can help you with.
     
  31. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    Are your naming conventions mandatory? IOW must I use "Text" and "textProperty"? IE could I use "Text" and "_textProperty"?
     
  32. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Yes, you have to use this naming convention for the property and the underlying data property as I use reflection to find the data property field for a property. The field can also be named TextProperty, but I don't check for the underlined version.
     
  33. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    OK. I use Resharper and like to follow its naming conventions. I'll adjust.
     
  34. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Hi,

    Very nice asset.
    I run into an issue, that is pretty annoying.

    I have some "Button Click Command" attached to gameobjects. It works fine.
    But after I build my game, and restart the game in the editor, the commands don't work anymore.
    I have to restart Unity for them to work again.
    Everything works well in the build.

    It seems that there is some kind of Reset mechanic after a build or something like this, like the events are not registered anymore.

    Hope to read you soon

    Robin

    EDIT:

    So I found the solution, it seems that it doesn't come from your asset.
    It's a bug with the EventSystem, ticking the "Force Module Active" did the trick.
    Cheers
     
    Last edited: Apr 7, 2016
  35. Burletech

    Burletech

    Joined:
    Jul 1, 2010
    Posts:
    73
    Hey there,

    Having some issues understanding how this asset works. I made a little demo scene and can't get it to work. It's almost exactly the same as the demo TinJaw posted but for some reason it's not working for me. I'm obviously missing some part of things. Any help is appreciated.

    Thanks!
     

    Attached Files:

  36. SidarVasco

    SidarVasco

    Joined:
    Feb 9, 2015
    Posts:
    163
    You weren't setting the data in the context.

    Code (csharp):
    1.  
    2.     public void SetHitPoint()
    3.     {
    4.         gameContext.HitPoints = myHitPoints = UnityEngine.Random.Range(0, 999999);
    5.         gameContext.TestString = myTestString = "Random Text: " + myHitPoints;
    6.  
    7.         Debug.Log( "New data should be set" );
    8.     }
    9.  
    Your mistake was in your understanding of passing values/references:

    Code (csharp):
    1.  gameContext = new GameContext { HitPoints = myHitPoints };
    Your hit points is an int, so it doesn't pass the value "myHitPoints" as a reference, rather by value.
    So if myHitPoints is 10, gameContext.HitPoints is 10, not a reference to myHitPoints. if myHitpoints changes value it won't change the value in gameContext.

    Anyways you have to tell the context what is changing.
     
  37. Burletech

    Burletech

    Joined:
    Jul 1, 2010
    Posts:
    73
    Thanks SidarVasaco. I realized my issue this morning but wasn't able to withdraw my question. Thanks again for your help.
     
  38. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    What Data Bind be used with custom classes? If so, is there an example somewhere?
     
  39. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    @SidarVasco: Thanks a lot for answering the question :) I just saw the new posts on the thread, so I'm glad the problem from @Burletech is already fixed!

    @Tinjaw: Not sure if I understand your question. What do you mean with "custom classes"? There are several examples in the package which use custom data contexts.
     
  40. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Version 1.0.6 was just submitted to the Asset Store! It brings a lot of improvements and bugfixes from the learnings of using Data Bind in our own current project (Called "Astro City" and available in the Android Play Store by the way ;) ).

    Here's the list of changes:

    • ADDED: Setters - Added setter which sets the items of a layout group one-by-one instead of all-at-once.
    • ADDED: Setters - Added setter too smoothly set the fill amount of an image.ADDED: Setters - Added setter for the canvas sorting order.
    • ADDED: Providers - Added provider for a material.
    • ADDED: Commands - Added commands which trigger on specific input events
    • ADDED: Setters - Added SmoothSlotItemsSetter which fills its slots over time instead of immediately.
    • ADDED: Operations - Initializing arguments in LogicalBoolOperation to make sure they are not null.
    • ADDED: Setters - Added event when slot of SlotItemsSetter was destroyed. Activating item in slot in case it was hidden before.
    • CHANGED: Setters - Making prefab inactive before instantiation in GameObjectItemsSetter. Otherwise new game object is already initialized before its context is set.
    • ADDED: Setters - Added base class for animator parameter setters. Added setter for animator speed. Only setting animator trigger if animator is initialized.
    • ADDED: Smootheners - Added data providers which smooth a float or long data value.
    • ADDED: Objects - Added object which holds a plain boolean value.
    • CHANGED: Lookups - More robust value getter in DictionaryLookup.
    • ADDED: Lookups - Added lookup for an item and a range of items in a collection.
    • ADDED: Formatters - Added string formatter which returns the lowered text of its data value.
    • ADDED: Formatters - Added SmoothCollectionChangesFormatter which provides its bound collection one-by-one instead of all-at-once.
    • ADDED: Formatters - Added formatter which uses a fallback data value if its primary one isn't set.
    • ADDED: Formatters - Added formatter which sets its value depending on a boolean data value.
    • CHANGED: Checks - Using utility methods in ComparisonCheck and EqualityCheck, so they don't have to do their own error handling.
    • CHANGED: Commands - Made Command class non-abstract so it can be added to a game object.
    • ADDED: Utils - Added TryConvertValue method to ReflectionUtils.
    • ADDED: Editor - Improved context holder inspector to show the set context in more detail.
    • CHANGED: Data Binding - More robust GetValue<T> method. Making sure OnValueChanged is called on initialization.
    • ADDED: Context - Added special value changed triggers if data value is of type Collection.
    • ADDED: Data Dictionary - Added key and value type properties. Implemented Add(KeyValuePair<TKey, TValue> item) method.
    • ADDED: Editor - Added custom DataProviderEditor to show current value.
    • FIXED: Getters - Using onValueChanged instead of obsolete onValueChange event of Unity input field in InputFieldGetter.
    • FIXED: SlotItemsSetter - Only hiding item game objects that have no slot to show them again when free slots are available again.
    • CHANGED: Providers - StringFormatter checks for null reference of its arguments before using them.
    • CHANGED: Setters - SingleSetter uses DataBindingOperator base class.
    • ADDED: Triggers - Added UnityEventTrigger which triggers a Unity event when a data triggers is invoked.
    • CHANGED: Commands - UnityEventCommand will forward argument to the command method instead of calling the method without an argument.
    • ADDED: Providers - Added NumberSwitch for selecting an option depending on an integer number.
    • FIXED: Providers - Updating value when data dictionary in DictionaryLookup changed.
    • ADDED: Core - Added DataTrigger which can be used to inform a context about a one shot event.
    • ADDED: Data Bind - Using deepest context if max path depth set for path.
    • CHANGED: Presentation - Using IContextOperator interface to inform all scripts that have to know it about a context change.
    • ADDED: Data Dictionary - Triggering OnCollectionChanged when value changes.
    • FIXED: Core - Getting correct item type for enumerable node in DataNode.
    • ADDED: Core - ContextHolder stores path to context to allow relative paths even for collections and initializers with multiple path parts (e.g. GameObjectItem(s)Setter or ContextHolderContextSetter).
    • CHANGED: Core - Storing parent node instead only parent object.
    • ADDED: Collections - Changed property "Count" to be a data property which updates data bindings if it changes.

    If you are using our asset already: Feel free to ask for any additions, ask questions about things that don't work out for you and in general let us know about any positive and negative feedback :)

    If you are not using the asset yet, but think about purchasing it: If you are fast, you'll save 5$, as the price after the update will be higher.
     
  41. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    Sorry. I misspoke. I should have wrote nested classes.
     
  42. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Hi Tinjaw,
    Nested classes should be no problem. If you mean nested data contexts this is also supported by Data Bind. You can just use another data context as a data property of a context. You would access its members than by the path, like "ParentContext.SubContext.MemberOfSubContext"

    Cheers
    Christian
     
  43. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    What if I have a data context which has a property that is a collection of data contexts, and I want to reference a specific item in the collection? How do I do that? I have tried using a custom path and used Items[0].ItemName, but it says it is not a legal path.

    Here is an example.
     
    Last edited: May 3, 2016
  44. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Hi @Tinjaw!

    That's true, the syntax is a bit different. I checked your example, just use
    Code (CSharp):
    1. Objects.0.Itemname
    instead :)
     
    Tinjaw likes this.
  45. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I am sure I am doing something wrong in my code, but the error is in your code.

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. Slash.Unity.DataBind.Core.Data.Context+DataNode.GetChildTypeInfo (System.String name) (at Assets/Plugins/Scripts/Core/Data/Context.cs:299)
    3. Slash.Unity.DataBind.Core.Data.Context+DataNode.CreateChild (System.String name) (at Assets/Plugins/Scripts/Core/Data/Context.cs:277)
    4. Slash.Unity.DataBind.Core.Data.Context+DataNode.GetChild (System.String name) (at Assets/Plugins/Scripts/Core/Data/Context.cs:292)
    5. Slash.Unity.DataBind.Core.Data.Context+DataNode.FindDescendant (System.String path) (at Assets/Plugins/Scripts/Core/Data/Context.cs:252)
    6. Slash.Unity.DataBind.Core.Data.Context+DataNode.FindDescendant (System.String path) (at Assets/Plugins/Scripts/Core/Data/Context.cs:258)
    7. Slash.Unity.DataBind.Core.Data.Context+DataNode.FindDescendant (System.String path) (at Assets/Plugins/Scripts/Core/Data/Context.cs:258)
    8. Slash.Unity.DataBind.Core.Data.Context.RemoveListener (System.String path, System.Action`1 onValueChanged) (at Assets/Plugins/Scripts/Core/Data/Context.cs:81)
    9. Slash.Unity.DataBind.Core.Presentation.ContextNode.RemoveListener () (at Assets/Plugins/Scripts/Core/Presentation/ContextNode.cs:326)
    10. Slash.Unity.DataBind.Core.Presentation.ContextNode.SetValueListener (System.Action`1 onValueChanged) (at Assets/Plugins/Scripts/Core/Presentation/ContextNode.cs:192)
    11. Slash.Unity.DataBind.Core.Presentation.DataBinding.Deinit () (at Assets/Plugins/Scripts/Core/Presentation/DataBinding.cs:125)
    12. Slash.Unity.DataBind.Foundation.Setters.Setter.RemoveBinding (Slash.Unity.DataBind.Core.Presentation.DataBinding binding) (at Assets/Plugins/Scripts/Foundation/Setters/Setter.cs:62)
    13. Slash.Unity.DataBind.Foundation.Setters.SingleSetter.OnDestroy () (at Assets/Plugins/Scripts/Foundation/Setters/SingleSetter.cs:47)
    14.  
    It is in a situation similar to the question I asked last time. I have a bit more complex classes involved, but basically the same.

    Any advice where I should start looking for an resolution?

    ===UPDATE===
    I realized that I was using an older version of Data Bind. I updated to 1.0.6 and all seems to be fine now.
     
    Last edited: May 5, 2016
  46. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    Glad to hear! Exceptions shouldn't occur, no matter what. So please report any that was thrown by Data Bind :)
     
  47. MaddoScientisto

    MaddoScientisto

    Joined:
    Jan 30, 2015
    Posts:
    62
    Hello, I purchased the asset a long time ago but just now got to try it out.
    I tried to follow the tutorial and the collection example, I made a context for a list and then I made a context holder that holds that context and creates the context and then added the UI stuff to get data from that context.

    The example does this:
    Code (CSharp):
    1.  
    2. public class ShipInventoryCollection : Context
    3. {
    4. private readonly Property<Collection<InventoryItem>> itemsProperty = new Property<Collection<InventoryItem>>();
    5.  
    6. public ShipInventoryCollection()
    7. {
    8. this.Items.Add(new InventoryItem() { Name = "Ayy", Amount = 1 });
    9. this.Items.Add(new InventoryItem() { Name = "lmao", Amount = 2 });
    10. }
    11.  
    12. public Collection<InventoryItem> Items
    13. {
    14. get { return this.itemsProperty.Value; }
    15. set { this.itemsProperty.Value = value; }
    16. }
    17.  
    18. public void AddItem(string name, int amount, Type type)
    19. {
    20. this.Items.Add(new InventoryItem() { Name = name, Amount = amount, ItemType = type});
    21. }
    22.  
    23. public void RemoveItem()
    24. {
    25. if (this.Items.Count > 0)
    26. {
    27. this.Items.Remove(this.Items.Last());
    28. }
    29. }
    30. }
    31.  
    so I tried to do that just for testing but what happens is that as soon as I start the game I get a null reference exception because the Items collection is still null.
    What did I do wrong?

    Also where is the context holder supposed to be? I placed it on a gameobject on top of my hierarchy, is that correct?

    And once I have this context how do I access it from a monobehaviour to put things in it?

    EDIT: so I looked at the examples in the project and that cleared the fact that the context holder has to be on the canvas object or else it won't work, I got things working but now I still don't know how to let my other monobehaviours in the scene access and manipulate these contexts, also I have no idea why the list is not automatically initialized in my code while it is in the examples
     
    Last edited: May 15, 2016
  48. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I didn't look any further than this, but you are missing an instantiation here. It should be...

    Code (CSharp):
    1. private readonly Property<Collection<InventoryItem>> itemsProperty = new Property<Collection<InventoryItem>>(new Collection<InventoryItem>());
     
  49. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    @Tinjaw already answered the thing about the NullReferenceException correctly, thanks :)

    About your other questions:
    You can put the context holder on any game object, but the Canvas makes most sense as the context will be available for all game objects underneath.

    You can put Setters/Getter/Formatters/... on the game objects below the context holder, this way they will find the context automatically if one is set. The easiest example is to put a string data property in a UI Text. For that you just add a TextTextSetter to the Game Object which holds the UI Text script. Then you choose the path to your data property.

    For the initialization: This is normally done by some setup, e.g. when loading a UI scene. But in the examples and in small projects you can just check the "Create Context?" toggle in the ContextHolder script. This will create and set an object of the specified context type on Awake.

    Hope those answers help, let me know if there are any more.
     
  50. MaddoScientisto

    MaddoScientisto

    Joined:
    Jan 30, 2015
    Posts:
    62
    @Tinjaw thank you, I guess I totally missed that because of all that nesting.

    @coeing so do I have to keep all my game objects under the canvas?

    or... I guess I could make a root object and put the canvas on a branch and the rest on the other so everything will find the holder at the top, right?

    Let's say I have a ship monobehaviour that wants to access the inventory context to place items on the list, how do I get a reference to that object? Do I have to search the hierarchy or is there a faster way?