#Creating a flexible weapon system
1 messages · Page 1 of 1 (latest)
Thanks, so I tested it out by having a ScriptableModifier (SM) that had a basic float and a ParticleSystem. I can create an array of these SMs on my Weapon script so that I can access their values whenever the weapon attacks.
However, that still seems like it'd be limiting since I'd have to define the exact same variables for each SM. That might be an issue for instance I have an SM called "Fire" which is supposed to add fire damage and a fire effect to an attack.
Okay, so the Fire SM will have a float for additional damage and a Particle System for the fire visuals. But what if I need an SM for just faster attack speed? That would need to be added to the default SM even though not every SM will use it.
I think I might need a different approach in that case.
When reading up on Composition, I see that it's based on the idea of smaller components that each do their own thing regardless of their relationship to other components. That way you can have a Fire component and a Speed component for example.
But I'd need some common way of calling their functions since these'd be different scripts without inheritance.
Initially, I thought of giving them an Interface that has the function ApplyModifier(), that way I can just call that function on any script that uses that interface.
However, that leaves out the ability to assign them in an array in the Inspector, since Interfaces are non-serializable.
Yeah, it's unfortunate that you can't serialize interfaces outside of 3rd party libraries (or managed through serialize references), but it's doable without.
@inner bough What would be a good approach to gathering all of the scripts with the Modifier interface on a gameobject? Just creating a List and populating it on Start with GetComponents<>()?
I generally go with an approach like having a List<Effect> on the things
the effects can have an EffectType and then sort of general float value and tags
Honestly, going with the composition approach, I leave all the behavior in the base type. Meaning, every weapon would have similar implementation, but doesn't necessarily means calling it will apply an effect. The idea is to just do comparisons when you call their methods.
Good implementation there too, instead of checking if it implements a behavior, check if a container of behaviors is populated.
private void OnHit(IEntity target)
{
foreach(Effect effect in Effects)
{
effect.Apply(target);
}
}```
An alternative to the OOP approach you'll often hear suggested is a more ECS one:
Generally if your data fits a consistent shape, it's good to use that shape because it keeps everything very consistent and lets you make good use of polymorphism without having to pay much for it. Often this is worth doing even if a few of the fields go unused, but it does lack extensibility long-term.
If your data isn't consistent, one solution is to flip that knowledge around so that now it's the callers who are aware of what's going on. So in your Shoot method or TakeDamage method or whatever, you collect up the data that you need and handle it when and how it matters in that context, rather than trying to overload the weapon data itself with support for every possible context.
For tags I do use bitwise enums, because even though every weapon may implement 'Speed', that doesn't necessarily mean I want modifiers to affect it further.
how's that look? like as a layer in front to say 'these are the fields that are actually active'?
Pretty much. I mean technically all bullets do have a speed to them, but I don't want you to pick up a 100% bullet speed modifier and then have your rocket launcher shooting torpedos at mach 10
haha and also what do you mean, cause i feel like i would expect a speed mod to affect every weapon with speed? or do you mean like 'but this is a rocket launcher with speed locked to 1, so i want mods not to affect it'
ok yeah
i think i would try to solve that with a bullet type speed multiplier or cap or something but i can see ending up where you did