Hi there!
I’m now working on a kind of home-brew weapon system and while the process I’m stuck in finding proper way for implementing strategy pattern with UnityEvents inside. But before we dive deep in certain problem let me draw the background.
Well, the game designing’ terms are the following:
-
each weapon belongs to one of defined types: handheld (clubs, knives etc), ballistic (fire with any type of solid bodied projectiles (arrow, bullets, grenades, rockets), energetic (laser etc)
-
due to its type weapon executes shooting method in different way:
-
ballistic ones spawn bullets - GOs which deal damage by colliding the target
-
energetic ones cast a ray for the same purpose
My architectural intentions are:
- I’d like to follow SOLID and MVC principles (eg: I separate weapon’ settings into Scriptable Object, weapon FX - into stand-alone component and so on)
- I use weapon controller on the weapon’ GO (it has references to data and keeps shooting strategy and events)
- and I use weapon operator on the player’ GO which equips weapon and performs shooting by input
- I’m intended to use as much Unity build-in components as possible (so why we use engine if don’t know what stuff it provides and remake a lot of things manually?), so I choose UnityEvents rather then delegates/actions
Thus, here goes the code and the problem - > defining OnShoot event inside WeaponBluprint but not inside ShootingStrategy
using System;
[CreateAssetMenu(fileName = "NewWeaponData")]
[Serializable]
public class WeaponData : ScriptableObject
{
[SerializeField]
float damage;
[SerializeField]
float shootingRange;
[SerializeField]
float ImpactForce;
#region Getters ...#endregion
}
public interface IShootStrategy
{
// This method will be implemented in ShootingStrategyBallistic
void Shoot(Transform muzzle, ProjectileBlueprint projectile);
// This method will be implemented in ShootingStrategyRaycast
void Shoot(Transform muzzle, float damage, float range, float impactForce);
}
public abstract class ShootingStrategy : IShootingStrategy
{
//Ballistic
public virtual void Shoot(Transform muzzle...) {}
//Raycasting
public virtual void Shoot(Transform muzzle...) {}
}
public class ShootingStrategyBallistic : ShootingStrategy
{
public override void Shooting(Transform muzzle, ProjectileBluprint projectile)
{
ProjectileBlueprint temp = (MonoBehaviour).Instantiate(...);
}
}
using UnityEngine;
using UnityEngine.Events;
//TODO: refactor this to WeaponBallistic : WeaponBlueprint
[DisallowMultipleComponent]
public class WeaponBlueprint : MonoBehaviour
{
WeaponData data;
Transform muzzle;
ProjectileBlueprint bullet;
IShootingStrategy shootingStrategy = new ShootingStrategyBallistic();
UnityEvent OnShoot = new UnityEvent();
public void ExecuteShooting()
{
shootingStrategyBallistic.Shooting(muzzle, bullet);
if(OnShoot != null)
OnShoot.Invoke();
}
}
using UnityEngine;
[DisallowMultipleComponent]
public class WeaponOperator : MonoBehaviour
{
WeaponBlueprint initialWeapon;
WeaponBlueprint equippedWeapon;
// ...
void Update()
{
if(equippedWeapon)
{
if(Input.GetButtonDown("Fire1"))
equippedWeapon.ExecuteShooting();
}
}
//...
}
So my main concern is the “man-in-the-middle” abstract ShootingStrategy class. I considered it to hold OnShoot event and trigger in virtual Shooting() and then reuse it as base.Shooting() in all overrides.
But non-MonoBehaviours don’t expose in Inspector and I can’t use visual assigning of listeners to OnShoot. I know I can use AddListener() but it forces me to keep references inside Strategy class - I think this approach is merely out of pattern’s concept. As I mentioned above I can use classical delegates inside abstract but… they’re not a Unity stuff and have no Inspector view also (besides requirement of tracking OnEnable/OnDisable subscribing).
Why I’m in doubt of keeping event inside WeaponBlueprint class… I think it violates SP pattern, may be not - I don’t explain my concern in more specific way…
What I see obviously: at the point of view of common logic, WeaponBlueprint “can shoot” independently of operator’s actions, because it has ExecuteShooting(). But it is out of “close to reality” behaviour of any weapon and - in code’ terms - duplicates ShootingStrategy() with tiny detail in addition → firing the event.
Maybe any of you can point me the way to implement SP pattern properly with the goal of using UnityEvents?
P.S. I’m a professional game designer not a programmer. Unity scripting is a hobby generally and the way to widen my professional field of view. So I’m sorry if my question sounds like how to shoot in elephant’ leg while catching the bird under the see on Moon.