'how do i go about cleanly implementing varying behaviours extending the same abstract method while simultaneously needing varying parameters?
i've just gotten around to using Scriptable Objects inside unity. and i've been wanting to use it to "drag and drop" different logic into another abstract class that will be extended upon later down the line.
more precisely what i want to do is to have a weapon class that can target other positions in the game world, but i need to do so slightly differently based on what kind of weapon it's going to be. for example, maybe i want the weapon to target enemies randomly on a global scale (think lightning strikes), or maybe i want to target the closest enemy from my players position(simple). or maybe i want to target multiple enemies in a certain area around me (damaging aura, shotguns), etc, etc.
now the problem isn't with the creating the logic on how to get these positions and such. the problem that i have is that i want to put all these differing ways to target enemies in each their own scriptable objects, so i can essentially drag and drop in my weapon what kind of targeting type the weapon will use. so what i need is to be able to call different logic with the same method name, let's say we make it an abstract named "Target()", now i can extend the abstract class with the function Target() everytime i need a different targeting implementation, and be able to call it without hassle, right?
well... thats not all. the thing is, i may need differing parameters for different targeting implementations. so yes i can overload the abstract method, create new overloads for all the different parameters i may use. but i don't think this is very practical, because if i do that, i'll at most end up implementing only one of those overloads in each class. and i don't feel like this is a good way to solve the problem.
another solution i've been thinking about is creating just one abstract method with all the parameters it could possibly need, and use only the parameters i need, for the particular implementation. though that still has me needing to assign all parameters all the time even when i won't use em. so instead those parameters i've been thinking of sending a struct. assign only the data i need within it and then send it off.
p.s after writing this post out, i'd like to add that i could make the parameter generic as well, and have a variety of structs to send, as to avoid unfilled/unused variables, your thoughts?
now ultimately my question is. is all of this okay? or 'good practice'. and if it's not, what should i do instead?
Thank you!
Code Bits:
here is the relevant part of my weapon class as it is now, i'm using Enums (TargetingTypes) to assign the weapon what kind of targeting (method) it will pick.
public void DetermineTargetingMode()
{
switch (targetingType)
{
case TargetingTypes.AutoTargetClosest:
TargetClosest();
break;
case TargetingTypes.RandomTarget:
TargetRandom();
break;
case TargetingTypes.RandomTargetWithinRange:
TargetRandomWithinArea();
break;
case TargetingTypes.AllTargetsWitninRange:
TargetAllWithinArea();
break;
}
}
public void TargetClosest() //returns a single target
{
FireWeapon(TargetingTools.GiveClosestTargetPoisitonFromArray(transformCache.position, WaveManager.instance.enemiesAlive.ToArray()));
}
public void TargetRandom() //returns a single target
{
FireWeapon(TargetingTools.GiveRandomTargetPositionFromArray(WaveManager.instance.enemiesAlive.ToArray()));
}
public void TargetRandomWithinArea() //returns a single target
{
FireWeapon(TargetingTools.GiveRandomTargetPositionFromArrayWithinRange(transformCache.position, WaveManager.instance.enemiesAlive.ToArray(), areaOfEffect));
}
public void TargetAllWithinArea() //returns an array of targets
{
FireWeapon(TargetingTools.GiveAllTargetPositionsWithinRange(transformCache.position, WaveManager.instance.enemiesAlive.ToArray(), areaOfEffect));
}
and although this works functionally fine, i feel it could be a lot more extendable/flexible. for example i think i could offload the content of each of these functions to seperate scriptableObjects like this
My solution:
(the abstract scriptable object)
public abstract class TargetingType : ScriptableObject
{
public abstract T Target<T>(TargetingData data);
}
the scriptable object where i would implement the 'target the closest single enemy' logic
public class TargetClosestSingleEntity : TargetingType
{
public override T Target<T>(TargetingData data) //maybe make the parameter generic as well so i can send more 'appropriate' and 'tailored' structs, avoiding unfilled/unused variables what do you guys think?
{
var dataToReturn = "xD";
return (T)Convert.ChangeType(dataToReturn, typeof(T));
}
}
and (pseudo)implemented the weapon class would look like so
public abstract class WeaponBases : MonoBehaviour, IWeapon
{
public WeaponConfiguration configuration; //just some data container, cooldown of weapon, damage source, name, etc
public TargetingType targetingType; // assigned what type in unity editor, let's just imagine i slotted the 'close single target' one.
private TargetingData targetingData;
public abstract void FireWeapon(Transform target); // where the class extending this will decide what to do with the target.
public abstract void FireWeapon(Transform[] targets); // ditto, but if an array gets returned.
public void AimAndFire() //placeholder names for now
{
FireWeapon(targetingType.Target(targetingData)); /* let's just imagine i've magically assigned the data to targetingData as needed,
* Target() must either return either a transform or a transform[]. */
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
