Tactics RPG Ability Effects

Over the past several lessons we have implemented tempory implementations for the effect of an ability, such as manually implementing an attack damage estimation and application of the damage itself. In this lesson we will be providing a much more complete implementation which allows for very different effects ranging from applying damage to inflicting status ailments and which supports just about anything you would want to do.

Bugs & Fixes

There are a few bug fixes in this check-in, several of which were pointed out by my readers – thanks!

  • TransformAnimationExtensions – was referencing MoveTo in the ScaleTo overloaded methods.
  • Point – should implement IEquatable in order to use the correct Equals method
  • BlindStatusEffect – needed to inherit from StatusEffect

Refactoring

I did a little refactoring which I don’t plan to cover in detail since you can refer to the code in the repository, but I want to point it out. In HitRate I modified the signature of the Calculate method.

// This was the old signature
public abstract int Calculate (Unit attacker, Unit target);

// This is the new signature
public abstract int Calculate (Tile target);

I decided I didn’t need to pass the attacker parameter because the “ability” itself will always be in the hierarchy of its own Unit and therefore can get (and optionally cache) its own reference without me needing to pass it along. I changed the target parameter from a Unit to a Tile because you can get a reference to the unit which occupies a tile from the tile, but there might be occasions where I actually want an ability to have an effect which works with something that isn’t a Unit. For example, maybe one ability allows you to place traps on empty tiles. I wanted this idea to be reflected through all of the ability related code.

Base Ability Effect

Let’s begin by creating a new abstract base class called BaseAbilityEffect and place it in a Scripts/View Model Component/Ability/Effects/ directory. This script will provide two functions – it will finish filling out the second part of our Hit Success Indicator with a damage “prediction” and it will provide an interface through which we can apply whatever it is that the ability actually does.

using UnityEngine;
using System.Collections;

public abstract class BaseAbilityEffect : MonoBehaviour
{
	public abstract int Predict (Tile target);
	public abstract void Apply (Tile target);
}

Damage Ability Effect

Our first concrete subclass will be used in many abilities, and is one which applies damage to a targeted unit. Create a script named DamageAbilityEffect and place it in the same folder as the base class.

I created this script while referencing the “Final Fantasy Tactics Advance: Mechanics Guide” by TFergusson so that I felt fairly confident I was going to be able to support a very complex damage algorithm (not that one is always needed). The algorithm in that guide is twenty steps long, with mutliple sub-steps in most of them. I don’t specifically outline each step of the process, but because of the way I architected this component, you could include each of those steps and more.

The damage algorithm starts with the Predict method. The Apply method actually uses the value calculated by the prediction and then merely adds a little random variance. The final calculated value is subtracted from the target’s hit points.

The “trick” to supporting large amounts of control in this algorithm is in the use of notifications which pass along a list of Value Modifiers in an argument. Any object can listen to those notifications and add a modifier at any step thanks to the sorting order ability of the modifier objects. The great thing is that my script doesn’t need to know about any of the objects which create special cases of value modification. For example, if I got around to implementing a feature where you carry along mission items, and by possessing a mission item you got an offensive bonus, then I can allow that mission item to listen for the appropriate notification, insert a value modifer into the list, and I am done. I can add (or remove) these kinds of little rules to my hearts content all without ever needing to modify this script in any way.

As an example, when I want to get the “base attack” stat of the attacker, I call my “GetStat” method. There I create an Info object which holds relevant information such as who the attacker and defender are, as well as that List of Value Modifiers I mentioned. The “GetAttackNotification” will be posted along with the info object. Any object which listens to the notification can then insert new modifiers into the list. Next, the list of value modifiers are sorted and applied to a value.

The very first modifier in the list would be the true “base attack stat” which would come from either the physical or magical attack stat of the unit, depending on what kind of ability is being used. It would be “added” to the formula with an “Add Value Modifier” to take the stat from zero to whatever it should be. Most other changes would multiply the final result with a “Mult Value Modifier”. Some of these modifiers could include:

  1. Mission item bonus
  2. Support checks like Turbo MP or Doublehand
  3. Status checks like Berserk or Boost
  4. Equipment checks

