How to manage many object's hp bar by ScriptableObject?

So I know there is ScriptableObject recently.

I saw few videos about it. Mostly describe with very primitive example.

But at real project, things are more complicated.

So HP bar problem.

At ScriptableObject videos, it make HP float variable with ScriptableObject and separate it and at another element like UI, effect parts, monitoring its value at Update and do things when it occurs. (But doing with Update is good? Compared to using event way?)

Then how to make many scriptable objects and many UI?
Normally there are many characters, monsters in games, and each of them has HP bar UI element.
Then how to implement them properly with ScriptableObject?

Just make as many floatVariable ScriptableObjects like Char1HP, Char2HP, ? And set its value with each characters, monster’s HP initial value?
And then connect them to each HP bar UI element by referencing?

Or make them just at one ScriptableObject file as list?
Then how to implement monitoring part?
Whenever needed, run foreach (or for) loop syntax of that list and find relevant data?

Or?

  1. Disregard the video and read a health float property from your character and monsters.

The “every variable is a ScriptableObject” concept probably works great with a huge team of people all working on different scenes and gameplay systems at the same time with thousands of manhours invested in the creation of editor scripts and utilities to simplify the process, but it’s a total nightmare for a single developer or small team. Don’t get bogged down in the notion that perfect code architecture is attainable in game development.

2 Likes

Hi @leegod

“Just make as many floatVariable ScriptableObjects like Char1HP, Char2HP, ?”

I haven’t used SO that much, but you could just use it as default data. That way you don’t have to create SO for each unit that has some health value (also, this value in SO wouldn’t be saved either by itself). So you could just have default values stored in SO that you assign to any unit of certain type and then clone it’s values in start:

public class Character : MonoBehaviour
{
    // Drag default data here
    [SerializeField] DefaultValues defaultValues;

    // runtime data
    float hp;

    void Start()
    {
        hp = defaultValues.hp;
    }
}

Thanks!
Yes this is true. Gamers who are customers really don’t cares how the code is. Then important thing is efficient time saving way from maker’s side.

Hm… but for only clone value purpose, why use SO?
Rather then just set it at inspector?
Cuz of scene changing convenience? But Dontdestroyonload exist…
Then what is more good things about SO?

The advantage of scriptable objects in this context is the ability to use the same value in several places. Using scriptable objects this way allows multiple instances of the same type to contain different information, but have almost the same flexible access as say a static.

In an RPG, you will need to display the same values several times, eg in combat, stat adjustment page, equipment adjustment page, anything that requires feedback from adjusting stats. you could go further and put all stat values in a scriptable object, and other scripts just read off it what values they need. This way, removing HP for example, from the scriptable object allows all scripts accessing the scriptable object to show the same data, instead of needing to transfer the values back and forth when the player changes screens.

public class CharStats: ScriptableObject{
    //data and methods
}

a scriptable object

public class StatPageScript:MonoBehaviour{
    //you can now assign this from the inspector
    public CharStats stats;
}

usage in scripts which need the data from the scritable object

There are many uses for scriptable objects, this is just one example. Scriptable objects are in essence a convenient data container not tied to any scene or prefab. This allows you to get creative with its uses which the official page and other tutorials should do a better job demonstrating.

Thanks.

But is that default value related convenience can be get same by using Clone method of class? like,

public class Unit : Character
{
    public object Clone()
    {
        return this.MemberwiseClone();
    } 
    public string eleName;        
    public int hireMoney;        
}

When instantiate monster instances for example, using this Clone method…

hm…but like what you saying, at RPG game, default values always needed for calculation by adjustment of effects, gears, skills, etc…

Then even make instances by Clone, after calculate one character’s strength stat for some duration, and then if need to turning its value back to origin, then another variable need, like STRReserve.

But even for use ScriptableObject, it is same job to make another variables…

I don’t know well.

There is no one way, and no best way, to do a thing in scripting, even less so for a game. If you feel scriptable objects dont fit your needs, nothing is forcing you to use it. Scriptable objects is just one way to accomplish that task.

I actually came from an art background, and I feel many of the lessons i learnt in digital illustrating fit well with scripting. Getting skilled at compartmentalizing your work(photoshop layers) and getting used to the idea of re-writing chunks of code (really just erasing and re-rendering a part of the picture) after youve found a better way to do something, or to an even more drastic measure of restarting a project since your base architecture is not as stable as it needs to be, will help you learn more just from the sheer repetition and practice, and lead to building better and stable projects

If youve written your code such that a chunk can be taken out and put in easily, it wont matter whether you choose scriptable objects or custom classes, since you can try your options out to see what works best. All you truly need to care about in the RPG scenario i mentioned is whether your target scripts have access to the data file.

Just want to chime and say thanks, this was very helpful. I have a big passion for RPGs, but am still (after 7 years of Object-Oriented Programming) baffled by making larger projects (10,000 - 50,000 C# lines, or more).

For my code, it tends to get super complex, not easily maintainable, not easily changeable, etc.
Also another big problem is over 80% of the changes in the future have to go through me, the programmer, instead of my sister (the designer/artist).

I recently started getting into Programming Design Patterns (big thanks to https://gameprogrammingpatterns.com/) and also watched this Unite 2017 talk on ScriptableObjects.

Brief Summary:
From the Unite talk, they create this abstract ScriptableObject class with a generic type , so that you can choose to refer (in the Inspector) to any shared variables you want. Say you wanted a shared integer (like the player’s HP) accessible across your project – you can make a simple ScriptableObject instance that just stores that integer.

My Problem:
But what is really confusing me is how to expand that.
It’s great we have this designer-friendly shared integer value that any script in the project has immediate access to, however…

1. Where should I incorporate HP clamping? (Making sure the HP stays within the range of 0 to their MaxHP)

  • This doesn’t apply to all variable types, just maybe integers and floats, so this definitely doesn’t belong in the abstract superclass with the generic type .

2. Where does the MaxHP get stored, if this is just its own integer in a single ScriptableObject?

  • Does it break code re-usability if I start using more custom data types to store HP, stats and etc., or make the code more stiff, relying on concrete data types?

3. Where should my damage function live? Should it be on my MobController (MonoBehaviour class)?

  • But then I can’t easily call damage on the HP ScriptableObject…
  • I also wouldn’t want to put damage logic in a general integer/float ScriptableObject class, since it’s specific to HP (not all integers/floats).
    For context, this function would be used to calculate damage based on a variety of factors that may change in the future, based on the type of attack, element (if any, like fire, water, etc.), the 2 mobs involved, etc.

I know this is all subject to my own design, and probably the game systems that I actually want to make. But I’d seriously appreciate any insight or advice!

I’ve been wanting an answer for this myself after learning about how scriptable objects can be used as event signals from Mister Taft’s videos. Quite simply, this scriptable object signal system falls apart the moment you need to use it for cloned enemies and their health / health bars. Judging by this discussion, my conclusion that it’s a useless way of event signaling doesn’t seem far off.

1.You can put HP clamping inside an OnValidate function in the SO. Unless you are talking about runtime.
2. MaxHP could be in the SO, unless it has to be unique per character. Then it has to be an instance, not read from the asset.

I don’t know about an event system, but for an HP system it’s useless, unless you only have one character.

Figured – yeah, I meant for me; There are cloned enemies all over the place that need HP and HP views, and this method does nothing for that. I found another event system somewhere that Will Miller posted on his unity blog, and someone in the comments improved upon to have a very small footprint, which I’ll have to look up.