Search Unity

Question Advice On Game Objects Vs Scriptable Objects For Holding Data

Discussion in 'Scripting' started by PaperMouseGames, Mar 23, 2021.

  1. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    So I've been working on abilities and on an ability tree system for my RPG. The system basically involves a UI with a bunch of buttons, and when a button is clicked, you learn the ability that the button holds if you meet all the requirements.

    Right now, each button is holding a ScriptableObject that is called
    AbilityData
    . These basically say what the ability does and what the requirements are.

    The thing is, now I'm having second thoughts about how to organize this system. I'm wondering if it makes more sense to put the ability properties that are in
    AbilityData
    directly onto the buttons as a new MonoBehavior script. So rather than each button holding an SO, they would hold MonoBehaviours that have all the properties and requirements for the abilities.

    This change occurred to me, because as the ability trees grow, it's becoming a pain to keep track of each SO and where it's attached. So for example, since the requirements are kept in the SO I have to make sure that I line up the abilities on the ability tree as the SO wants them to be lined up. If I want to edit an ability I have to leave the ability tree canvas and go edit the SO.

    I'm just wondering if it would be way easier to work with the abilities if they were just MonoBehaviours but I don't know if there are any major drawbacks to this idea.

    I tend to use SOs a lot, especially when saving data. I use them for my item system, so I can create custom items that aren't randomized, I use them sort of like enums as well, but I'm not sure they are the best fit for this ability system.

    EDIT: More info about the issue I'm explaining.

    Here is a picture of how my SOs handle abilities:
    https://imgur.com/a/MuXWC5H

    Here is how my ability trees look while I'm working on them:
    https://imgur.com/a/UKeHzXp

    So basically, if I want to edit the ability or its requirements, I have to do it outside of the ability tree, the thing I dislike about this, is in an ability tree system, I feel like how the ability is placed in the tree and what the requirements are are very intertwined. So making everything from the SO into MonoBehavior on the ability tree buttons would put it all in one place.

    The thing is, I don't know if making them MonoBehaviours would have some bad consequences that I'm unaware of. I guess instead of a list of
    AbilityData
    SOs I would need to keep a list of MonoBehaviours to know what abilities I have? I'm not sure if that's a problem
     
    Last edited: Mar 23, 2021
  2. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    I love SOs and rarely end up thinking a MonoB is the better choice where an SO is an option.

    Hard for me to tell from your description, but I would consider the possibility that the problems you are having are due to coupling between your SOs and your tree structure. If the tree has to be configured a certain way because of something about one or more SOs, maybe whatever it is that stores that configuration is in the wrong place (maybe it's in the tree when it needs to be in the SOs).

    Best I can do with what I am able to infer. Of course, never let analysis paralysis stop you from experimenting. Freeze a copy of your project, redo some of it to use MonoBs, and see if you like that better. No one says you have to know it's the right way to do something before you try it.
     
  3. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Yes exactly! Thanks for the reply, I added some pictures to the original post to help explain my issue.

    So basically the issue I'm having is one of organization and keeping track of things. I guess I'm having a hard time keeping track of the abilities as SOs and where they arre in the ability tree because they are two separate entities. So the solution I'm proposing is making them 1 entity (putting all the data onto a MonoBehaviour on the ability tree button), but I guess I'm worried that keeping data like that in a MonoBehaviour is a problem.

    I also love working with SOs and use them any chance I get, but hoping back and forth between the Ability SOs and the Ability Tree canvas is jarring, and I only have a few test abilities right now, I worry about this when I have 100 abilities. 100 SOs all sitting there and me putting in their requirements (which are other ability SOs) and then trying to make sure the tree is lined up correcltty base don that seems like a problem.
     
  4. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    If each MonsterAbilityButton is only ever going to have one MonsterActiveAbilityData at a time, this may be a case where the advantages of SOs are limited. That's because you'll simply be storing the data in the SO that could as well be stored in the MonoB directly. Now, it would make a difference to me if any of the following were true:

    1. More than one MonsterAbilityButton will use the same MonsterActiveAbilityData SO, and you want changes to the values in the SO to affect all MonsterAbilityButtons that use that SO.

    2. A MonsterAbilityButton might contain more than one MonsterActiveAbilityData SO, with the number of them changing during play, something like an inventory that gains and loses contents.

    3. You need a lot of predefined sets of values that can be saved in MonsterActiveAbilityData SOs, and you will be changing which MonsterAbilityButtons get which SOs frequently, either during your design process or when the game is being played.

    If each MonsterAbilityButton has a single MonsterActiveAbilityData SO that it keeps more or less permanently and that it does not share with other MonsterAbilityButtons, then I don't see an advantage to keeping your data in SOs here.
     
    PaperMouseGames likes this.
  5. TimmyTheTerrible

    TimmyTheTerrible

    Joined:
    Feb 18, 2017
    Posts:
    186
    sounds like what you need is a custom editor for your monobehavior that also draws the data from any scriptable objects you have linked to it. that way you never have to leave the inspector to edit the data of the SO after you have linked it. Odin Inpspector has a very useful 'attribute' you can put over a scriptable object variable inside of a monobehavior so that it automatically draws the inspector of the SO inside the monobehavior. Very handy, but with a little more effort you could just do it yourself with custom inspectors.
     
    PaperMouseGames likes this.
  6. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    @Stevens-R-Miller

    Thanks a lot for your help, your advice makes sense. I guess it's a bit tricky because right now, the way I'm keeping track of what abilities a monster has is by making each monster hold a
    List<MonsterActiveAbilityData>
    but the issue with this, is that I also have some SOs that are something like "+10 HP". These very basic general SOs I wanted to sprinkle across the ability tree, but that won't really work, because the monster can't hold 2 of the same SO in its list. So I would need to create a different SO for each instance of "+10 HP" in the ability tree which seems silly.

    Each MonsterAbilityButton will only hold one MonsterActiveAbilityData but if I want some MonsterActiveAbilityData to be "repeatable" throughout the tree it kinda feels like the SO solution could get bloated very easily.

    I think you were 100% right when you described this as analysis paralysis haha I feel like I'm going back and forth with this non-stop!

    @TimmyTheTerrible

    Thanks for the reply! That's actually a brilliant idea! Though I'm not sure how to do it. The only issue this has is that I'm wondering how to handle the example I just gave above. Where I have an SO of something like "+10 HP" and I want mulstiple ability buttons to have that property but since the monster is holding a
    List<MonsterActiveAbilityData>
    then I would need to create a separta SO for each instance of it.

    What about this solution:

    • I could have SOs that hold the templates for each ability (e.g. Fireball move, +10 HP, etc).
    • MonoBehaviors hold these SO templates (maybe with a custom editor so they can be seen and edited from the MonoBehaviour)
    • MonoBehaviours hold the requirements for their specified button
    • When a monster learns an ability, rather than holding a
      List<MonsterActiveAbilityData>
      it creates a new ability of a new class (see below) using the
      MonsterActiveAbilityData
      as a template.
    New Class:
    Code (CSharp):
    1. public class MonsterAbility
    2. {
    3. // This will hold all the info for the ability
    4. // and basically create itself based on an SO template
    5. }
    6.  
    7.      
    This way I only need 1 of each type of SO, rather than having multiple ones for the example of "+10 HP" because the instances are a different class being created from a template.

    I can also edit the properties and requirements directly from the MonoBehaviour which will make things smoother.

    I do have 2 concerns with this sytem though:

    1. Can SOs really be edited in a MonoBehaviour via a custom editor? For some reason I thought this wasn't possible.
    2. If I do this, I'm not sure how I would know that a monster has learned an ability in the tree. How can I know if the created
    MonsterAbility
    corresponds to the
    MonsterActiveAbilityData
    held by the MonsterAbilityButton? Maybe an ID system?

    Thoughts on this would be really appreciated!
     
  7. Elango

    Elango

    Joined:
    Jan 27, 2016
    Posts:
    107
  8. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    I do your second thing all the time, and it's fine. I'll slap a monobehavior "data" script on the button, which serves as the master-copy of that action. If a keystroke also does it, it has to reference the button, ugg, but that's the same amount of work as referencing the SO. The imaginary person yelling "but you have to separate data from representation" is easy to ignore.

    Right now, like you, one of ,my projects has a list of spells as SO's(*), and a list of spells-you-know buttons, each wtih with links to the SO for that spell. I'm wondering why I did that. Buttons-to-spells is a 1-1 relation and isn't gong to change. Now it's 20% more hassle and no gain. I should have put the spell data on the stupid buttons.

    (*) Actually prefabs with only a script, which is the same thing
     
  9. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,913
    First of all, do you know you can click on the SO (in the MonsterAbilityButton inspector) to highlight where it is in the assets folder? It doesn't seem like it's too much of a hassle.
    An editor plugin to show the SO embedded in the inspector would be the fastest fix.

    It would be strange to tie your UI buttons so closely to the ability with a MonoBehaviour. A tree is just parent/child relationships. You can use Unity's hierarchy, or a prefab, to define one. It is also easy to define one in code.

    Your UI should just take the tree data, and automatically instantiate the buttons based on the data, so you wouldn't have to manually position them. If you want some manual placement, your UI can be a template that gets filled in.
     
  10. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    @Elango

    I'll take a look, thanks!

    @Owen-Reynolds

    Yeah in my instance it feels a bit weird because I can't justify not doing it beyond just keeping the UI and the ability data separate for the reason that they are different.

    @Stardog

    I do know about the clicking, the issue is, when we're talking about something like dozens or 100 abilities, each with their own button, the cross checking involved seems really nasty. So, I click on the SO, then I inspect the SO to see what abilities are requirements, but where those requirements placed in the right position on the tree? Did I move them? Gotta make sure, and then it's back and forth for so many abilities.

    This I don't know how to do. I'm manually building the trees on a canvas and placing the buttons manually based on the decisions I'm making with the SO.

    I'm still thinking the idea I came up with a few posts ago might be the best solution, if more work. Where I create abilities from simple SO templetes held in a MonoBehaviour by buttons and MonoBehaviours on the buttons hold the ability requirements in the form of other buttons.
     
  11. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    No, it doesn't _seem_ that way, but when you actually do it it's one more step that can tangle you up. An old trick is to name the SO's so that Unity shows them in the order you want, such as skill01Leap, skill02Hop, ... .

    Sure, if you have a lot of buttons in a regular pattern, and/or the layout may change, and/or the skill selection change in lots of ways. I also do that all the time. But it takes time to code and debug. Esp. if the buttons have a "fun" layout or look different in odd ways. And it's still 10% more difficult to recheck when things go funny.

    One trick I've used is having code lay out the buttons, but then hand-tweak the positions (during Play) and resave (need code for this). So each skill has
    Vector2 screenPos;
    in it. That's easy if the skills are in a text file (so no SO's). For some things which are just text and numbers a text file seems nicer than all of those little Inspector boxes.
     
    Stevens-R-Miller likes this.
  12. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    Dude, don't call these techniques "tricks!" You are giving sound advice about good practices. Wish I saw more code with those kinds of "tricks."