The same basic idea is utilized in each step of the process. When we get the defense stat for example, we can also check Mission Items, Support checks and Status checks, but keep in mind that each implementor would be different based on whether the attacker or defender has a particular status. For example, if the attacker is under Frog status then the attack would become weaker as a result of reducing the attack stat, but if the defender is under Frog status then the attack would become stronger as a result of reducing the defense stat.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class DamageAbilityEffect : BaseAbilityEffect 
{
	#region Consts & Notifications
	public const string GetAttackNotification = "DamageAbilityEffect.GetAttackNotification";
	public const string GetDefenseNotification = "DamageAbilityEffect.GetDefenseNotification";
	public const string GetPowerNotification = "DamageAbilityEffect.GetPowerNotification";
	public const string TweakDamageNotification = "DamageAbilityEffect.TweakDamageNotification";

	const int minDamage = -999;
	const int maxDamage = 999;
	#endregion

	#region Public
	public override int Predict (Tile target)
	{
		Unit attacker = GetComponentInParent<Unit>();
		Unit defender = target.content.GetComponent<Unit>();

		// Get the attackers base attack stat considering
		// mission items, support check, status check, and equipment, etc
		int attack = GetStat(attacker, defender, GetAttackNotification, 0);

		// Get the targets base defense stat considering
		// mission items, support check, status check, and equipment, etc
		int defense = GetStat(attacker, defender, GetDefenseNotification, 0);

		// Calculate base damage
		int damage = attack - (defense / 2);
		damage = Mathf.Max(damage, 1);

		// Get the abilities power stat considering possible variations
		int power = GetStat(attacker, defender, GetPowerNotification, 0);

		// Apply power bonus
		damage = power * damage / 100;
		damage = Mathf.Max(damage, 1);

		// Tweak the damage based on a variety of other checks like
		// Elemental damage, Critical Hits, Damage multipliers, etc.
		damage = GetStat(attacker, defender, TweakDamageNotification, damage);

		// Clamp the damage to a range
		damage = Mathf.Clamp(damage, minDamage, maxDamage);
		return damage;
	}
	
	public override void Apply (Tile target)
	{
		Unit defender = target.content.GetComponent<Unit>();

		// Start with the predicted damage value
		int value = Predict(target);

		// Add some random variance
		value *= Mathf.FloorToInt(UnityEngine.Random.Range(0.9f, 1.1f));

		// Clamp the damage to a range
		value = Mathf.Clamp(value, minDamage, maxDamage);

		// Apply the damage to the target
		Stats s = defender.GetComponent<Stats>();
		s[StatTypes.HP] -= value;
	}
	#endregion
	
	#region Private
	int GetStat (Unit attacker, Unit target, string notification, int startValue)
	{
		var mods = new List<ValueModifier>();
		var info = new Info<Unit, Unit, List<ValueModifier>>(attacker, target, mods);
		this.PostNotification(notification, info);
		mods.Sort();

		float value = startValue;
		for (int i = 0; i < mods.Count; ++i)
			value = mods[i].Modify(startValue, value);

		int retValue = Mathf.FloorToInt(value);
		retValue = Mathf.Clamp(retValue, minDamage, maxDamage);
		return retValue;
	}
	#endregion
}

Inflict Ability Effect

I wanted to show a variation in ability effects so the second effect I created doesn’t do any damage at all, it merely applies a status effect. Create a new script named InflictAbilityEffect in the same folder as the base class.

In the Status Effects post I discussed some architectural ideas for implementing an “Add Status Feature” which is quite similar to an “Inflict Ability Effect”. In that post I used a generic implementation which would require a new subclass for every type of status we would want to add. Now that I am doing almost the exact same thing with Ability Effects, I feel that the “explosion of classes” problem I was worried about is a definite issue. This time I decided to go ahead and try the reflection route. I begin with the use of a string (since it is serializeable and you can use it in the editor) which maps to a Type of StatusEffect. I used some error handling to verify that we both have a legitimate class type and that the type is also a subclass of StatusEffect. If both of those are true, I am able to generate and invoke the appropriate Generic Method dynamically. I’m still not crazy about reflection, but at least I can reuse this single component for any StatusEffect that I might want to add.

using UnityEngine;
using System.Collections;
using System;
using System.Reflection;

public class InflictAbilityEffect : BaseAbilityEffect 
{
	public string statusName;
	public int duration;

	public override int Predict (Tile target)
	{
		return 0;
	}

