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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more..
    Dismiss Notice
  3. Dismiss Notice

Feedback Item system - what method is best

Discussion in 'Scripting' started by LambdaTheDev, Nov 15, 2020.

?

Which method is the best?

  1. ScriptableObj

    5 vote(s)
    100.0%
  2. Class

    0 vote(s)
    0.0%
  3. Abstract class

    0 vote(s)
    0.0%
  4. Something else?

    0 vote(s)
    0.0%
  1. LambdaTheDev

    LambdaTheDev

    Joined:
    Jan 15, 2020
    Posts:
    53
    Hello Unity.
    I have a question for you about the Item system for my multiplayer game.

    Each item contains:
    - Name, ID, description, and stuff like that;
    - GameObject prefab with model
    - Use() function

    Which method will be best for me, ScriptableObject, regular class, abstract class, or maybe something else?

    Inventory is a script with List<Item>().

    Thanks for your opinions.
     
  2. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    I would expect ScriptableObjects would work well, but why not run a few experiments with each of them? Your particular needs and coding style will probably make it clear if there's a preference to be had.
     
  3. MinhocaNice

    MinhocaNice

    Joined:
    May 3, 2020
    Posts:
    249
    What each of those mean actually? I need to implement an inventory system in my game and this would help me too.
     
  4. Havyx

    Havyx

    Joined:
    Oct 20, 2020
    Posts:
    140
    ScriptableObjects as they are somewhat built for this purpose.

    Benefits
    - Less persistence between scene requirements (using Do Not Destroy and Singletons) which means less worries about potential bugs in complex systems.

    - They are lightweight and a great way to compartmentalize data that can then be serialized to a binary file (or whatever you prefer to do).

    Drawbacks
    - ScriptableObject data is not reset in the Unity Editor. This an be annoying sometimes. Imagine this scenario:

    You store data for the Player's XP. When testing a new feature you do something that increases this value from "0" xp to "10" xp. When you stop playing the game the data will persist so you would need to reset this to "0" again (or use a script that sets defaults when you play the game)

    - Somewhat ironically they do reset when on a build (PC/Android etc). This means that they act more like containers that need data put into them when the player loads the game.

    Example: A player starts with 0 experience and gains 10 experience when playing the game. Whilst the game is playing the ScriptableObject data will stay at 10 but when they close the game it will go back to 0.

    When they load the game again, you would need to deserialize a binary file and unpack this information back into the scriptable object so they can continue at "10" experience.

    Other Stuff
    One of the benefits of ScriptableObjects is their flexibility. For example, image you need to represent an integer. However, you might need:

    - an integer that represents the players current health
    - an integer that represents how many items the player is carrying
    - an integer that represents the players current xp level


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [CreateAssetMenu(fileName = "GenericInt", menuName = "SO/Generic/Int", order = 1)]
    4. public class GenericIntSO : ScriptableObject
    5. {
    6.     public int value;
    7. }
    So now you have this generic integer that can be used to represent multiple things and you can use them anywhere else by referencing them in a script.


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Example : MonoBehaviour
    4. {
    5.     public GenericIntSO playerHealth;
    6.     public GenericIntSO playerXP;
    7.     public GenericIntSO playerInventorySlotsAvailable;
    8. }
    So you could have an SO for different types of inventory items


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [CreateAssetMenu(fileName = "InventoryItem", menuName = "SO/Core/Inventory/Item", order = 1)]
    4. public class InventoryItemSO : ScriptableObject
    5. {
    6.     public int itemID;
    7.     public string itemName;
    8.     public string itemValue;
    9. }
    You could use that as a generic inventory item template that lets you create all of the possible items using a single class and keep a list of SOs.


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Player: MonoBehaviour
    4. {
    5.     public List<InventoryItemSO> playerInventory;
    6. }
     
  5. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    I guess whether or not this is a drawback depends on the context. I use Ryan Hipple's pattern, where values stored in SOs have initial and current members, so you can load the initial, change the current. Changes persist, but that makes it easy to copy them into initials if you changed them while in play mode and end up liking the change.
     
    Bunny83 and Havyx like this.
  6. Havyx

    Havyx

    Joined:
    Oct 20, 2020
    Posts:
    140
    That's an interesting method.

    I tend to use a single SO with all default values and use them to load the required SOs.

    SomeIntegerSO.value = DefaultValuesSO.someIntegerValue
    DifferentIntegerSO.value = DefaultValuesSO.anotherValue;

    The "default" values get loaded during the initial boot/loading screen and if the player is loading a game instead of starting a new game - during the loading the values are instead deserliazed from a binary file and packed into the appropriate SO.
     
  7. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    That works. I'd be a bit anxious about the coupling between singleton and all those others, but if you never forget to update them in tandem, it would work. Hipple's trick lets you put a default value in each SO individually. He also layers this with yet another SO that refers to shared SOs, with a boolean override. That is, you can have all your "enemy" GameObjects start with the same number of lives (for example), controlled by a single SO they all share. But, each enemy can use its own local value instead of the shared value by checking a boolean in the inspector, where the boolean is in that additional SO. This lets designers mix and match value settings without having to go back to the coders for any changes at the source level, nor do they have to create a separate SO for each individual enemy.

    There's a great video of a Unite 2017 presentation he gave on this on YouTube.
     
    Havyx and Bunny83 like this.
  8. lubba64_unity

    lubba64_unity

    Joined:
    Sep 16, 2020
    Posts:
    9
    I use scriptable objects for my project. i can see why people would want to have them in script classes EX: base item class with a constructor, then make a script file with all of your items, but i think that scriptable objects are more user friendly and are better in most cases. the only issue I've had with them so far is that its hard to attach my items to a piece of code (func or action) for example drinking a potion or applying an effect from an equip-able, but this is a niche case, and there are workarounds. for identifying the scriptable object when saving the game's state I just use its name.
     
  9. Havyx

    Havyx

    Joined:
    Oct 20, 2020
    Posts:
    140
    True I could see how the could be useful as it's essentially just a different method than I might use to do the same thing. I found the video you are talking about (I assume)



    So will watch it later on. Thanks for the tip.
     
  10. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    That's the one. It's excellent (but ignore everything he says about writing your own event system; I am guessing Ryan didn't yet know that C# has events built into the language).

    He refers to an earlier one by Richard Fine. It is also excellent and covers some stuff Ryan didn't. I heartily recommend both:
     
    Havyx likes this.
  11. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    Here's a simple pattern I use a lot:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [CreateAssetMenu(menuName = "Boxes/IntegerValue")]
    4. public class IntegerValue : ScriptableObject
    5. {
    6.     public int InitValue;
    7.     public int Value;
    8.  
    9.     private void OnEnable()
    10.     {
    11.         Value = InitValue;
    12.     }
    13. }
    Let's you set both values in the Inspector, but "remembers" the InitValue between test runs in play mode.
     
    Havyx likes this.