Search Unity

Advice needed: attachable modules/items with traits - ScriptableObjects

Discussion in 'Scripting' started by TehGM, Aug 25, 2016.

  1. TehGM

    TehGM

    Joined:
    Nov 15, 2013
    Posts:
    89
    Ok, so basically I am making a game a quite inspired by FTL. I have ShipRoom class which holds a module - module has health etc etc. It also has traits attached, as in for example - NaniteRepair module in this room will get +5% to repair speed. That alone is simple.
    Code (csharp):
    1.  
    2. public class ShipRoom : MonoBehaviour
    3. {
    4. [SerializeField] private Module _module;
    5. [SerializeField] private Trait[] _traits;
    However, I am unsure how to store modules with the least code duplication.
    I think about using Scriptable Objects. My structure ideally would be
    Code (csharp):
    1. public abstract class Module : ScriptableObject, IUpdateable
    2. {
    3. public float MaxHealth;
    4. public virtual void Update() { }
    5. }
    6.  
    7. public class RepairNanites : Module
    8. {
    9. public float RepairPerSecond;
    10. public override void Update()
    11. {
    12. // repair logic here
    13. }
    14. }
    And then, Room could track current health of module, and call update on it.
    Code (csharp):
    1.  
    2. public class ShipRoom : MonoBehaviour
    3. {
    4. public float ModuleHealth { get; private set; }
    5. void Update()
    6. {
    7. if (_module)
    8. _module.Update();
    9. }
    However, let's say room has trait +5% to Repair speed.
    So I'd like RepairNanites to use value of 1.05 * RepairPerSecond in it's update.

    I am not sure how to approach it without modyfying scriptable object - as they would work as module templates.

    One of solutions I can think of is make a copy of ScriptableObject in memory, and then modify values in that copy.
    Another one would be make Room have subclasses that reflect specific module data - this however would be a lot of code duplicating.
    Code (csharp):
    1. public class ModuleData : ScriptableObject { /* ... */ }
    2. public class RepairNanitesData : ModuleData { /* ... */ }
    3. public class Module { /* ... */ }
    4. public class RepairNanites : Module { /* ... */ }
    And third option I can think of, make single ModuleData as scriptable object, and subtypes of modules separate - this however is pretty nasty to deal with.
    Code (csharp):
    1. public class ModuleData : ScriptableObject, IIdentifiable<string>, INamed
    2. {
    3. private Dictionary<string, string> _additionalData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    4. public string this[string dataName] { /* ... */ }
    5. // see? pretty much not fun
    So far, the cleanest option for me seems the copying approach - however, I'd love to hear your suggestions - hopefully I'll learn some neat tricks this way, too.

    Edit: I also came up with 4th possible solution.
    Room could calculate applicable traits whenever module gets attached, and then pass Traits collection to Update with each call, and do stuff based on those traits.
    However, I'll still wait for replies - perhaps someone gets a great idea that will suit my needs!
     
    Last edited: Aug 25, 2016
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    ok, have you watched:



    ?
     
  3. TehGM

    TehGM

    Joined:
    Nov 15, 2013
    Posts:
    89
    Yes, I watched it just before posting here. Before I didn't realize ScriptableObjects are treated just as reference when attached to object.
    And now I'm hoping for a suggestion regarding working around that issue for my particular case.
     
    Last edited: Aug 25, 2016
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Your 4th solution should work pretty well. You could just pass (Trait[] _traits) to Module.Update. Or, if you need more than traits, you could define some kind of data provider interface and pass that instead:

    Code (csharp):
    1. public interface IShipDataProvider {
    2.     Trait[] traits { get; } // or:
    3.     float GetModifier(string traitName);
    4.     // etc.
    5. }
    6.  
    7. public class ShipRoom : MonoBehaviour, IShipDataProvider {
    8.     //...
    9. }
    10.  
    11. public abstract class Module : ScriptableObject, IUpdateable {
    12.     public virtual void Update(IShipDataProvider dataProvider);
    13. }
     
    TehGM likes this.