	public override void Apply (Tile target)
	{
		Type statusType = Type.GetType(statusName);
		if (statusType == null || !statusType.IsSubclassOf(typeof(StatusEffect)))
		{
			Debug.LogError("Invalid Status Type");
			return;
		}

		MethodInfo mi = typeof(Status).GetMethod("Add");
		Type[] types = new Type[]{ statusType, typeof(DurationStatusCondition) };
		MethodInfo constructed = mi.MakeGenericMethod(types);

		Status status = target.content.GetComponent<Status>();
		object retValue = constructed.Invoke(status, null);

		DurationStatusCondition condition = retValue as DurationStatusCondition;
		condition.duration = duration;
	}
}

Base Ability Power

When we created the Damage Ability Effect, I mentioned ability “power” and also created several notifications that nothing listens to yet. Create a new script named BaseAbilityPower and place it in the Scripts/View Model Component/Ability/Power/ directory.

This base class implementation will handle subscribing to the various notifications, and in the handler methods will add value modifiers with values based on abstract methods which will need to be implemented by the concrete subclasses.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public abstract class BaseAbilityPower : MonoBehaviour
{
	protected abstract int GetBaseAttack ();
	protected abstract int GetBaseDefense (Unit target);
	protected abstract int GetPower ();

	void OnEnable ()
	{
		this.AddObserver(OnGetBaseAttack, DamageAbilityEffect.GetAttackNotification);
		this.AddObserver(OnGetBaseDefense, DamageAbilityEffect.GetDefenseNotification);
		this.AddObserver(OnGetPower, DamageAbilityEffect.GetPowerNotification);
	}

	void OnDisable ()
	{
		this.RemoveObserver(OnGetBaseAttack, DamageAbilityEffect.GetAttackNotification);
		this.RemoveObserver(OnGetBaseDefense, DamageAbilityEffect.GetDefenseNotification);
		this.RemoveObserver(OnGetPower, DamageAbilityEffect.GetPowerNotification);
	}

	void OnGetBaseAttack (object sender, object args)
	{
		var info = args as Info<Unit, Unit, List<ValueModifier>>;
		if (info.arg0 != GetComponentInParent<Unit>())
			return;

		AddValueModifier mod = new AddValueModifier(0, GetBaseAttack());
		info.arg2.Add( mod );
	}

	void OnGetBaseDefense (object sender, object args)
	{
		var info = args as Info<Unit, Unit, List<ValueModifier>>;
		if (info.arg0 != GetComponentInParent<Unit>())
			return;
		
		AddValueModifier mod = new AddValueModifier(0, GetBaseDefense(info.arg1));
		info.arg2.Add( mod );
	}

	void OnGetPower (object sender, object args)
	{
		var info = args as Info<Unit, Unit, List<ValueModifier>>;
		if (info.arg0 != GetComponentInParent<Unit>())
			return;
		
		AddValueModifier mod = new AddValueModifier(0, GetPower());
		info.arg2.Add( mod );
	}
}

Physical Ability Power

Create a new script named PhysicalAbilityPower in the same directory as the base class. This script will (obviously) provide the implementation for abilities which are physical in nature. It references the Attack stat of the attacker and the Defense stat of the defender. It also holds an int field called level which can be different for each ability. Weak units performing a strong ability might still do a lot of damage in this way. Along the same vein, a unit can have multiple abilities that might do different amounts of damage thanks to the power “level”, even though the attack stat hasn’t changed.

using UnityEngine;
using System.Collections;

public class PhysicalAbilityPower : BaseAbilityPower 
{
	public int level;
	
	protected override int GetBaseAttack ()
	{
		return GetComponentInParent<Stats>()[StatTypes.ATK];
	}

	protected override int GetBaseDefense (Unit target)
	{
		return target.GetComponent<Stats>()[StatTypes.DEF];
	}
	
	protected override int GetPower ()
	{
		return level;
	}
}

Magical Ability Power

The magical ability power is very much like the physical one, but it uses the magic attack and magic defense stats instead:

using UnityEngine;
using System.Collections;

public class MagicalAbilityPower : BaseAbilityPower
{
	public int level;

	protected override int GetBaseAttack ()
	{
		return GetComponentInParent<Stats>()[StatTypes.MAT];
	}

	protected override int GetBaseDefense (Unit target)
	{
		return target.GetComponent<Stats>()[StatTypes.MDF];
	}

	protected override int GetPower ()
	{
		return level;
	}
}

Weapon Ability Power

For a third and final variation of the ability power types, I will cause the power level to be determined by the equipped weapon. If there is no weapon equipped, it will fallback to using the default attack strength of whatever job the unit has taken.

