Now that we have two separate components that need accessed in two different methods that do not pass them between, I feel like a class level variable is appropriate for CooldownStore and Mana. It starts null and gets set on Use. Then TargetAcquired can access it directly from the cached class variable. Is there any reason this would be frowned upon? I would think that it would be fractionally more efficient to do it this way, really.
This would generally be frowned upon, as we really shouldn’t be saving any state within the ScriptableObject. The reason for this is that we want the Ability to be useable by enemies as well. That’s why we pass the User in on Use. Were we to set global state at that point, imagine if two or more characters used the ability at the same time… the state would get overwritten by the next Use statement.
We could consider passing this along as a field in the AbilityData class, though we would be adding fields that are easily re-aquired from the User, and any gain from caching them in AbilityData would likely be lost in the extra time to box the references for passing the data from strategy to strategy.
Ooooooh… that makes more sense… Thanks for that. It’s obvious now that you frame it like that. That’s why they pay you the big bucks
I can’t even imagine the horrendous bugs that would have popped up if I left it implemented as a class level variable… Sheehs.
Well, if you never had the enemies using abilities, you’d never encounter said bugs. We don’t really cover having the enemies use abilities, though it is relatively easy. (You just need to make some adjustments to some of the targetting strategies, for example: if an enemy tries to use the an ability with DelayedClickTargeting, it should just instead immediately return the player’s location as a target.)
Oh I get that. However, I do plan on using this Ability SO system for my main project. I actually REALLY like it. It’s one of the most intuitive things we have done and I’ve been nailing the challenges. I plan on making SO’s for my enemies that are “attack patterns” basically. Some will be very rigid like a soulslike enemy going through their patterns and some will have some randomness thrown in and others will be more reactive to what the player is doing. So using abilities that the player uses will be a big part of that.
However, after thinking about this more… How does editing Ability.cs to have that top level variable differ than any other script we put on player and enemy alike? Don’t they all get their own instances?
Ability.cs is a ScriptableObject. ScriptableObjects, by default, are shared instances… This has some positive functions (like SO’s make GREAT shared blackboards, for example), and negative ones (since every character with a reference has a reference to the same SO, you risk race conditions and data being overwritten).
There is another solution, which does provide a clean copy to every character using it, and that’s to instantiate the ScriptableObject at runtime. This creates another set of problems to manage, however (specifically with stackable objects… if you Instantiate the SO, then try to drag a stackable item on top of another stackable item, they won’t match up and they’ll swap instead!)… As such, it’s still easier to keep the Scriptable Objects stateless for the purposes of Abilities.
I gotcha. I mean I assumed as much given the original issues with my having made those top level variables. It almost seems arbitrary. However, the good outweighs the bad BECAUSE we want that specific functionality.
So basically, every call to Use is live and asynchronous into the same script. It’s actually funny because when I first started using Unity I thought that is how ALL scripts worked. That every GO would need its own script. So weird to come full circle with this.
This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.