A fully commented architecture which hopefully helps me and others

As mentioned before, I found that Ben went way too fast here and should have used a step-wise zoom-out instead of a holistic and way too abstract zoom-in approach.

I still can’t wrap my head around this, but maybe things will start to become clear if I read the below a hundred times or so. Hopefully it will help others too.

Must fully understand this before I move on. Sigh.

Special Abilities
namespace RPG.Characters {
// All special abilities use the struct AbilityUseParams, basically a data package containing a target (anything that adheres to the IDamageable interface, which demands that the target has implemented at least a TakeDamage method; note that this is a great way to group/address components without reverting to layers or tags) and baseDamage.
    public struct AbilityUseParams
    {
        public IDamageable target;
        public float baseDamage;

        // So-called constructor        
        public AbilityUseParams(IDamageable target, float baseDamage)
        {
            this.target = target;
            this.baseDamage = baseDamage;
        }
    }

// All special abilities have an energyCost in the editor, under a new heading.
    public abstract class SpecialAbility : ScriptableObject
    {
        [Header("Special Ability General")]
        [SerializeField] float energyCost = 10f;

// All special abilities adhere to the ISpecialAbility interface “behaviour” (see below), which demands that the ability has a Use method (which deals the damage in question). This interface is protected and can only be accessed by children.
        protected ISpecialAbility behaviour;

// All special abilities have an AttachComponentTo method, which attaches a PowerAttackBehaviour to a game object (an enemy or player). In this method, the “behaviour” variable is set to this behaviour.
        abstract public void AttachComponentTo(GameObject gameObjectToAttachTo);

// We can find the above-mentioned Use method (which deals the damage in question) in the now attached PowerAttackBehaviour. To actually deal the damage, we need to input the target and the baseDamage using the above-mentioned struct.
        public void Use(AbilityUseParams useParams)
        {
            behaviour.Use(useParams);
        }

// Users of special abilities can access their costs via this method in the superclass. We could also have done this by setting a public float as a variable, but then any changes made from the outside would be directly written to the special attack scriptable objects, which we don’t want (everything would still work, but it would be very confusing to see the values of these suddenly change).
        public float GetEnergyCost()
        {
            return energyCost;
        }
    }

// The IspecialAbility interface demands that the ability has a Use method (which deals the damage in question).
    public interface ISpecialAbility
    {
        void Use(AbilityUseParams useParams);
    }

}

Power Attack Configs
// All power attacks are a kind of special ability. They have an extraDamage in the editor, under a new heading.
namespace RPG.Characters
{
    [CreateAssetMenu(menuName = ("RPG/Special Ability/Power Attack"))]
    public class PowerAttackConfig : SpecialAbility
    {
        [Header("Power Attack Specific")]
        [SerializeField] float extraDamage = 10f;

// Override whatever was in the superclass (SpecialAbility).In this case there is nothing in the superclass that should be overridden, but you still need this word. This is the actual AttachComponentTo method which we promised to define in the superclass (SpecialAbility). It attaches a PowerAttackBehaviour to a game object (an enemy or player). In this method, the “behaviour” variable is set to this behaviour.
        public override void AttachComponentTo(GameObject gameObjectToAttachTo)
        {
            var behaviourComponent = gameObjectToAttachTo.AddComponent<PowerAttackBehaviour>();
            // this = SpecialAbilityConfig
            behaviourComponent.SetConfig(this);
            behaviour = behaviourComponent;
        }

// Users of power attacks can access their damage via this method in the superclass. Again, we could also have done this by setting a public float as a variable, but then any changes made from the outside would be directly written to the power attack scriptable objects, which we don’t want (everything would still work, but it would be very confusing to see the values of these suddenly change).
        public float GetExtraDamage()
        {
            return extraDamage;
        }
    }
}

PowerAttackBehaviours
namespace RPG.Characters
{
// All power attack behaviours adhere to the ISpecialAbility interface “behaviour” (see above), which demands that the ability has a Use method (which deals the damage in question).
    public class PowerAttackBehaviour : MonoBehaviour, ISpecialAbility
    {
// The actual power attack we belong to is called config
        PowerAttackConfig config;

// With this method, we can tell the behaviour which config we should adhere to from the actual power attack
        public void SetConfig(PowerAttackConfig configToSet)
        {
            this.config = configToSet;
        }

        // Use this for initialization
        void Start()
        {
            print("Power Attack Behaviour attached to " + gameObject.name);
        }

        // Update is called once per frame
        void Update()
        {

        }

// The Use method (which deals the damage in question) in this attached PowerAttackBehaviour. To actually deal the damage, we need to input the target and the baseDamage using the above-mentioned struct.
        public void Use(AbilityUseParams useParams)
        {
            print("Power attack used by: " + gameObject.name);
            float damageToDeal = useParams.baseDamage + config.GetExtraDamage();
            useParams.target.TakeDamage(damageToDeal);
        }
    }
}
1 Like

Privacy & Terms