using UnityEngine;
using System.Collections;

public class WeaponAbilityPower : BaseAbilityPower 
{
	protected override int GetBaseAttack ()
	{
		return GetComponentInParent<Stats>()[StatTypes.ATK];
	}

	protected override int GetBaseDefense (Unit target)
	{
		return target.GetComponent<Stats>()[StatTypes.DEF];
	}
	
	protected override int GetPower ()
	{
		int power = PowerFromEquippedWeapon();
		return power > 0 ? power : UnarmedPower();
	}

	int PowerFromEquippedWeapon ()
	{
		int power = 0;
		Equipment eq = GetComponentInParent<Equipment>();
		Equippable item = eq.GetItem(EquipSlots.Primary);
		StatModifierFeature[] features = item.GetComponentsInChildren<StatModifierFeature>();

		for (int i = 0; i < features.Length; ++i)
		{
			if (features[i].type == StatTypes.ATK)
				power += features[i].amount;
		}
		
		return power;
	}

	int UnarmedPower ()
	{
		Job job = GetComponentInParent<Job>();
		for (int i = 0; i < Job.statOrder.Length; ++i)
		{
			if (Job.statOrder[i] == StatTypes.ATK)
				return job.baseStats[i];
		}
		return 0;
	}
}

I added a new method to the Equipment script in order to easily determine what the primary item would be:

public Equippable GetItem (EquipSlots slots)
{
	for (int i = _items.Count - 1; i >= 0; --i)
	{
		Equippable item = _items[i];
		if ( (item.slots & slots) != EquipSlots.None )
			return item;
	}
	return null;
}

Confirm Ability Target State

Open the ConfirmAbilityTargetState. Add a new field to the class:

AbilityEffectTarget[] targeters;

and use that field in the FindTargets method instead of the local declaration. I removed the CalculateHitRate and EstimateDamage methods and modified the UpdateHitSuccessIndicator so that I am actually using the correct HitRate and Damage prediction based on what is actually able to be targeted by an ability effect.

void UpdateHitSuccessIndicator ()
{
	int chance = 0;
	int amount = 0;
	Tile target = turn.targets[index];

	for (int i = 0; i < targeters.Length; ++i)
	{
		if (targeters[i].IsTarget(target))
		{
			HitRate hitRate = targeters[i].GetComponent<HitRate>();
			chance = hitRate.Calculate(target);

			BaseAbilityEffect effect = targeters[i].GetComponent<BaseAbilityEffect>();
			amount = effect.Predict(target);
			break;
		}
	}

	hitSuccessIndicator.SetStats(chance, amount);
}

Perform Ability State

Finally, let’s open the PerformAbilityState script and replace the TemporaryAttackExample method with a more complete implementation. This time we aren’t hard-coding an ability effect but are letting the ability perform the effect for itself. It also takes into account the hit rate – it rolls a random number to see whether or not it actually hits.

void ApplyAbility ()
{
	BaseAbilityEffect[] effects = turn.ability.GetComponentsInChildren<BaseAbilityEffect>();
	for (int i = 0; i < turn.targets.Count; ++i)
	{
		Tile target = turn.targets[i];
		for (int j = 0; j < effects.Length; ++j)
		{
			BaseAbilityEffect effect = effects[j];
			AbilityEffectTarget targeter = effect.GetComponent<AbilityEffectTarget>();
			if (targeter.IsTarget(target))
			{
				HitRate rate = effect.GetComponent<HitRate>();
				int chance = rate.Calculate(target);
				if (UnityEngine.Random.Range(0, 101) > chance)
				{
					// A Miss!
					continue;
				}
				effect.Apply(target);
			}
		}
	}
}

Demo

At this point you should be able to build a nice variety of abilities. We will still need to add Elemental attributes and magic point costs in the future, as well as some constraints on the ability effects, but we have enough for a good demo already.

If I were to create an Ability, I would want to begin by adding an Empty GameObject as a child of the Unit which would be able to use it. I would name the GameObject the same name as the Ability (like you would see in the menu to select it). On this object I would add a component for the types of “Ability Power”, “Ability Range”, and “Ability Area”. When we add Magic Point costs and Elemental attributes I would also add those here.

