ScriptableObjects for an RPG with multiple units in your team

Let’s say I have an enemy Blob. I can create an EnemyObject SO in the asset menu and tweak his stats, equipped skills, loot and such. And then whenever I want that unit in a certain map, I can just use put it in the “map enemy inventory”, a list for every map that stores what enemies should be in there.

That’s what I have learned by searching a bit about ScriptableObjects and their implementation in units/enemies for an RPG. But that leads to a very obvious problem that I’m struggling to elegantly work around. That implementation only works for units/enemies with fixed stats. I created a Blob preset, so whenever I use that Blob, it will be the exact same lvl 3 Blob with 10 STR and the Blob Attack skill. That’s fine for enemies, but not so much for how I’m trying to implement units in my game. You have stat points, skills to equip, that kind of stuff. So your stats aren’t fixed. You can delete and create new characters at will - and you’re supposed to have multiple ones in your “unit inventory”. So I have a varying number of characters with varying levels, equipped skills, builds, and such. And I’m just lost as to how to implement this properly, considering that this choice might affect a lot of coding I’ll have to do later on, like how I save/load, for example.

I’m pretty sure it’s correct to have a unit inventory, a list that stores my available characters. I’m just not sure what those characters should be - they can’t be SOs, apparently, so they’d have to be just instances of a unit class? That would work, I think, but that’s very vanilla-C-sharpy, and I’m not sure how it’d affect saving/loading, as well as the inconsistency as to how I’m going to implement enemies as ScriptableObjects.

I’m afraid I might have misunderstood or overlooked an important concept that might be leading me the wrong way. I’d really appreciate some feedback. Thanks a lot in advance.

The most important concept about ScriptableObjects (SOs) is that they are custom-shaped blobs of data that you can edit inside the Unity inspector.

The serialized values inside a SO map directly to that created asset on disk, the instance of the SO.

When your game runs, it needs some data. One way to get that data is to hard-code it in the code. That is bad because when you change it, you need to change code. That’s why we have things like a SO to configure existing unchanging shipped code.

Where you are getting into trouble (and I had this same problem at first!) is combining the idea of runtime data that changes, with initial setup data that is constant. ScriptableObjects are only the second part, the static setup part.

Let me offer you an analogy with actual pen-and-paper RPGs:

  • Your program → the AD&D game rules as codified in the AD&D Players Handbook.

  • The CPU your program is on → the Dungeon Master (I know this isn’t exactly accurate… DM/GMs, don’t hate on me!)

  • ScriptableObjects → the tables of players, weapons, enemies, traps, etc. in the Players Handbook, or perhaps in one of the expansion books. You don’t (normally) mark this up for each game. It is READ-ONLY essentially.

  • Runtime data containers → the xerox-copied pieces of worksheet paper that you pass out, such as the Player Sheet, the player’s inventory, the discovered map the players go through, etc.

You still need to make some kind of runtime data containers.

Essentially you want to have SO classes like:

public class MonsterDefinition : ScriptableObject {}
public class MonsterModifiers : ScriptableObject {}

And then when you spin up your MonsterBehavior Monobehavior, you provide it with a MonsterDefinition to use in order to set itself up, vary the stats, etc.

That MonsterBehavior might also have a method that says “You have been modified with this MonsterModifier” and it would know how the modifier impacts its internal runtime stats.

All of that would NOT be changing the root ScriptableObject stats, only the runtime Monobehavior variables for that monster.

Hope that sorta gives you more insight into how things work. SOs are great, but you still have to use them within their design parameters.

3 Likes

@Kurt-Dekker Thanks a lot for the thorough reply! That really helped me understand SOs better!

Huge thanks again, cheers!

ScriptableObjects are essentially your Database. You should treat them as fixed references to information and your runtime data should be separated as unique instances or copies of the database versions. @Kurt-Dekker has a good idea with having a mirror of the Data as Runtime-only, just make sure you actually make deep copies of the information when you clone it so you don’t actually just reference the SO file (for example, array clones).

I have a product called Vault in my sig that you might find to be a head start for handling this kind of thing.

1 Like