I am a beginner in the Unity Engine, but have lots of experience in development (mostly Web/App-Development).
I started to build my own game as a Hobby project and stubbled across ScriptableObjects, which I understand conceptually but am doubting a little bit.
In my concrete example, I want to have different items (100-200), that have basically several Effects and corresponding triggers. The fights are autonomous, so user interaction during the fights is not a relevant szenario:
Effects: What does the effect do (e.g. deal 20 damage, gain 1% crit chance, …)
Trigger: When does the effect happen (e.g. Every 5 seconds, passively, when you take damage)
So an example of a sword could be:
Every 3 seconds, deal 20 damage. Passively grants 5% crit chance. When you crit, deal 30 damage over 4 seconds.
Now, best practices recommend to use ScriptableObjects, but here I am running into the following problem: I would need a seperate ScriptableObject for each possible configuration (e.g. 30DamageOver4SecondsOnCritEffect, 20DamageOn3SecondChargeEffect, …) which sounds totally wrong to me.
So I was hoping to have something like a SerializableField where I can basically select “OnCombatEvent”, then select “OnCrit” and then get to select what Effect should happen and modify the numbers directly in the editor. However here I run into the problem that my Serializable objects need to be super flexible to allow all these combinations (e.g. when I select a Modifier as an effect it would need to ask which stat to modify and how long it lasts). It is basically a dynamic tree structure that I would have to generate dynamically at editor time (and then map accordingly to game logic).
As I do think that my problem is a “common” szenario, I decided to reach out to the experts out there to point out some oversights or misconceptions I am having. Is there any insights or best practices how to tackle such problems?
I think you probably just want to look at some basic inheritance concepts. A base item/effect type, which you make inherited scriptable objects of to have different behaviour.
There’s also [SerializeReference], which lets you serialize plain C# objects with polymorphism within monobehaviours or scriptable objects to do something like this without the need for tons of different scriptable object types. Downside is Unity doesn’t have built in inspector support for it, but addons like Odin Inspector can add it, or I wrote a simple example of a property drawer here: abstract SkillList that can be edited in Inspector - #15 by spiney199
Though with stuff like this, the complexity does start to stack up quickly.
Thank you for your input. I actually tried inheritance in my first attempt and ended up with something like “ChargedItemEffect”, where I inherit a basic Item Effect but add ChargeTime (e.g. 3 seconds to trigger) and a damage and heal value. But as mentioned if I have 2 weapons now with different damage and charge times, I will need 2 different SOs to basically represent the same behaviour (just with different numbers). I will take a look at Odin inspector, maybe it is doing what I am trying to achieve. Maybe I am also just overcomplicating it and should just make lots of SOs.
Thank you again for your insight, really appreciating it.
You may be better of with building custom inspector tools and serialised it from there.
If you got so many variations, you probably will end up in the tangle, if will try use generic tools.
Maybe easier would be to disgn in such way, as it is moddable game, to create variations. So in such way, you can change things post production, withouth needing Unity editor.
I mean having two instances of the same type of scriptable object is entirely normal. That’s the point of scriptable objects: to make instances of objects with different data.
Unless you haven’t realised that you can serialise fields in scriptable objects?
My understanding is, that the different data is on SO level. So assuming I have 2 item effects, one with 10 damage and one with 20 damage, that makes two seperate SOs. This does not sound right to me. I could then just make a 20Damage and a 10Damage class and have the same outcome without using SOs.
The approach I am looking for is having a generic “DamageEffect” and I can just assign a value of 10 for one of the items and 20 for the other in the editor while reusing the same SO and it’s behaviour. Is the point of SOs really to have something like “20Damage” or “10Damage” and so on?
I am not sure if I misunderstood a fundamental part of SOs.
I’m having trouble working out which part you’re misunderstanding honestly.
The point of scriptable objects is to have multiple instances of the same type of asset with different data. That’s really it. It’s about being make actual assets of your C# classes. The Object part of Object Oriented Programming.
If you really want to, you can separate the data from the logic, and have two separate objects for that instead.
But that’s more code for no reason. Ideally you don’t write more code to just have different data. It’s much faster iteration time wise to make things data driven, which is what scriptable objects excel at. Particularly as you can change data on the fly during play mode, which you can’t do when things are hardcoded.
But if you want to do this:
Then you just separate the data from the logic, and use a scriptable object as a pluggable behaviour object that gets fed data from outside.
This is where I feel like the [SerializeReference] approach becomes more required.