This is a simple ability system for Unity 3D. It is extremely customizable and comes out the box with many different popular ability features.
- Download the Asset from the Unity asset store.
- Import the package.
- For an example of the ability system in action, go into the demo scene.
- To add the ability system to an existing character, add the AbilitySystem script component to the player character.
- To allow the player to be effected by abilities (including their own buffs), add an abilityTarget script component to the player character.
- Add abilities to the ability list property in the ability system component you just added.
- To use the casting method used in the demo scene (abilities bound to nums 1-9), add the DemoPlayerCasting script to the character as well.
- The DemoPlayerMovement script uses a character controller so it will not work with physic based rigidbody characters but the movement script is not necessary to the function of the system and is only included to show off the demo scene. If you do add the demoPlayerMovement script, set the controller and camera. If the game is in 3rd person, set the camera boom (the thing your camera should be attached to so it can rotate around the player). If you have animations, you can add the animator but you'll have to change the script to work with your character's animations.
- If you want the UI seen in the demo scene, add the DemoUI prefab to the scene and check "Uses SAS Demo UI" in the ability system component.
- For targets/enemies, you can either add the AIDemoCharacter or DemoTarget prefabs or you can use your own targets by adding the AbilityTarget script to the targets. For enemies, you can also add an abilitysystem script to them and add abilities to their ability list if you want them to use abilities as well. The DemoAICharacter script shows an example of how to get enemies to cast abilities randomly (no advanced AI included in this asset).
- For penetrating projectiles to work, you need to go into the ability system settings found at SimpleAbilitySystem/AbilitySystemSettings and change the target layer to whatever layer you want to use for targets/enemies.
- Settings - SAS_SystemSettings: A link to the global settings for the ability system. The demo just has prefabs for projectile and aura as well as the layer for targets. This is set to water in the demo so that I don't take up any of your layer slots but you should make your own layer.
- Ability List - List of Ability Objects: A list of abilities usable by whatever character this system is attached to. Order does matter.
- Mana - float: Used as an example of a resource the abilities can use. I'll go over how to change this to your system in the FAQ section.
- Uses SAS Demo UI: If you want to see the UI used in the demo scene, check this to true, add a Demo UI prefab to the scene, and set the Demo UI property to the UI object.
- Demo UI: The Demo UI object in the scene.
- Starting Target Stat Values - List of structs: A list of the target's starting values. Uses structs with effects and floats as well as bools for if the stat can go above the starting values or under 0.
- Destroy When Health Runs Out - bool: If true, the target will be fully destroyed when its health runs out. If false, it will just be disabled. Out of the box, this hides targets and just stops ai character movements. If it is set to true, it cannot be reset without restarting the game.
- Has Health Bar - bool: If true, this target has a health bar which displays its health value.
- Demo Health Bar Prefab: A reference to the health bar prefab found at SAS_Prefabs/Demo/SAS_HealthBar
There are examples of a few abilities found in the SAS_Abilities folder.
- Type - enum: The type of ability. The system comes out the box with projectile type and aura type abilities.
- Ability Lifespan - float: Determines how long the ability will last in seconds. Has no influence over how long buffs/debuffs last but instead how long the ability itself is out. A projectile will die off or an aura will disappear after this long.
- Size - float Determines the radius of the ability's effective area. A projectile and its collision will be scaled to fit this size. Because a projectile can be sent any mesh, it will scale the largest dimension of the mesh to fit the size and then scale everything else appropriately to look right. An aura is always a sphere so no need to do this.
- Cast Time - float: How many seconds between the cast is called and the ability shows up. This is helpful for when you have an animation for casting a specific ability which you want to start playing when the button is clicked but you don't want to spawn the projectile/aura until the animation has finished.
- Cooldown - float: How long, in seconds, the player/ai has to wait until the ability can be cast again after using.
- Cost - float: How much of a resource the ability uses. I'll go over switching out the resource in the FAQ section.
- Base Ability Effects - list of ability stat lines:
What happens when the ability hits a target? This includes a few properties of their own:
- Effect - enum: What stat is effected on the target.
- Magnitude - float: How is the stat changed? This can be set to negative to decrease the stat or positive to increase it.
- Revert Stat On Duration End - bool: If true, the effects are undone after the effect duration. Helpful for temporary stat buffs/debuffs like a slow or a strength boost.
- Effect duration - float: How long, in seconds, before the effects are undone. Advanced
- Is Percentage - bool: If true, the stat will be changed by a percent of their current stats. If set to true, magnitude should usually be between -1 and 1. For instance, if the effect is speed, magnitude is -0.5, and is percent is true, the target will lose half of their speed.
- Multi Applied Effect - bool: Can the effect be applied multiple times? This is helpful for situations like when you want an aura that does damage while enemies are in it instead of just damaging once.
- Effect Application Tick Rate: float: How often, in seconds, a multi applied effect can be applied to a single target.
- Effect Delay: float: How long an effect takes to happen after the ability hits. This is helpful for situations like buffs that give debuffs after the buffs run out.
- Can Effect User: bool: If true, this ability can hit the user. Helpful for buffs and for friendly fire. This can be adjusted to be a "can effect team" stat instead and I'll go over that in FAQ.
- Can Effect Others: bool: If true, the ability can hit targets other than the caster. Most obvious reason you'd turn this off is probably for buffs you don't want to give to enemies.
- Knockback - float: The force applied when this ability hits a target that can be knocked back.
- Applied knockback Multiple Times - bool: If true, an ability can knockback the same target multiple times (even if it doesn't apply the effects).
- Can Knockback Caster - bool If true, the caster of the ability can be knocked back by the ability (even if no effects apply to the caster).
- Projectile - mesh: The mesh used for the projectile
- Projectile Material - material: The material used for the projectile's mesh
- Projectile Speed - float: The speed of the projectile
- Penetrates Targets - bool: If true, the projectile will go through targets but still hit them. It will not bounce off or stick to targets if this is true.
- Projectile Bounces Off Surfaces - bool: If true, the projectile will bounce off surfaces like walls, ceilings, and floors.
- Max Bounces - int: How many bounces until a bouncing projectile is destroyed.
- Projectile Sticks To Surfaces - bool: If true, the projectile will stick to surfaces like walls, ceilings, and floors. note: If a projectile is set to bounce and stick, it will bounce until it has bounced more than its max bounces and then it will stick.
- Bounces or Sticks To Targets - bool: If true, the bounce and stick logic will be used with targets as well. The effects will still be done on the targets.
- Use Precise Collision - bool: If true, the projectile will use a mesh collider instead of a simple sphere collider.
- Aura Mesh - mesh: The mesh the aura will use. If not set, there will be no mesh but the collision will still happen so that's helpful if you want to handle how the aura looks with visual effects or particles.
- Aura Material - material: Material to set the mesh to.
- Aura Stays in Casted Position - bool: If true, if the player casts the aura and then walks away, the aura will still be where the player was when he casted it. If false, the aura will follow the player around.
- Aura Expands over Duration - bool: If true, the aura will change size after it is casted. It will go from the starting size to the size given in the general settings.
- Starting Size of the Aura - float: The radius of the aura when it is casted. Only used if it will expand.
- Aura Expand Method - enum: How the aura expands. If set to uniform, the aura will go from the starting size to the ending size over the duration of the aura ability. If set to manual, it will change size at a given speed until either the aura is destroyed because its duration is over or it hits the max size.
- Aura Expand Speed - float: Used for manual expand method
- Player Anim - animation clip: What animation is played when this ability is cast.
- Visual Effect - game object: The visual effect to play when this ability is cast. Set to game object so particles or visual effect can be used
- Sound Effect - audio source: The sound effect to play when this ability is cast.
- Can Ability Level Up - bool: If true, the ability can level up and get different stats at different levels
- Max Level - int: The max level an ability can get to. note: Levels start at 0 so if max level is set to 2, there are 3 levels: 0, 1 and 2.
- Level Scaling Method - enum: How the ability changes on level up. Automatic calculates simple effect magnitude scaling. Manual has separate base ability effects at different levels (good for if level 0 of an ability just does damage but level 1 also slows the target for example). Advanced has completely different ability objects for each level. Good for if things like size, cooldown, cost, lifespan, projectile speed, or aura expand speed need to change.
- Auto Level Stats - List of Structs:
If the method is set to automatic, this determines what stats are scaled on level up and by how much. Stats are scaled like this: base magnitude + base magnitude * level * level multiplier. An example would be as follows:
scaled stat = health; base health = 10; level multiplier = 0.5f
- Level 0: 10 + 10 * 0 * 0.5 = 10 health
- Level 1: 10 + 10 * 1 * 0.5 = 15 health
- Level 2: 10 + 10 * 2 * 0.5 = 20 health
- Level 3: 10 + 10 * 3 * 0.5 = 25 health
- Manual Level Stats - List of Structs: If the method is set to manual, this determines the exact stats at each level, completely overriding the base ability stats.
- Advanced Level Stats - List of Ability Objects: If the method is set to advanced, this determines the ability used at each level. Examples of these are found in the UniformExpansion folder in DemoAuras and the BounceTestLevels folder in DemoProjectiles.
-
How to make an ability? Right click in the project area and click Create->SimpleAbilitySystem->AbilityObject You can also duplicate one of the demo abilities in SimpleAbilitySystem/SAS_Abilities to make an ability quickly
-
How do I make a new ability type besides aura and projectile? First, I would make sure that what you want to do is not able to be done with the existing types. For instance, a self buff can be easily done with auras by simply not setting the aura mesh and making the size small however if you want something completely different like a hit scan ability, you’ll have to do this:
-
Make a new script and make it a child of SAS_BaseAbilityType and put whatever logic you want your new type to have.
-
Go to line 15 of the ability system script and add your new type to the ability type enum.
-
Create a new cast function in the ability system script for your new function. The projectile and aura examples of these functions are in lines 180-205.
-
Lastly, you need to add a new if statement to check if the casted ability’s type is your new type and if so, call the cast function you just made.
-
How can I use my own resource system instead of the built in mana system? You'll have to do a little scripting for this. Open up the SAS_AbilitySystem script and head to the Cast Ability function where it deals with ability cost. This is found around line 115. You should see this logic:
if (mana < ability.cost) // If we don't have enough mana, interrupt the cast { if (usesSASDemoUI) StartCoroutine(demoUI.NoMana()); return; } // Otherwise we change the mana amount and update the text box if we're using the demo UI mana -= ability.cost; if (usesSASDemoUI) demoUI.mana.text = mana.ToString();
You should change the above logic to check your resource. Since your resource is likely found in another script, you'll have to get that script and do something like this:
yourScript = GetComponent<YourScript>(); if (yourScript.yourResource < ability.cost) { // Do anything you want here to show the player they don't have enough mana return; } yourScript.yourResource -= ability.cost; // Continue casting as normal
-
How to Adjust the Can Effect Others and Can Effect User to be team based? Like Can Effect Enemies and Can Effect Team? On base, the ability system simply checks to see if the target hit is the caster and then if so, it is considered a friendly hit. Otherwise it isn't. To change this to a team system, head over to the SAS_BaseAbilityType script and go to the HitTarget function. Change the Hit target function to look for your team system. An example would be a team tag or you can add a target property to one of the scripts attached to the target (either the abilityTarget script or your own script) and then check that. For everythign to work properly, you need to change where it is checked if the target is equal to the caster. You should find the logic around line 120:
public void HitTarget(GameObject targetObject, Vector3 knockbackDirection) { if (targetObject.TryGetComponent(out SAS_AbilityTarget target)) { if (!targetsHit.Contains(target)) { target.Hit(stats, target == caster); // <-------------- Change this if (target != caster || canKnockbackCaster) // <-------------- Change this ApplyKnockback(targetObject, knockbackDirection); /* Example of team tag system: target.Hit(stats, target.CompareTag(caster.tag)) if (!target.CompareTag(caster.tag) || canKnockbackCaster) ApplyKnockback(targetObject, knockbackDirection); */ targetsHit.Add(target); List<SAS_AbilityStatLine> readyStatLines = new(multiTargetsOnCooldown.Where(kv => !kv.Value.Contains(target)).Select(kv => kv.Key).ToList()); if (readyStatLines.Count > 0) { ApplyStatCooldown(target, readyStatLines); } } else { if (appliesKnockbackMultipleTimes) { if (target != caster || canKnockbackCaster) // <-------------- Change this ApplyKnockback(targetObject, knockbackDirection); /* Example of team tag system: if (!target.CompareTag(caster.tag) || canKnockbackCaster) ApplyKnockback(targetObject, knockbackDirection); */ } if (multiTargetsOnCooldown.Count > 0) { List<SAS_AbilityStatLine> readyStatLines = new(multiTargetsOnCooldown.Where(kv => !kv.Value.Contains(target)).Select(kv => kv.Key).ToList()); if (readyStatLines.Count > 0) { target.Hit(readyStatLines, target == caster); // <-------------- Change this /* Example of team tag system: target.Hit(stats, target.CompareTag(caster.tag)) */ ApplyStatCooldown(target, readyStatLines); } } } } }
-
I added, removed, or edited a public or serialized property in the SAS_AbilitySystem, SAS_Ability, or SAS_AbilityStatLine script but I don't see it on the inspector or am getting an error? To prevent clutter and be easier to understand, these have editor scripts. This is what makes it so when you make an ability of type aura, it shows the aura settings but not the projectile ones and vice versa. The ability system and ability editor classes are found in SimpleAbilitySystem/SAS_Scripts/Editor/SAS_AbilityEditor.cs while the AbilityStatLine uses a property drawer found in SimpleAbilitySystem/SAS_Scripts/Editor/AbilityStatLineDrawer.cs. You can either edit these classes to include your property changes or delete the classes completely. An example of what to add to the editor class to include your new property is as follows:
EditorGUILayout.PropertyField(serializedObject.FindProperty("yourNewPropertyName"));
If you need further help with this, you can email me.
-
My projectile that is suppose to penetrate the target isn't working You probably don't have the target layer set. Head to the AbilitySystemSettings found in the SimpleAbilitySystem folder. Change the target layer to whatever layer you are using for the targets or enemies. To see the logic for projectile penetration, head to the SAS_Projectile script line 55. You should see this logic which I will break down here:
// If this projectile is one that should penetrate targets if (penetrating) { // Exclude the target layer from the physics collider (which allows it to pass through the targets). physicsCollider.excludeLayers = targetLayer; // Create a new collider which will not collide but will overlap with the targets so they can still be effected by the projectile. SphereCollider trig = gameObject.AddComponent<SphereCollider>(); trig.isTrigger = true; trig.radius = 0.5f; }
If there is a suggestion or bug you'd like to report or you need help with something related to this package, you can reach out to me either through email: marasciagames@gmail.com or using the unity asset store page.