Search Unity

Discussion Handling many similar behaving attributes of a class

Discussion in 'Scripting' started by pi_314159265, Jan 2, 2023.

  1. pi_314159265

    pi_314159265

    Joined:
    Aug 14, 2022
    Posts:
    29
    I have a class "PlayerAttributes", which main purpose is handling (improveable) statts of a playercharacter (e.g. stamina, haste, dexterity, ...). While all of these statts do something different for the character, they share some features in handling them. As am example each of my attributes has 4 values
    • raw (e.g. rawStamina) -> this value comes directly from spending (earnt) statt points
    • factor (e.g. factorStamina ) -> percent bost of the statt (e.g. from temporary buffs)
    • additive (e.g. additivStamina) -> additive bost of the statt (e.g. from temporary buffs)
    • no prefix (e.g. stamina) -> the final value used to modify the character, calculated by the 3 values above
    Now there are some functions needed handling this 4 values for example
    Code (CSharp):
    1. public void CalculateAndSetStamina() {
    2.         stamina= (int) System.Math.Round(factorStamina * (rawStamina + additivStamina));
    3.     }
    4.  
    5. public void SetRawStamina(int newValue) {
    6.         if (newValue < 0) return;
    7.         rawStamina = newValue;
    8.         CalculateAndSetStamina();
    9.     }
    10.  
    Now there are several options implementing the handling of these statt-functions
    1. Hardcode everyting, i.e. write the same function for each of the attributes
    2. Use reflection and parse the attribute name as string
    3. Write a class which provides the needed functionality and create an instance for each desired attribute (in the script handling these statts)
      1. Code (CSharp):
        1. public class StattHolder {
        2.     public string stattName;
        3.     public int rawValue, value, additivValue;
        4.     public float factor = 1;
        5.  
        6. public void CalculateAndSetValue() {
        7.         value= (int) System.Math.Round(factor * (rawValue + additivValue));
        8.     }
        9. ......
    4. Use arrays similar to reflection, i.e. parse the correct array position as additional function input
      1. Code (CSharp):
        1. public void CalculateAndSetValue(int arrayPos) {
        2.         values[arrayPos] = (int) System.Math.Round(factors[arrayPos] * (rawValues[arrayPos] + additivValues[arrayPos]));
        3.     }
    5. Use dicts similar to arrays or reflections
    I was using approach 3, but i ran into a problem now:
    At the Moment i see 2 possible solutions
    1. Modify the parts in my code, which copies the GameObjects and some how copy the components, which lose data "by hand", too
      1. Is this possible or will this still fail, because of not serializable data?
    2. Use an other approach (1-5 in the above List)
      1. My favorite at the moment is 4 (use of arrays)
    Can anyone give some advice, if i am on the right track or should i do something completely different?
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    All you need to do is make your StatHolder class serializable. That's pretty much as simple as putting
    [Serializable]
    in front of the class definition if it is as you say here.
     
    pi_314159265 likes this.
  3. pi_314159265

    pi_314159265

    Joined:
    Aug 14, 2022
    Posts:
    29
    Thank you for the suggestion, but
    upload_2023-1-3_0-41-17.png
    upload_2023-1-3_0-41-31.png

    Am i doing something wrong?
     

    Attached Files:

  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    Probably just missing
    using System;
     
    pi_314159265 likes this.
  5. pi_314159265

    pi_314159265

    Joined:
    Aug 14, 2022
    Posts:
    29
    Indeed, that was the problem. Thank you for the quick fix.

    It is sometimes funny, how one can think about a problem for 1-2 hours with no progress and someone with other / more knowledge can fix it in seconds.
    Good that communication (e.g. forums exist :) )
     
    Last edited: Jan 2, 2023
    Kurt-Dekker likes this.
  6. pi_314159265

    pi_314159265

    Joined:
    Aug 14, 2022
    Posts:
    29
    I also had to add the tag [SerializeField] to a private (boolean) variable in the class StattHolder (which is not present in above picture). While it is somewhat clear, why i had to do this, i am confused why Unity behaves in this way:
    • When i copy an object, i would expect, that (by default) everything is copied
    • Instead i have to mark somethings explizitly to be copied
    Can someone give some insight to why it Unity is inplemented in this way? Is it, because deep copies are hard to do by default?
     
    Last edited: Jan 3, 2023
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,927
    Unity only copies serialised values. It's as simple as that. It makes sense that Unity works with data 'as it is written' so that it can be written out again in another location.
     
  8. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    It makes more sense when you realize the environment Unity serialization is working in where almost every object that it serializes has a bunch of extraneous fields that should not be serialized in general:
    • InstanceId
    • References to native C++ objects
    • References to various Components or the GameObject a script is attached to
    • etc
    Going with an "opt-in" approach makes things very clean.
     
    pi_314159265 likes this.
  9. chemicalcrux

    chemicalcrux

    Joined:
    Mar 16, 2017
    Posts:
    720
    To be "serializable" means that you can reliably and correctly go between an object in memory and data at rest.

    Obviously, you could just make a bit-for-bit copy of anything, but that might result in an invalid object! Consider a class that has a pointer (eek) to a blob of memory -- if you make a bit-for-bit copy, now two objects point to the same memory, and everything explodes.

    Hence, things are non-serializable by default.