#De-spaghettiing power-ups
1 messages · Page 1 of 1 (latest)
Hang on this is gonna get long let's move to a thread
Instead of saying things like "the instantiated object" and "on key press" please be specific. Use game words, not code words. Things like "When the player touches a power-up, their gun can shoot a different kind of bullet"
Saying things like "The instance of the prefab" is ambiguous. There are many prefabs involved in this situation
Use nouns, not descriptions
when the player presses a key, an effect is applied that makes it so the gun can shoot a different kind of bullet until the effect expires
Okay, so these are more like League of Legends-style active abilities? You hit a button and it applies for a while?
yes exactly
So then what was this about colliding with something in the world?
the bullet that gets spawned when the effect is active applies a slow effect when it hits other players
Okay, so earlier when I asked what the effects were and you said they changed what attack was used, that wasn't actually what they were for
The effects are what the bullets do to the things they hit
ah, I have called buffs and debuffs "effects" as a general term
As I said, right now, pick one thing
Just pick your favorite one
And then we go through the process on that one
The effect that changes the bullet is a buff
Okay, so, you want to discuss the buff here?
Which is basically "For the next N frames, whenever you spawn a bullet, spawn this other one instead"
yes
Since you refuse to give me concrete examples like I asked for, we are going to assume that this buff is called BeegBullet and it simply a larger prefab
So, let's talk through how a player might enable BeegBullet and how it uses code
i admire you for your patience, hard to help someone who has no clue what you are talking about and asking for
And I kept saying pick one we can discuss in specifics because being general actively makes it harder to discuss concepts
and you never did
so now we're using the ability BeegBullet
which makes the bullet very large
I did, the confusion came because I called both things effects
I named the effect I want to discuss, I explained what I wanted it to do and I posted the code in the channel
I'm not sure what I'm doing wrong to not be concrete here
Let's discuss the lifetime of BeegBullet.
Let's say it's a buff that is activated by pressing R.
Let's say it lasts five seconds, and has a cooldown of ten seconds after that.
Its effect is that when the player attacks, instead of normalBulletPrefab, they spawn a beegBulletPrefab
yes
So, here's what we need.
We need some sort of data structure that can hold the information that we need.
We need a way to "apply" the data structure and keep track of time so we know when to stop
yes I have a list for that, that part already works
In the first case, this seems kind of like we want a "Database" of variables that are tied to a specific effect. This sounds, to me, like a use case for ScriptableObjects
This will let us define a file in the project that contains some data. Something like this:
[CreateAssetMenu(menuName = "Buffs/BulletChangeBuff")]
public class BulletChangeBuff: ScriptableObject {
public float activeDuration;
public float cooldownDuration;
public Bullet newBulletPrefab;
}
You will then be able to create an asset in your project from the right click menu. You can create one called BeegBullet, and set its values to 5, 10, and your beegBulletPrefab respectively.
On the player, you could have a field akin to:
public BulletChangeBuff abilityR;
And you could drag in that BeegBullet.asset you just made
Your player now has a reference to this database that tells it which ability is equipped to that button.
You follow me so far?
yes, that sounds a lot like what I already have, which is separate classes like that for each buff and debuff
now imagine you have 100 "buffs", so you want to have a separate class for each?
that's sounds like a terrible architecture to maintain
rob himself said to do it that way
Okay and what makes instances of classes
i dont think so
(tbh i thought there was a good reason for multiple types of an Effect)
These classes need to be instantiated somewhere. The ScriptableObject is also a class, and instances are created through the asset menu, the right clicking
There probably are going to need to be multiple types of effect SOs, but we'll get to that later
I have an effect controller with a StartEffect() method that takes the effect name and instantiates it
yes and that may be easiest for them to manage too
Well, the ScriptableObject method completely replaces that, so whatever shenanigans you're doing to get a class by name is no longer necessary
ok
The SO I defined above can handle any effect that involves changing out the bullet prefab, with just one class. Is there any other properties you can think of that might be useful to have on the file that defines one of these buffs?
by one of these do you mean bullet changing buffs?
Yes, any effect that involves changing out the bullet prefab
well currently I have this abstract class
public abstract class EffectObject
{
public abstract EffectType EffectType { get; }
public abstract EffectAlignment EffectAlignment { get; }
public abstract int EffectAuthorityLevel { get; }
public abstract float EffectDuration { get; set; }
public virtual void StartEffect(MonoBehaviour mono) { return; }
public virtual void StopEffect() { return; }
}```
that all effects inherit from so those properties and methods should probably exist if they go in the SO
Okay, so, we have duration already. EffectAuthorityLevel seems like a priority sytem, where a conflicting effect of higher priority just kicks the old one off and takes its place, right?
StartEffect(MonoBehaviour mono) is blasphemy and should be purged.
the authority level is meant to be the difficulty of dispelling the effect
I think that came from the "how to start a coroutine" discussion done a bit ago
multiple effects of the same kind can apply in parallel with their own timers
yes
Yeah but why does the effect need to start a coroutine
but we'll get to timing later. Spoiler: It's gonna be on the thing using the effect
for something like a burn effect that deals damage every second
or rather, it deals x damage every y seconds
hang on I'll show you the burn effect
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
I think this seems like a different kind of thing than the one we're working with at the moment
Yeah we're separating the responsibilities of "Define a thing" from "Do a thing"
The general principle is that your effects should have a file that defines what they are, and the MonoBehaviours in your game actually apply the effects.
For practical examples, we return to BeegBullet
If your player has a set of fields like:
private BulletChangeBuff activeBulletBuff;
private float bulletBuffDuration;
Along with the above abilityR field:
void OnPressRButton(){
activeBulletBuff = abilityR;
bulletBuffDuration = activeBulletBuff.activeDuration;
}
The duration can tick down in Update:
void Update(){
...
if (activeBulletBuff != null){
bulletBuffDuration -= Time.deltaTime;
if (bulletBuffDuration <= 0) {
activeBulletBuff = null;
}
}
...
And when you shoot your bullet:
if (activeBulletBuff != null){
bulletFired = Instantiate(activeBulletBuff.newBulletPrefab, ... );
else
bulletFired = Instantiate(normalBulletPrefab, ... );
Does this make sense
With this you can have an infinite number of bullet buffs without ever having to write new code
You just make a new SO instance
The if-else chain you had before is no longer a thing
well ok but that chain is not just for bullet buffs
Instead, if looks like now I will have to do this private BulletChangeBuff activeBulletBuff; but for every possible kind of effect?
You'll need to do a bit of thinking and find ways to group things.
For example, the burn effect you mentioned, could be something like a PeriodicEffect that has a duration, a frequency, and the thing it does. Like a healOverTime would add health, while burn would decrease it
The important thing is to try to keep data flowing one way. You have a read-only effect instance that defines some data. The thing that triggers the effect reads from it and sets up its own fields to handle the timing, you don't put that object back into the effect data
so basically, I would group them by the arguments in the constructors used here?
Seems like a good plan
And you can make a shared parent class since they all seem to have effectDuration in common
And make each of these sub-classes of that
also a SO?
Yes, SOs can have inheritence just like a normal class
The core idea is that instead of having UnityEvents and invokes, you'd have an object simply hold a reference to one of these things and modify itself. Like, Slow and Quick for example, could be something along the lines of:
if (movementAlteringEffect != null) {
movementVector = movementAlteringEffect.GetNewMovement(movementVector);
}
But it could also be something like this instead:
void OnGainEffect(StatAlteringEffect eff){
currentStr = originalStr * eff.strMult;
currentDef = originalDef * eff.defMult;
currentSpd = originalSpd * eff.spdMult;
...
}
and your slow effect would just have 1s for everything and a 0.8 for speed or something
There are multiple avenues, some more general than others, but the important thing is to consider what kinds of things you want to apply. It's okay to have a catch-all "Effect" term, but really drill down into each category of things. Categories can have sub-categories that can have even more sub-categories
well I'm handling different stats in different controllers so I might have to group by affected controllers too
but also how do I actually apply these effects then
What are all the ways an effect can be applied? In gameplay terms.
We've seen "Hit the ability button" and "get hit by a bullet"
what others are there
it could also be "get hit by a skill" which I guess is the same as "get hit by a bullet" practically but it could also be something like "picking up the flag" and possibly "when game timer is low" but I'm not sure about that yet tbh
I haven't thought about how to handle those last parts yet, but it might make sense to handle it like a debuff instead of creating a new system
Currently, to apply an effect, I have the EffectController instantiate the effect, call the StartEffect method on it and put it in a list to keep a reference and count down the timers
thats this thing for reference https://pastebin.com/R7zLQ5a3
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.