Search Unity

GlobalVariables

Discussion in 'Immediate Mode GUI (IMGUI)' started by NoBrainer-David, Nov 20, 2017.

  1. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    Hello everyone,

    Ryan Hipple from Shell Games explains the concept of "Modular Data" and its benefits in his excellent talk at Unite Austion 2017 (timestamped link). I had been working on a surprisingly similar concept that I want to share with you here.

    Use Case

    Keeping values on different prefabs in sync is burdensome and error-prone. A way to solve this problem is by not storing a value on the prefab itself but to reference an asset in the project folder. Now, all prefabs that share this value can reference the asset instead, automatically keeping them in sync.

    One problem that arises from this, however, is that you don't always know in advance if you want a simple value or a reference to an asset. In a naive implementation, this would lead to many .asset files cluttering your project. That is why the ReferenceableValue can specify if it is local or global. 'Global' meaning that it is using a reference to an asset and 'local' meaning that it is storing the value itself.

    The Goods
    Check out our public repository for the code and an example project. The readme also shows you how to easily make any type into an asset that you can reference.

    Feel free to contribute! If you have any questions, post them here or contact me directy at david@nobrainer-interactive.com.
     
    Last edited: Feb 6, 2018
    rakkarage, Malbers and Fajlworks like this.
  2. Fajlworks

    Fajlworks

    Joined:
    Sep 8, 2014
    Posts:
    344
    That's actually a clever idea you got there, thanks for sharing!
     
    NoBrainer-David likes this.
  3. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    Thank you so much! My plan was to publish this later in the year after I submit my first Unity Asset to the store but seeing as there was some interest in a very similar methodology because of Ryan's talk, I thought I'd post this ahead of schedule. That's the reason why it is a little rough around the edges and there is very little documentation as of yet.

    However, I intend to keep on working on it because I use it myself in the bigger project.
     
  4. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    I started working on a more generic implementation of some of the examples from Hipple's talk myself because I think the ideas are ingenious, but I have been struggling a little with how to serialize these as generic types. Your code will probably be quite helpful to me in that regard so thanks for sharing!

    I'm very tempted to help contribute to your project too, but I don't think I really like your naming convention. No offense, terminology is just kind of a personal obsession of mine :). Also think your system might be a little too generic for my tastes, since it isn't actually limited to just single value types. Now that's not really a bad thing, but it kind of ruins the elegance and simplicity of the idea of "variables as assets" for me, but this is all very much my own personal opinion.

    I'd also say the inspector view for ReferenceableValues is ugly and confusing at the moment, but I'm sure this is just because it wasn't exactly ready for prime time and I appreciate you releasing it anyway so others like myself could maybe find it useful.
     
  5. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    Thank you for your interest!

    I am actually planning on writing a blog post on exactly that topic. It took me quite a while to figure out a class structure pattern where you can:
    • make use of generics to implement common functionality once
    • have the data of that class be serialized by Unity
    • allow other code to have a polymorphic reference to a common base class.
      • that is, client code doesn't have to know about the derived type

    For starters, take a look at the GenericAssetWrapper and then StringAsset. Let me know if you have any questions!


    Just because the system allows you to put in custom classes, doesn't mean you have to. It gives you the freedom to decide for yourself. I can also see where there is a use for grouping certain values together, but as you say, that is personal opinion. But opinions change, so having a system that works regardless might be valuable.


    You are right, I pushed it out into the world in an immature state. I hope you still find something useful in there!
    I agree, the inspector is not so great. I really like Ryan's explicit setting of 'reference' or 'constant'. Check here for a Roadmap for plans for future development where I added that to the list of UI improvements.

    I see you forked the repository, I am interested to see where you take this. If it is solely for naming reasons, we should talk, as combining our efforts will certainly be beneficial!
     
  6. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    I actually totally agree with you here. In hindsight I think I just explained my actually issue with the generic-ness here all wrong. I'll try to expound on this in the future.


    I'm not as familiar with BitBucket as I am with GitHub, so I don't know what exact features they support. Do they also have an Issues section where we can discuss these things? Your Roadmap link gives me a "You do not have access to the wiki." error message. So maybe it's set to private?

    Naming Conventions

    Okay, I'm gonna be brutally honest here, but please remember these are all just my opinions.

    First off, I don't think "Referenceable Value" is a good name. It seems too ambiguous and not ideal for representing the core system. We're working with ScriptableObjects here, which inherit from Unity Objects, so I think "Referenceable Object" is more fitting, but I also don't like the use of an adjective, I think they're only supposed to be used with interfaces in C# anyway. In my fork I renamed the classes "AssetWrapper" and "ReferenceableValue" to "AssetObject" and "ReferenceObject" respectively, which I think is an improvement. Let me know what you think.

    This also leads me to another issue I have: the use of words like "wrapper", "abstract" and "generic" in the class name. I think it's fine to use these terms inside the classes themselves, but I don't think they should be used in the class's actual name. It just seems completely unnecessary to explicitly state such things in the names even if it's actually true.

    Inspector Property Drawer

    Yeah, I like Hipple's property drawer, but found the "variable" vs "constant" names confusing at first because that's just not what I think of as a constant. I actual like your "global" vs "local" names better, but realized this might also not be that intuitive for many. So I'm now thinking "asset" vs "variable" are better terms maybe. Thoughts?

    In any case, I think an ideal inspector view would be a hybrid of both yours and Hipple's. It would basically display the referenced field exactly like a standard one in the inspector, but would have an added asset browser or popup menu button next to it where you can select an asset to use for that field. If no asset is selected then the field behaves just like a normal one, but if you select an asset then that's used instead. You could then attach "(asset)" or "(reference)" to the end of the property field label to let users know what is actually being used. This is something I'm currently experimenting with making, but I'm kind of bad at editor scripting.
     
    Last edited: Dec 7, 2017
  7. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    First off, sorry for taking so long to get back to you. I missed the forum notification on this thread. Maybe we can continue this discussion via e-mail? (david@nobrainer-interactive.com).

    You're absolutely right, I had it set to private :/

    Yes, there is an issues section. I haven't used BitBucket a lot either, so mostly all of this is new for me. I just find mercurial to be more intuitive than git, so I use BitBucket.

    I also linked my the Trello Board I plan to use to keep up with the different tasks. Discussions on there are very easy to have, as well. You can use this link to join the board.


    I agree that the naming needs work.
    One thing about your proposal: the user of "ReferenceableValue" does not care if it uses a ScriptableObject or a local object to store the information he/she is interested in. And this information might not even be an object, it can just be an int. So having the *Object postfix on a lot of names is not really ideal either.

    The only naming I stand fully behind is AssetWrapper. Because what does it do? It takes a generic parameter and wraps it up to make it an asset you can see in the project view. Perfect description, no?


    In regards to "asset" vs "variable": the asset is variable, as well. I actually prefer global vs local here, because global and local are keywords in programming vocabulary, describing scope, that perfectly fits what we are trying to achieve here.
    So what we want to achieve is to have a field where you do not care if it is global or local. That is up for the designer to decide. So it is scope agnostic. So maybe we could rename it to ScopeAgnosticReference? Or SAReference for short? What do you think?


    Right, 100% with you. It was more of a crutch for myself while I was developing the class structure to remind myself why those classes were necessary. Documentation would have been the way to go there, I guess :)
    Only the "Wrapper" I find to be so essential to what the class does, that I find it appropriate there.


    I agree with the direction on this. Combining the benefits of both is the way to go!
     
  8. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    I just pushed some changes to the project. I went ahead and made a few naming changes of my own.

    Additionally, I added a flag to the AssetWrapper, that lets you specify if you want playmode changes to persist or not. I needed to upgrade the project to Unity 2017.2 in order to achieve that (for the EditorApplication.playModeStateChanged callback). I wrap the code in appropriate preprocessor directives, so it should still work in older versions, just without this feature.

    Check out a video demo of the new feature:
     
  9. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    I prefer "object" over "value" here because of the generic nature of this base class. Yes, you could implement just a single int, but it's still stored in an object and you could just as easily implement a big complex class instead and referring to the latter as a "value" seems weird, but maybe my definition of "value" in this context is incorrect?

    This also doesn't mean you need to suffixed everything that derives from it now with "object". It's just used here because of the generic nature of the base class. The actual naming of what derives from it totally depends on the purpose and personal preference. For example, a complex multi value class could be named anything you like, while a single value variable like class could have a suffix of "var" like "FloatVar". etc.

    If this base class was actually limited to a single value then I'd actually be fine with the current name, but as it stands I would have the same exact issue if you called it a "ReferenceVariable" instead, because "Variable" also seems like way too specific a term for such a generic system. I think this is somewhat related to my original issue regarding the super generic nature of it all.

    I might not understand the concept of a wrapper correctly, but to me it seems like you're just creating data assets with generic types and not really wrapping anything, but maybe my understanding here is all wrong? Reading up on the supposed definition hasn't really helped clear things up though. However, in any case Unity doesn't even include the word "wrapper" in the name of it's own wrapper type classes. So there's that.

    Upon further reflection I actually came to the conclusion that your original "local" vs "global" terms make the most sense from a designer perceptive. It's pretty intuitive so I actually agree with you on this now. I was really just thinking out loud I guess :)

    Sorry, but I really dislike "ScopeAgnosticReference" for some reason. Something about it seems way too explicit and ambiguous at the same time. I'd actually go with "Object/ValueReference" over that. Just seems a lot more straightforward and concise.
     
  10. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @NoBrainer-David Don't know if you've seen this already, but Unity's Post-Processing Stack (v2) makes extensive use of ScriptableObjects and implements a lot of advanced systems to deal with them. So you might find it very helpful in aiding you in the development of your own system.
     
  11. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    Okay, great to hear that. I really like the distinction local/global, as well. Let's build on that!


    I agree, if you look at the distinction between pass-by-value and pass-by-reference (StackOverflow discussion), value really refers to the specific value a variable is at any one point. So when you pass a value, you do not pass the object, you copy the object and then feed the copy into the function call. By the way, this can only be done with primitives (int, float, etc.) in C#, since all objects are pass-by-reference.
    So let's purge "value" from all the names :)



    Because of the above, I would argue that object and value are more specific than variable. Since a variable can be an object or a value. Also, global variable is a term that has a meaning in programming terms which aligns very well with the actual goal we are trying to achieve: having objects/values that can be referenced from anywhere, the specialty being that the designer hook up the references via the Editor GUI.
    Also, you propose to name the concrete classes with a "var" suffix, so yo do secretly like "Variable" ;)


    I would propose we scrap the AssetWrapper name and actual call it GlobalVariable. I think you are right, the name should be more about the intent and less about what it actually does.
    Next, we scrap ScopeAgnosticReference and call it VariableReference (because it references a variable, either local or global).
    The concrete classes we could then call FloatVariable, and FloatReference, respectively.


    What do you think?

    I think it's great we're having this discussion now instead of a few months down the road. Thank you for helping sharpen the naming!

    Also great link to the post-processing project. I will check that out for best-practices in editor coding.
     
  12. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Hmm.. seems like my understanding of the concept of a "variable" has been all wrong. I've always thought of them as single values, like floats and vectors. I've never actually thought of more complex data structures, like sub-classes, as actual variables before, but it seems I was mistaken.

    I could probably try to argue that variables are objects in a way, but I think that's going too far :). I think going with "variable" is an excellent compromise here and I actual like the idea of a GlobalVariable and VariableReference a lot. Funny thing is, we've basically landed exactly where Ryan Hipple did with his own naming, with the exception of the global and local distinctions, which I think are excellent additions to the basic concept.

    I proposed using "Var" instead of "Variable" because I find using the full name for every declaration kind of annoying. In fact, I was even thinking about the idea of not using any suffix at all for the reference! Just calling it "Float" instead. So you could use a float reference instead of a normal float by just capitalizing the name like so:

    Code (CSharp):
    1. using NoBrainer.Utility.Reference;
    2. using UnityEngine;
    3.  
    4. public class MyClass : MonoBehaviour
    5. {
    6.     public Float myFloat = new Float(1f);
    7. }
    However, I guess this could cause confusion and naming conflicts and is not as clear as going with FloatReference instead. Just some thoughts anyway.

    No problem and thanks for listening to my feedback! Hopefully this will make your system even more awesome in the end. :D
     
  13. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Probably the most interesting thing about that repository I've seen so far is that they've built a system that automatically supports derived classes in their custom inspector without any additional coding. Learning how to do something like that in your own system should make it even easier to extend.

    I also found this interesting comment on Ryan Hipple's blog:

     
  14. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    Ha! You're right! That also has the benefit of people only having to do minor renamings in their head after watching his presentation to using this system.

    So we have a naming scheme we agree on. Nice!


    That statement by Ryan Hipple you quoted further reinforces my belief that the class structure in this project is very similar to how he structures his.


    That sounds very interesting. I will try to see how they did that. Before I go browsing the entire repository, do you know of an example where this shows?
     
  15. roboryantron

    roboryantron

    Joined:
    Apr 2, 2014
    Posts:
    5
    I can throw a new suggestion into the naming argument!

    I was not really happy using the term constant.... since it is not a constant. I have my variable reference implementation now use the term "literal" to describe a value specified locally instead of in another variable. I feel like this is more in line with programming language descriptors.

    I also have the idea of "read-only" on the variables that makes it so that they are not mutable at runtime.
     
  16. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @roboryantron I've never actually heard the term "literal" used in relations to programming before, but that doesn't mean much coming from me. However, the first thing I found when researching it on the internet says that constants and literals are the same thing!? That would make sense to me because both terms seem to suggest very rigid or fixed constraints that have nothing to do with scope. I mean, a constant/literal value can be either public or private, right? So I still think using "local" and "global" is still more fitting and intuitive.
     
    Last edited: Apr 14, 2018
  17. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @NoBrainer-David I've now implemented common variable types (Bool, Float, Int, String) and an event system for value changes into my fork of the repository. The last thing I want to do before working on the editor GUI is to implement implicit operators for each common type, so you can assign values to them just like a normal variable. However, I'm having some trouble implementing it. Here's what I have so far:

    Code (CSharp):
    1.  
    2.     [Serializable]
    3.     public class Int : ScopeVariable<int, IntAsset>
    4.     {
    5.         public Int(int val)
    6.         {
    7.             Value = val;
    8.         }
    9.  
    10.         public static implicit operator int(Int scope)
    11.         {
    12.             return scope.Value;
    13.         }
    14.  
    15.         public static implicit operator Int(int val)
    16.         {
    17.             return new Int(val);
    18.         }
    19.     }
    20.  
    This works, however I've noticed that if I use the implicit operator (myInt = 5) to update/change the value instead of the value property (myInt.Value = 5) that it will break/remove any GlobalVariable links, since the operator is returning a new variable. Is there a way to make this operator work without returning a completely new variable? And/or could the implicit operator be implemented in the ScopeVariable base class itself, so you don't need to declare them in every derived class?
     
    Last edited: Jan 31, 2018
  18. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    @roboryantron Thank you for your input! The "read-only" idea is also really good!

    I tend to think along the lines of @IsaiahKelly that the term literal implies something fixed and not really fitting to the idea that I have in my head of this project.

    @IsaiahKelly In regards to the naming: I find it inconsistent to call one GlobalVariable and the other ScopeVariable. I pushed my naming changes to the repository to what we agreed on before.

    I attached an image that I think might make clear why I want to keep the name VariableReference. The VariableReference contains a LocalVariable but can also point to a GlobalVariable. But the code that uses the VariableReference does not need to know about this. This adds a level of indirection which is exactly what a reference does.
     

    Attached Files:

    Last edited: Jan 31, 2018
  19. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    I don't see a way how you can make (myInt = 5) work. I don't see a way to get a hold of the variable that you want to set this on in the static operator method.
    I think you will have to use myInt.Value = 5.
    I guess your problem is that this makes it a pain to upgrade a script to use IntVariables instead of ints? I don't see a way around that...

    About the implicit operators in the base class:
    You can implement one of them in the base class. The one where you cast from the GlobalVariable<T> to T. The other direction is problematic. In order for this to work, the base class would need to know the specific derived class in order to be able to create an instance of it. I don't think this would be a good idea.


    I will also check out your event system. Sounds like a very useful feature!
     
    Last edited: Jan 31, 2018
  20. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @NoBrainer-David Not sure I understand what you mean by "inconsistent", but I can now see how using "Scope" might be bad.

    I only really wanted the implicit operator for setting initial values in a script, and that works great. The problem is when you try to change/update a GlobalVariable value using the operator. So if we could just prevent someone from using the operator outside of initiation or local values, that would kind of "fix" the issue, but I don't think that's possible? Also setting initial local values is pointless if you just use a GlobalVariable anyway. So maybe the inconvenience of not having the operator is better than risking all the issues it might cause. Just using a constructor is probably the way to go.
     
    Last edited: Apr 14, 2018
  21. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    In my fork I also condensed some classes into a single file. Namely, the GlobalVariable and VariableReference generic and "untyped" base classes, or whatever you call them. This seems to have worked fine. However, I also tried condensing my new common types into single files as well (reference and asset class), but discovered that the ScriptableObject assets created from these classes would then lose all their data after closing and reopening the project in Unity. So now I have a separate reference and asset class file for each of them again. Think this issue has something to do with how Unity serializes and saves the data.

    Do you know of any adverse effects caused by condensing classes into a single C# file? Is this just bad practice? I'm really just doing it here for organization, portability and convenience.
     
  22. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    I agree. I only implemented the operator that casts downwards. The other one is just confusing.


    Yes, this definitely does not work, as Unity wants one serializable thing per file only.

    Let us continue these discussions on the issues you have raised in the BitBucket repository! Thank you for your initiative in that regard!
     
    Last edited: Feb 1, 2018
  23. Nyckos

    Nyckos

    Joined:
    Feb 1, 2017
    Posts:
    2
    Thanks for sharing! I'm currently working on a similar concept. I haven't explored all your code yet, but this definitely caught my interest.
     
  24. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Hey @Nyckos!

    Don't know what your exact use case is with this type of system, but I'm currently implementing common global variable types (`bool`, `float`, `int`, etc.) and Unity event trigger scripts for each one. This will make it super easy to setup event callbacks right in the editor for any common global variable type. No coding required!

    Please also feel free to join us over on [Bitbucket](https://bitbucket.org/nobrainerinteractive/globalvariables) and give us feedback and/or contribute to the project. Working together will only help make this thing more awesome for everyone. :)

    @NoBrainer-David Oops, looks like the repository rename broke your link to the repository in the first post. Might want to fix that ;)
     
    NoBrainer-David likes this.
  25. Xandir88

    Xandir88

    Joined:
    May 2, 2017
    Posts:
    1
    Agreed ! thanks to you guys for this.
    I'm currently using the original GitHub code from the Unite Talk to experiment and understand the concepts However I'm really thrilled about exploring your Code base and probably going to try to use it in a demo project to see how I feel about it.
    It might take some weeks until i reply but I'll sure give it a shot and give you some feedback for this
     
    NoBrainer-David and IsaiahKelly like this.
  26. Nyckos

    Nyckos

    Joined:
    Feb 1, 2017
    Posts:
    2
    Hey @IsaiahKelly, sorry for taking so long to reply

    I use this type of system as an elegant alternative to singletons when I need a static variable. Starting from the examples from the Unite Talk, I built my own solution with global variables, lists, and events in a concrete team project. I use it with both common and custom types, and I'm able to listen directly to a variable (or a list) just like an event.

    I totally agree with that!

    Some thoughts after exploring your code:

    • A notable difference between our two solutions is that you use two files per type like the examples from Ryan Hipple's talk (e.g. StringReference and StringVariable) while I use only one (the variable). I guess this is because the reference allows you to store a local value? So far, when I need a local variable, I use the regular `bool`, `float`, `int`, etc; in code. That way I don't have to write `.Value` when using a local variable (e.g. in assignments, or when accessing an inner member), and I like the benefit of having a single file per type. Moreover, I can't prevent my teammates from using regular local variables, so we end up with regular local variables anyway.

    • About custom classes, I didn't understand the purpose of the ICustomClass interface. It looks like it's implemented by 4 classes (CustomClass, CustomClassVariable, CustomClassReference, and ComponentWithReference) but not used anywhere else. What's its purpose? Access `Description`, `OuterInt`, and `InnerObject`directly from a CustomClassReference without a `.Value` in the middle? Do you create such an interface for every custom class?

    • I played a bit with the way you reset values (I originally discovered this thread while looking for an elegant way to reset assets) and overall it works great, but I might have found a particular case with a slight unintended behavior. When a global variable changes in Update(), like a float game timer for example, the asset isn't reset to exactly its initial value. It's consistently about 0.02 higher each time. As a result, the asset slowly increases to 0.02, 0.04, 0.06, etc; after each play. I can push an example test scene showing that behavior if you are interested.

    • By curiosity, why do you declare a non-generic class of the same name whenever you define a generic class?
     
    NoBrainer-David likes this.
  27. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    IMPORTANT NOTE: @NoBrainer-David is the owner / creator of this repository. I'm only a contributor. Maybe you @ the wrong person by mistake?

    I assume you're using an event system just like the one found in Hipple's source code? If not, it would be interesting if you could share your implementation.

    You don't actually need to use the .Value when getting the value of a variable in this implementation, thanks to an implicit operator suggestion I made to solve this very issue. However, for now it only works for getting values. You still need to use .Value to set them. This is because of the way implicit operators work (or fail to work) with this system. See reply #17 for more info.

    The reason for the extra reference type is explained in Hipple's talk and this repository's readme:
    You can still use a global or local variable directly when you know for sure it will only ever be one type or the other, but that's not always easy to know, and the reference type is a great convenience because it eliminates the need to determine the type ahead of time. You can also switch between types without loosing any data! So I think the extra class is a small price to pay for all the benefits it gives you. It's also only a few lines of code.

    This is David's code and I haven't examined it myself yet to see why he set it up that way. So I'll have to let him explain it. I'm also curious about this too.

    Thanks for reporting this! That's definitely a bug. Not sure what's causing it yet, but the only way I can see this being possible is if Update is called before OnEnable, since that's when the original reset value is copied. I suspect that it must be copying the value right after the first update for some reason. I'll see how I can fix this.

    I believe this is required to get custom generic types to serialize their data correctly in Unity.
     
    Last edited: Apr 14, 2018
    NoBrainer-David likes this.
  28. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    Hey guys, sorry for the late reply. I only got notified of Stephans Post.


    Say you want to have an array of GlobalVariables. For example, you want to have one place to check all player-defined settings (invert y-axis, mouse sensitivity, etc.). So you have a GlobalVariable<bool> and a GlobalVariable<float>. Without declaring the non-generic base-class, you could not put them in the same array.
    With a non-generic base class, you can do this:

    Code (CSharp):
    1. GlobalVariable[] settings;
    Now you have an array of settings that can easily be extended via the editor without changing your code.

    Whether or not this is the best way to do this is questionable. This was the first example that popped into my head ;)

    Originally, I wanted to illustrate how you can further abstract away the difference in handling a CustomClassVariable, a CustomClassReference and a CustomClass by making them all implement a common interface.

    This way somewhere else in your game you can just retrieve an ICustomClass and not worry about if it is actually a project-space variable, a field on a MonoBehaviour or even just a plain old C# object.

    Without explaining this, I should probably not have included it in the repository. The whole demo section needs an overhaul!


    Thank you for finding this!

    If you have time, it would be great if you create an issue for this on the repository directly here.

    That way, I will definitely notice it much sooner!

    Thank you, Isaiah, for picking up the slack here on the forums!
     
  29. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    @Nyckos I created an issue on BitBucket describing the readonly-problem.
    I also created a branch with a possible fix.
    Before, it was assumed that OnEnable is only called once. However, this may not be the case if things go in- and out of scope. So it is bad to rely on this.

    I would love to hear from you, if this fixes the bug!


    Also, for a more structured discussion of different features, I would suggest using the Trello board. It is really easy to have focused discussions on different topics there.

    The Trello Board and the Issue Tracker may cover a lot of the same ground. I would suggest Trello to be used for more informal discussions and brainstorming and the issue tracker only for things that will definitely be implemented or definitely need to be fixed.

    As always, feedback is very welcome!
     
    Last edited: Apr 12, 2018
    OverboyGames likes this.
  30. arshadameen

    arshadameen

    Joined:
    Mar 5, 2018
    Posts:
    8
    Whole discussion is very informative for me.
     
  31. NoBrainer-David

    NoBrainer-David

    Joined:
    Jan 5, 2014
    Posts:
    34
    @eestradaRAS, once you've wrapped your head around the basics, be sure to check out the ranged variable feature branch. There is a really cool example scene "PongExample" where a ball bounces around in a box. Which is not very exciting by itself. However, the parameters in that scene setup are all controlled via GlobalVariables. This is realized with GlobalVariables, DependentVariables and RangedVariables.

    I've been meaning to finish this feature, document it and merge it into the develop branch but I've been really swamped with work lately.

    I hope you find something useful in there and would love any and all criticism and feedback!
     
  32. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    Am I right in thinking that in your implementation any consumers of your global variables have to constantly poll for value changes and do a comparison or is there a message system that a value has changed?
     
  33. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @Ziplock9000 No. Values are not constantly checked like in the Ryan Hipple's simple Unite examples . There is a basic event system that handles all that. Checkout the LocalizationExample in the develop branch of the repository for more details.
     
    Ziplock9000 likes this.
  34. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    Brilliant.. Thanks.
     
  35. Braza

    Braza

    Joined:
    Oct 11, 2013
    Posts:
    136
    I've just watched Hipple's talk and assumed someone should have already gone that path since 2017. Glad to find your thread and repo. Good job!
     
    NoBrainer-David likes this.
  36. PitaBreadGames

    PitaBreadGames

    Joined:
    Sep 15, 2017
    Posts:
    5
    Hello, @IsaiahKelly @NoBrainer-David!

    This thread has been super informative! I wanted to check out the repo, but it brings me to the below message:
    upload_2020-9-14_23-22-50.png

    Is the project dead? Or was the repo made private?
    This has been super helpful understanding scriptable objects' usefulness for global variables, but I haven't seen any of the code y'all are talking about. I would like to explore the repo, if possible.
     

    Attached Files:

  37. das_hund

    das_hund

    Joined:
    Sep 17, 2020
    Posts:
    1
    Hello PitaBreadGames,

    Thank you for your interest! I am the original poster. Long story short, BitBucket stopped supporting mercurial. That is why it went offline.

    I have converted the project to git and also applied some modernizations (e.g. Assembly Definitions).

    There is still a lot of work to be done. But feel free to explore it over on GitHub:
    https://github.com/das-hund/GlobalVariables
     
    Last edited: Oct 6, 2021