Customizable Skill Components

This article will describe the way that Rogue Tap allows skill effects to be easily customized.

Concept

Rogue Tap has different skills the player can learn and use. Each skill has very different behavior: protect the player, kill enemies, heal the party, etc. When each skill is so different, there is not much code that can be easily shared between the skills. However, Rogue Tap skills can also be upgraded. These upgrades are simple enhancements on the base skill. For example, you can upgrade your attack skill to heal the party based on the damage it does. Because these enhancements are similar across the skills, we can design them so that they are a reusable component (as opposed to coding a new skill for each upgrade).

Overview

Here is a picture of the finished product. The player is using the Fanatic's Charge skill. The skill has been modified by a customizable add-on to emit yellow particles and increase the player's defense while the skill is active.

The charging behavior is controlled by the base Skill code. The particles and defense increase are added by skill add-ons. These add-ons are designed so that they can be added to any skill in Rogue Tap. The rest of this post will explain the design of the Skills and add-ons.

Base Skill Class

Here is the code for the beginning of our base Skill class.

public abstract class Skill : MonoBehaviour { //maintain a list of our addons to avoid performance hits on repeated component queries protected List<SkillAddon> addons; protected virtual void Awake() { addons = new List<SkillAddon>(); //Get the list of addons from unity // this makes it easy to modify the add-ons through the Editor addons.AddRange(GetComponents<SkillAddon>()); } protected virtual void Start() { //optional wiring to the parent skill //we need to set it here because we are linking the add-ons via the Editor //if we instantiated the addons within this class we could link them // to the parent skill more gracefully foreach (SkillAddon add in addons) { add.SetSkill(this); } } }

This beginning handles retrieving and storing the add-ons for our Skill. During the Awake hook method, we query the Skill's GameObject to get all of the configured SkillAddons.

During the Start method, we wire those SkillAddons to the parent Skill. This is not required - but Rogue Tap does it so that the add-ons can perform more complex behaviors (e.g. cancel the parent skill, modify its power, etc.).

Triggering the Add-ons

Inside the Skill class, we use a simple Start and End hook method. This is the entry (and exit) point for all Skills, so it is a good place to notify our add-ons.

//Each Skill implementation class *must* call the base class' StartSkill method //Failure to do so will disable all add-ons. public virtual void StartSkill() { foreach (SkillAddon add in addons) { add.OnSkillStart(); //If you do not like the earlier step of wiring the parent skill on Start // we can do it here like this: // add.OnSkillStart(this); //Doing it this way makes it possible to reuse a single add-on instance // across multiple Skill instances. However, using optimization is likely // more trouble than it is worth - and it breaks the ability to configure // the add-ons purely via the Editor. This should be considered purely a // design preference. } } public virtual void EndSkill() { foreach (SkillAddon add in addons) { add.OnSkillEnd(); } }

Now our Skill class will notify all add-ons when it starts and ends. You can add many more notification hooks for add-ons (e.g. OnDamageDealt, OnTimeElapsed, etc.). They would all follow the same pattern.

SkillAddon Class

Here is the code for our SkillAddon class.

public abstract class SkillAddon : MonoBehaviour { protected Skill skill; public virtual void SetSkill(Skill s) { this.skill = s; } public abstract void OnSkillStart(); public abstract void OnSkillEnd(); }

Now we have a simple base class for all SkillAddons. We need to create an implementation of this class to start doing interesting things.

First, we can create a SkillAddon implementation class that starts a ParticleSystem when the skill starts.

public class ParticlesAddon : SkillAddon { //Here we use the Editor to wire in the particle system to use. public ParticleSystem addonParticles; //But we could also wire it in via initialization. public override void SetSkill(Skill s) { this.addonParticles = s.gameObject.GetComponent<ParticleSystem>(); } public override void OnSkillStart() { this.addonParticles.Play(); } public override void OnSkillEnd() { this.addonParticles.Stop(); } }

Now our ParticlesAddon will start and stop a ParticleSystem each time the Skill starts and stops. Next we want to increase the defense of our player through a new SkillAddon.

public class IncreaseDefenseAddon : SkillAddon { public int defenseIncrease = 5; public override void OnSkillStart() { //Here you will need to work with your specific Character implementation. //In Rogue Tap, each Skill is aware of the Character using it. Character c = this.skill.GetEncounterCharacter(); c.IncreaseDefense(defenseIncrease); } public override void OnSkillEnd() { Character c = this.skill.GetEncounterCharacter(); c.DecreaseDefense(defenseIncrease); } }

With our two SkillAddons coded, we can configure our upgraded skill.

Here you can see the base Skill class (Charge), with the two SkillAddons we created. They are all attached to the same GameObject so that the Charge Skill is able to find the SkillAddons.