Each effect of the ability (you can have multiple effects per ability) would be added to another child GameObject of the Ability GameObject. For example, to implement “Attack” I could add another GameObject named “Damage”. On this child object I would add a type of “Hit Rate”, “Effect Target”, and “Ability Effect” component. When we add constraints to the Ability Effect (like only adding a status effect if a previous effect hit, or implementing drain where you absorb the amount of hit points you attacked for) I would also add them here.

For now, let’s test out a pair of effects. Modify the Hero prefab’s “Attack” ability to cause Damage and Inflict Blind as follows:

Attack Ability with Damage and Blind photo AttackAbility_zpspice91td.png

Have one unit hit another. If the attack connects, there is a good chance the target will also have been blinded. You can tell whether or not it succeeded by using the Editor’s hierarchy pane to look at the targeted unit for a child GameObject named “BlindStatusEffect”. Once a unit has been blinded, it should have a very low chance of attacking another unit – particularly from the front. Check for a “miss” to see that our HitRate code is functioning. You can tell we missed if the hit points of the target haven’t changed.

Summary

In this lesson we implemented a couple of Ability Effects including the ability to apply damage to a target and the ability to inflict a status effect. We provided a means of powering abilities both by physical, magical, and weapon power. All of these pieces are components which can be mixed and matched to create a large variety of abilities.

When implementing the inflict status ability effect, we took a look at using Reflection as a way to work around an inability to use generics dynamically. In this way we were able to avoid an “explosion of classes” where we would need a new subclass for every kind of status effect we would wish to inflict.

Don’t forget that the project repository is available online here. If you ever have any trouble getting something to compile, or need an asset, feel free to use this resource.

Advertisements

