- Home /
Trying to organize skill functions.
I'm still somewhat green on the subjects of polymorphism, interfaces, etc. What I'm trying to accomplish is an RPG skill system within a somewhat complex damage and statistics system in where different types of damage and resistances and things are being watched/calculated.
---Basically I'm trying to get a skill to perform a function.
As in the long run there will be hundreds of skills, passive, weapon, class, etc. I was going to put them into a SkillDatabase.cs as I've done with WeaponDatabase, etc. using:
public static Weapon shortSword = new Weapon(dmg, speed, etc);
however I was unable to figure a way to go that route if the Skill base class contains virtual functions, for example with:
public virtual void onUse ();
I can't then go:
public static Skill Strike = new Skill(onUse {yada yada}, etc); (right?)
So in looking for alternatives I started reading about interfaces and so changed Skill into ISkill with only { get; } for the values and gave up on the database idea, instead thinking of a script for each skill. The first test skill as follows:
public class MeleeSkillStrike : ISkill {
public string _name {
get { return "Strike"; }
}
public Classes.currentClass _classRequired {
get { return Classes.currentClass.Squire; }
}
public int _levelRequired {
get { return 1; }
}
public int _currentLevel {
get { return _currentLevel; }
set { _currentLevel = value; }
}
public skillCategory _category {
get { return skillCategory.Class; }
}
public skillType _type {
get { return skillType.Offense; }
}
public bool _passive {
get { return false; }
}
public void OnEffect () {
int newDmg;
if (Mathf.RoundToInt(PlayerData.damage() * 1.1f) == PlayerData.damage()) newDmg = PlayerData.damage() + 1;
else newDmg = Mathf.RoundToInt(PlayerData.damage() * 1.1f);
PlayerData.player.GetComponent<PlayerScript>().target.GetComponent<MobAi>().TakeDamage(newDmg, PlayerData.damageType());
}
public void OffEffect () {
//used for timed buffs, etc.
}
}
which is fine and works.... but at this point the only thing the interface is really doing is throwing errors for me if I leave any of the data out or input it incorrectly... not really what an interfaces main intention is I think, and really I might as well not implement at all and just hard code each skill if that's going to be the case...
can anyone think of a "why don't you just...." that I'm just not aware of?
To make things easier I suppose I can still make my database and just have it return the skill after hardcoding links into the database but... just seems like there should be an easier way to organize them I'm not getting... or is making a horde of scripts in neatly ordered folders totally acceptable?
thanks for any input.
Answer by VesuvianPrime · Dec 17, 2014 at 01:38 PM
Interfaces are good because it means later you can write SomeFunction(ISkill skill) type logic that can accept any skill type.
You're probably going to see benefit in using an abstract base class at the same time, since you can provide default values there via virtuals.
I'm not sure what you're trying to do with the constructor. It looks to me like perhaps ISkill should have an event OnUseCallback that the parent subscribes to after instantiation.
ahhh, I see the benefit in being able to accept any skill type.
Currently I'm rena$$anonymous$$g: Weapon currentWeapon = (Weapon)WeaponDatabase.ShortSword to call currentWeapon.damage because it's inheriting the Item class, but is a Weapon not an Item..... I suppose that I wouldn't be able to SomeFuction(IItem item) if it was public static Weapon : IItem, IWeapon though would I? Or would I? It would have all the necessary IItem variables so I guess I could couldn't I.... hmmm I'll have to mull on that a bit.
As for my skill stuff basically I want to have lots of skills for example a Strength buff and a Strike attack, both of which are similar with 99% of their variables but the void OnUse() or whatever function needs to be defined differently for them all. Thus I couldn't get the simple hard-coded new Skill database approach to work because I can't add functions and their lines into the "public static Skill Strike = new Skill (yada yada)" approach.
So making a script for each skill will work fine, and with ISkill inheritance I can be sure that I can call them without error from other scripts... just seems like I'm missing some other way to handle varrying functions within a parent-child class relationship... maybe I don't fully understand virtual and override uses yet...
Have moved from iPad to PC, so time for some examples. $$anonymous$$y approach would be something like:
// ISkill is a convenient place to describe functionality for
// skills and let the compiler tell you if anything is missing
// in your implementation.
public interface ISkill // : ICooldown, IEtc...
{
string name {get;}
// classRequired {get;} - I just realized how awful calling an enum "class" is, try a different name!
int levelRequired {get;}
// etc
void Use();
}
// AbstractSkill is where you can put default values and shared
// functionality
public abstract AbstractSkill : ISkill
{
// This is an example of providing a default value
public virtual string name { get { return "Unnamed"; } }
// This is an example of a property with no default
// value and must be reimplemented in derived classes.
public abstract int levelRequired {get;}
// Sounds like Use needs to be reimplemented in every skill
public abstract Use();
}
// Example of a concrete skill
public ExplosionSkill : AbstractSkill
{
public override int levelRequired {get { return 1000000; } }
public override Use()
{
Explode();
}
}
Furthermore, I suspect you might like to replace your static hardcoded skill script with a factory
public static SkillFactory
{
public static T GetSkill<T>()
where T : AbstractSkill, new()
{
return new T();
}
}
ExplosionSkill skill = SkillFactory.GetSkill<ExplosionSkill>();
Even better, we can add some caching:
public static SkillFactory
{
private static readonly Dictionary<Type, AbstractSkill> s_CachedSkills;
static SkillFactory()
{
s_CachedSkills = new Dictionary<Type, AbstractSkill>();
}
public static T GetSkill<T>()
where T : AbstractSkill, new()
{
if (!s_CachedSkills.Contains$$anonymous$$ey(typeof(T)))
s_CachedSkills[typeof(T)] = new T();
return s_CachedSkills[typeof(T)];
}
}
I see I need to study examples of abstracts and overrides more thoroughly. That looks in fact like what I'm looking for. Thank you for the detailed response.
I'm not sure how the factory works, but I can look into that further now that I have the keyword and an example. Thank you.
I don't understand your SkillFactory you made for me... it doesn't let me play with it if I just copy and paste like that. Would you $$anonymous$$d explaining it a little more for me?
I tried reading documentation on factories but I can't get it to sink in XD
For now I've ended up hiding the inherited parent functions with "new public void OnUse()" and I can successfully hardcode them into a database script which calls fine and is returning the proper values. I still can't generate a List or any form of "ReturnAllSkills()" or "foreach (Skill in SkillDatabase)" for GUI program$$anonymous$$g, etc.
I think that's what you were trying to $$anonymous$$ch me with the factory but I don't understand it XD