Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

[Solved] Utilizing a system sorta like ParameterOverride

Discussion in 'Scripting' started by joshcamas, Mar 10, 2018.

  1. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,268
    I am in dire need a system that allows for lists of parameters to be stacked, if that makes any sense at all.
    I guess the best way to explain would be using an example:

    Let's say I have a ScriptableObject called "CharacterData", which holds several variables: "name", "speed", "hatarmor", "cloakarmor", "pantsarmor", "feetarmor".

    So this is cool. I can create a whole bunch of these objects.

    BUT this is unrealistic for my game, since there will be MANY characters. Balancing would be a pain if every character had their own data.

    SO Instead, my idea is to have many layers of this scriptable object. In other words:

    Layer 0: BasicZombie: This is the base class, so it modifies all of the variables
    Layer 1: FastZombie: All this changes is the "speed" variable

    And so on.

    So if I have a zombie that is a "FastZombie", then it's a basic zombie with a faster speed. And if I edit the BasicZombie layer, then the zombie's attributes (and all other monsters referencing this layer) will change accordingly.

    This is also super needed for save data.

    For example:

    Layer 0: BasicZombie: This is the base class, so it modifies all of the variables
    Layer 1: FastZombie: All this changes is the "speed" variable
    Layer 2: SaveData: All this changes is the "health" variable

    In this example, any variables modified at runtime are separated into the save data layer, so then the savegame only edits what it needs to. So, if I had a million swords but only 2 had modified "durability" variables, then the only save data would be those 2 variables... since the "base data" for that item was edited in the editor, it doesn't need to be saved. The reason why I need this system is currently each item is saved individually... which results in needless clutter that adds up VERY quickly.

    I imagine this system like an array of layers.

    First it checks the top layer. Is this variable set? No? Then go to the next, and repeat.

    Post Processing Stack's ParameterOverride sorta looks like this is an option?

    I'm so unsure of how to proceed here. This system would also be mod friendly, since it would be easy to override certain parameters while maintaining others normally. So having mods stack would instantly work :))

    Any thoughts? I feel like I'm going insane.

    Thanks!

    Josh

    EDIT: Here's a crazy visual to go along my crazy idea.

     
    Last edited: Mar 10, 2018
  2. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,900
    You just described the basic use-case of scriptable objects.
    You create a SO with a parameter of speed.
    Create two asset instances out of it:

    - named BasicZombie
    speed parameter set to low
    - named FastZombie
    speed parameter set to high

    And then use these two Asset instances to create slow and fast zombies or slow down fast zombies by changing the linked SO or make slow zombies fast by changing the linked SO.

    There is no need any 'layers'. You have the SO which is a kind of 'blueprint' for data and then you have the asset instances which saved to disk with different data sets.

    This is true for the other stuff you described as well. Maybe you will need a proper organization in terms of folders in the Asset folder, but it works and proven.

    https://unity3d.com/learn/tutorials...lity-system-scriptable-objects?playlist=17117
     
  3. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,268
    Do scriptable objects work on top of each other?

    ScriptableObjectInstance1 - > ScriptableObjectInstance2

    And then when I modify the first one, the second one automatically modifies?
     
  4. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,900
    No, but you can change the defaults any time, that will affect all of them.
     
  5. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,268
    Ahh there's the problem. I'm in need of parameters that either point to a parent object's parameter, or store it's own parameter when the value is set.
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    That's not the problem. That's just object oriented.

    One approach is to use SOs to model the spec-data, and let another component hold the actual values that can be saved and loaded back in.

    For instance, you can use the SO to define the "base values". The most trivial scenario is that you only create a single instance of your "Frogman" SO (taken from your image) and use it to define the "specs" of a frogman. Let's say max health (or base health - depends how you model your health system). Let's call the SO type FrogmanData to avoid further confusion.

    Once that single data specification is no longer enough - i.e there are different frogmen (archer type, guard etc) that share the exact same attributes but have a different "base data specification" or i.e. for balancing purposes - you can create more instances, but all of those are simply containers for reference data (usually defined at editor time).
    You can even generalize this as some data container for various kinds of entities, whereas the instances of that SO would specify data for a particular kind of entity, such as Frogman.

    All your actual "Frogman" instances ( the actual GOs with all the components) would reference a single instance of the FrogmanData asset (or the generalized data type). The Frogmen (and any other entities) do not write to that referenced asset - I highly recommend to treat SOs as readonly at runtime, as this is an asset and is usually meant to be shared among multiple instances and provide consistent reference data (changes will be undone either way).

    For individual state - i.e. the player damaged one particular frogman instance - there'll be another component (either MonoBehaviour or a simple class) that will keep track of it. That's also the type that will be taken into account in your save&load logic for the world state. This type does not necessarily have all fields that are defined in the data asset, but at least those that are 1) subject to change at runtime && 2) need to be saved/loaded.

    If 1) does not apply, there's no need to include it and it can always be queried from the referenced data asset.
    If 1) does apply, but 2) does not apply, you can include it in the runtime state component but exclude it from your save/custom serialization system. In this case, those values would be initialized using the linked data asset whenever you start your game, i.e. it resets with each session so it's purely runtime data. (Think of monster HP in MMORPGS that reset when the server restarts).

    That's all you need.

    An advanced system may only save values for instances that have actually changed their data at runtime. While a trivial system saves the individual state for all instances (even of those which have no diffs to the data asset / base data), an advanced system might initialize instances from data assets first and run a second world-state update afterwards, in order to recover state from preivious sessions.

    I'd recommend to improve and optimize this step by step - start with a complete backup of the world state, then try to implement algorithms to exclude instances that have no changes, then limit the saved data to actual diffs per instance and so on and so forth.
     
    Last edited: Mar 10, 2018
  7. Internetpolice

    Internetpolice

    Joined:
    Oct 16, 2017
    Posts:
    60
    You can inherit a SO onto another SO if you need but as for a dynamic inheritance you will prob need to implement some sort of Interface and abstraction logic if you're really trying to get into advanced stuff

    It is likely to be possible to do this but i'm no expert, I've just watched several of the videos about SO
    In this video he kind of explains what you're talking about by using the brain type SO's.
     
  8. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,268
    I think I've figured it out. What made the system much more simple than layers is to use a single parent.

    Each Variable is stored in a generic class, which also has a boolean called "is Set"

    So when you try to access that variables value, it checks if the isSet variable is true. If so, then return the value. If not, return the parents value.

    And then when the data is saved to a file, only save variables with the "isSet" being true.

    We shall see if this works :)
     
  9. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,268
    True! The reason I'm looking towards using this implementation is that it uses the same container of data for saving in editor and game, and it would also support modding out of the box.

    And yus! I've found that excluding things is very helpful, such as not saving a container unless it's been actually modified by removing / adding things!

    I've seen this talk, but it looks like I missed that part, thank you!
     
  10. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,268
    I actually managed to build this system! I'm surprised it works, and it's certainly a little wonky, but it actually works!

    First off, I built a lookup table. This is a list of references to objects, each having their own guid. Then, some simple functions that either takes in an object to receive it's guid or a guid to receive it's object. Super straight forward.

    Then, I built ParameterListObject. Hate the name, plan on changing it later. This is what holds the parameters.
    Parameter is the base class for all parameters, and parameter<T> is a generic for this. Parameter is essentially a class with two major variables - isSet (bool) and value (T), alongside a string of the field name and the ParameterListObject that owns it, for reasons I'll explain below.

    Parameter has the functions .Get() and .Set():
    Set(T value): Simply sets isSet() to true, and then sets the value to value
    Get(): If isSet is true, return it's own value. If not, run owner's parent's GetParameter() function.

    ParameterListObject:
    Awake(): run on awake, loops all Parameter fields and sets their owner and field names accordingly
    GetParameter(string name): This returns a parameter's value by running it's Get().
    Parent: This is the parameter list's parent

    Quite simple! Thanks to how Unity's Serialization works, no generics can be serialized, so for every type you'll need to create a custom class for it:

    public class BoolParameter : Parameter<T>

    I also added some functions to save and load the parameter data, all it really does is save a dictionary of the parameter's and the values (and to save space it only saves values isSet is true on). During runtime I create a new parameterlistobject and set it's parent to whatever scriptable object I'm using as a reference, so then if I ever modify any values on the item (like durability) then only that will save. I also created another type, called ReferenceParameter, which allows one to hold references. (ScriptableObjects and other assets) This uses a lookup table like described here. I even made it possible to hold a list of references, which is rad.

    Example!


    Base class for books - notice the checkmarks on the left for each parameter


    Greenbook - its parent is item_book, so anything that is not checked will use those variables. Notice how the only variable being changed here is the pickup mesh.

    The only thing I need to add (that you can't really see here) is when a checkbox is unchecked, it displays it's own value greyed out. What would be much nicer is if it would show the parent value greyed out, since then you can see what the value actually would be.

    All in all, I am incredibly overjoyed I finally built this. This has shrunk the footprint of a single dropped item from 10 kb to less than 1 kb, as well as made it so easy to modify values for different objects, even with old saves. (So if I have a save where a sword was 10 damage, if I edit the scriptable object to have 15, it will have 15 next time I load it)

    I'm sure no one really cares about this LMAO it's just something I've been wanting to build for like 6 months now. If someone really wants the code I can give it, it's not pretty and pretty clunky. But it works :')

    Thank you friends for all of the help!