18 thoughts on “Tactics RPG Ability Effects

  1. Hey, great tutorial again, thanks for your work!

    I have a question about Weapon Ability Power. I want to make sure I got this right, so when equipping an Equippable, its features will be applied, but the Weapon Ability Power increases the power by the amount of the increase of the ATK of the features of the primary weapon so that we could say that the increased ATK of the weapon is applied twice to the resulting power?

    I’m not sure I understand this correctly and if this is expected behaviour. Thanks for telling me if I’m wrong!

    Like

    1. The algorithms are admittedly a bit hard to keep straight. I think you understand but might have said it a little wrong. When considering the “Weapon Ability Power”, the ATK of the weapon would have already been added to the character’s ATK stat when evaluating his “BaseAttackStat” and would also be used as the “Power” stat. Those stats are different and are used at different places in the damage algorithm. So the ATK of the weapon would play a part twice during the damage calculation algorithm, but would not modify the “Power” stat twice.

      Each of the different component types have a “Power” value to use. Weapon based skills would be great while your character’s level was less than the attack value of your equipped weapon. At higher character levels it may turn the tables the other direction. Everything is designed in such a way as to require you to have to constantly change your approach and strategy so the gameplay is engaging for longer.

      Like

      1. Thanks for the clarification, I understand now!

        I also have a comment regarding Hitrate, I hope that asking it here isn’t a problem, if it is, you can move/delete my comment:

        In the ATypeHitRate, for example, we return and divide an int value by an int: “return rate / 2;”.
        Isn’t this not gonna work since dividing ints isn’t supposed to happen?
        I will try to use floats since facings aren’t really working how I expect them to in my code right now.

        Thanks for any help!

        Like

      2. There is nothing wrong with dividing integers as long as you understand that the result is floored. In other words “7/2” is not “3.5” but returns “3” instead. A lot of the algorithms I looked at chose to use integer division, and so I followed suit. I don’t think it will hurt anything to use better precision but it also wouldn’t surprise me, given the complexity of the algorithms, if even this small change could makes things feel wrong.

        What is wrong with the facings? Are you making something new based on my code or are you having a hard time implementing one of my examples?

        Like

  2. Ah I see, I had that feeling that it could be a problem but I was wrong.

    Since I’m using hexagon tiles, sometimes I have to change things a bit, but for facings, I get correct dot values when changing direction but the hitrate doesn’t change. It probably is something I overlooked when doing the hitrate lesson.

    Keep up the good work.

    Liked by 1 person

  3. Hey, i have problems just refactoring the Calculate method in the HitRate class. Because when it is override in the other classes. And the project directory doesn’t help because it doesn’t have the HitRate classes.

    Like

    1. Hey Draco, sorry you got stuck on the refactoring. I’m not sure I understand exactly what you are trying to say – it sounds like you can’t find the HitRate class in the project. The HitRate class was added in the previous lesson to this one (number 18 on the project page) so if you are missing it then you should go back a lesson and re-read it.

      Once you find it, then modify the function using the definition I provided in this post. When you build, it should produce errors, but those errors will tell you exactly which classes and even which lines need to be updated. If you are unable to complete the refactor on your own, then I would recommend trying to download the project with a source control program such as SourceTree. You can select the commit for this lesson and see exactly where code was changed and what it was changed to, etc.

      If none of that works for you, let me know (with as specific of a question as you can) and I will try to help some more.

      Like

  4. I just tried to demo this lesson today and got this error when I attacked:

    “TargetException: Non-static method requires a target.
    System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:236)
    System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115)
    InflictAbilityEffect.Apply (.Tile target) (at Assets/Scripts/View Model Component/Ability/Effects/InflictAbilityEffect.cs:35)
    PerformAbilityState.ApplyAbility () (at Assets/Scripts/Controller/Battle States/PerformAbilityState.cs:64)
    PerformAbilityState+c__Iterator6.MoveNext () (at Assets/Scripts/Controller/Battle States/PerformAbilityState.cs:22)”

    I compared the repository scripts to my InflictAbilityEffect and PerformAbilityState scripts, and it looks like the only differences are several “return 0” you added to the InflictAbilityEffect (which caused even more errors) and a refactoring of the ApplyAbility function in PerformAbilityState (which I haven’t gotten to yet). It seems like it’s a problem with reflection, as when I remove the inflict blind component from the hero, I can attack just fine.

    Like

    1. It is worth pointing out that those “return 0” statements are very important. A difference in a line like that is definitely worth fixing. For example, I might use a return 0 to exit early in the case where I don’t have something I need in order to continue with the rest of the method. You can think of them as “guards” against allowing code to operate under false assumptions which can lead to crashes in the game, which might be exactly what you have experienced.

      Both of the classes you mentioned, “InflictAbilityEffect” and “PerformAbilityState”, receive some code updates in upcoming lessons. If you want to check your code against the code in the repository you need to make sure you are referring to the code at commit ‘aca7035’ and have not simply downloaded the full project. Otherwise you could expect to see a bunch of extra errors. If you aren’t sure how to do that or what I am talking about with commits etc, you are probably better off closely examining the code listed on the blog post here. If you remain stuck, let me know.

      Like

      1. Hey Jon,

        I’m getting a similar error too and I cant seem to get it right even though I’ve looked at the scripts at the correct commit. I’m not familiar with reflections so I don’t know whats wrong

        It seems that the problem starts in this bit:
        Status status = target.content.GetComponent();
        //Debug.Log(target.content);
        object retValue = constructed.Invoke(status, null);

        Where the status variable returned null. Is it supposed to be like this?

        Like

      2. Found my problem, apparently I missed the step of putting the Status and Equipment component in the ally Hero prefab. I put the missing pieces accordingly and it now works fine. Thanks for the hint!

        Liked by 1 person

  5. Ok, 2 weeks and I think I finally figured it out lol. I skipped to the next lesson after getting this error, and noticed that there were alot of code changes around this point in the tutorial, so it definitely gets really confusing. I was still getting errors and getting nowhere on the “Magic” tutorial, so I downloaded the repository. Had a problem opening that up, but finally figured out that I had to open the Battle scene, thanks to your comment in lesson 1 “Introduction.” Long story short, I think I found the 2 main issues causing my problems. First off, in the “Magic” tutorial you added an Ability script, but only when I looked through the final project’s Attack gameobject (GO) did I realize I needed to add an Ability component to the Attack GO on the hero prefab. Secondly, I noticed that in addition to the Unit component on the hero prefab, you also had Status and Equipment components, which either I glossed over, or it wasn’t implicit that they needed to be on the hero prefab, so I added them. The Status component seemed to be the cause of the “TargetException: Non-static method requires a target” error, as when I added it everything worked, and when I removed it I got that error again. Finally, the attack range and attack area of effect are too big for a normal attack, which for a melee unit would be 1 Tile over, with that Tile being the area of effect, so I changed Constant Ability Range to x=1 and y=max int, and Specify Ability Area to x=0 and y=1. I hope this helps any future readers as it was very frustrating for me when I couldn’t get it to work initially.

    Liked by 1 person

  6. I noticed that after the first successful attack, the BlindStatusEffect is applied to all 3 hero units. Is this working as currently intended or did I screw up somewhere? Thanks and cheers!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s