General Information
The Character Abilities are the scripts that will enable your character to perform actions. Whether it’s jumping, running, or pressing a button, that’s where it happens. In previous versions of the engine, this was all handled by a single CharacterBehaviour script. While it’s not necessarily bad design in a small game where your character only does walking and jumping, here it became hard to read and maintain as new features kept being added. Having separated ability scripts also allows for a much easier extension of the system. It’ll also make it much easier for you to create your own abilities.
Standard Abilities
-
Character HorizontalMovement : This component handles basic left/right movement, friction, and ground hit detection. In its inspector you can define standard movement speed, walk speed, and what effects to use when the character hits the ground after a jump/fall.
-
Character Ability Node Swap : This ability lets you specify a new set of abilities, and swap to them at the press of a button.
-
Character Auto Movement : This ability will make your character move automatically, without having to touch the left or right inputs, ala Super Mario Run. You’ll find an example of this in action in the MinimalAutoMovement demo scene. Note that you can use AutoMovementControlZones to define direction changes, state change, stop movement, etc. Some features, like push or gravity zones won’t work with that ability, but most will work just fine.
-
Character Bounce : This ability will cause your character to bounce up every time it collides with the ground.
-
Character Button Activation : This component allows your character to interact with button powered objects (dialogue zones, switches…). Nothing special to setup here.
- Character Crouch : This component handles crouch and crawl behaviours. In its inspector you can determine the crouch speed, and whether or not the collider should resize when crouched (to crawl into tunnels for example). If it should, make sure you setup its new size. *
-
Character Crush Detection : introduced in v6.5, this ability lets you detect when your character gets crushed, and lets you act on it, causing damage or death. For an example of this, you can look at the RetroCrusher prefab in the RetroVania demo scene (first room to the right when entering the level).
-
Character Dash : This component allows your character to dash. From the inspector you can define the distance the dash should cover, how much force to apply, and the cooldown between the end of a dash and the start of the next one
-
Character Damage Dash : Similar to the Character Dash ability, but will apply damage to its target layer.
-
Character Dangling : Add this component to a character and it’ll adopt a dangling stance if facing a hole in the ground. The detection is done using a raycast, whose origin and length can be setup from the inspector.
-
Character Dive : This component allows your character to dive (by pressing the dash button + the down direction while in the air). In its inspector you can define how much the camera should shake on impact, and how fast the dive should be.
-
Character Fall Damage : This component will apply damage to the character if it falls from a height higher than the specified MinimumDamageFallHeight. How much damage that is will be remapped between the specified min and max damage values. You can look at the MinimalFallDamage demo scene for an example of this ability in action. Requires v6.5 or more.
-
Character Fly : Allows your character to fly, either permanently (think Mario’s ghosts), or on command. You can define the Fly speed from the ability’s inspector. You’ll find an example of an always flying character in the RetroCopter demo scene, and one of a “fly on command” character in the MinimalFlight demo scene.
-
Character Follow Path : Probably more suited for an AI character than a Player character, this ability allows the Character to follow a path made of a set of nodes.
-
Character Glide : This ability allows your character to glide while falling. It’s heavily inspired by old school games such as Aladdin on Super Nes (still my superior version, fight me). From its inspector you can define the force that will be applied to the character when gliding (you’ll want to make it something like -0.1), and whether or not you can only glide if you don’t have any jumps left. There’s a separate input axis in the InputManager settings for this, but you can also plug it to the same button/axis as Jump if you want. The RetroCorgi demo character (among others) has this ability, along with an animation for it.
-
CharacterGrabCarryAndThrow : This class lets you grab, carry and throw objects with a GrabCarryAndThrowObject component. You’ll find an example of this ability and corresponding objects in an action in the RetroPush demo scene. On the ability’s inspector you’ll be able to define a lot of settings, the most important being how objects should be detected (should a ray be cast downwards? on the side?), what layer the objects should be on, and what object to attach carried objects to
-
CharacterGravity : This class will allow your character to have a custom gravity direction and be affected by gravity zones and points. While most features will work exactly the same whether gravity is modified or not, note that some features are “normal gravity” specific (sticking to slopes, push & pull).
-
Character Grip : Add this component to a character and it’ll be able to grip level elements that have the Grip component.
-
Character Ground Normal Gravity : this experimental ability will automatically compute the current slope’s angle and change the gravity’s direction to match the slope normal. This lets you (for example) walk on the walls of a room, as shown in the MinimalDynamicGravity demo scene. How well it’ll perform really depends on your level design, it’s not bulletproof.
-
Character Handle Weapon : This component will allow your character to pickup and use weapons. What the weapon will do is defined in the Weapon classes. This just describes the behaviour of the ‘hand’ holding the weapon, not the weapon itself.
-
Character Handle Secondary Weapon : This component will allow your character to handle a secondary weapon. It acts exactly like the regular Character Handle Weapon component (actually, it extends it), but is bound to different inputs. Feel free to duplicate it to handle a third, fourth, and more weapons.
-
Character Inventory : This component is mandatory if you want your character to use an InventoryEngine’s inventory. It’ll make the link between both systems, and will allow your character to equip weapons, swap them and use items. It will also let you define what weapon to auto equip on start, as well as what items to add to the inventory, should you wish to start with a specific ready and loaded inventory.
-
Character Jetpack : Add this component to a character and it’ll be able to activate a jetpack and fly through the level. From its inspector you can define the force to apply when jetpacking, the optional direction (and how you want it controlled), the particle system to use, various fuel info, and optionnally what sound to play when the jetpack gets fully refueled
-
Character Jump : This component handles jumps. In its inspector you can define the jump height, whether the jump is proportional to the press length or not, the minimum air time (how long a character should stay in the air before being able to go down if the player has released the jump button), jump restrictions, an input buffer (keep it below the duration of your jump), how many jumps the character can perform without touching the ground again, and how long collisions should be disabled when exiting 1-way platforms or moving platforms.
-
Character Ladder : This component allows your character to climb ladders (objects with a Ladder component). From its inspector you can set the speed to apply to the character when it’s climbing a ladder, or define whether or not input has to be pressed for the climb to be initiated.
-
CharacterLedgeHang : This component allows your character to hang from ledges when connecting with them. Ledges are gameobjects with a Collider2D on them, and a Ledge component. On the Ledge component you’ll be able to specify from what direction that ledge can be grabbed, and define a Hang Offset and a Climb Offset, which are respectively the position the character should be at when hanging from the ledge and after climbing it. Tweaking this ability can take some time, as basically it’ll require you to have its inspector and your animation in sync. You can look at how the RetroCorgi works in that regard.
-
Character Look Up : This component allows your character to look up when pressing up while grounded. How much the camera will move in this situation is defined on the CameraController’s inspector.
-
Character Particles : A utility ability used to trigger particles while the character is in certain states.
-
Character Pause : Allows this character (and the player controlling it) to press the pause button to pause the game. Note that you’ll need both an InputManager and a TimeManager in your scene for this ability (and the Pause) to work.
-
Character Persistence : Add this component to a Character and it’ll persist with its exact current state when transitioning to a new scene. It’ll be automatically passed to the new scene’s LevelManager to be used as this scene’s main character. It’ll keep the exact state all its components are in at the moment they finish the level. Its health, enabled abilities, component values, equipped weapons, new components you may have added, etc, will all remain once in the new scene.
-
Character Push : This component will allow your character to push Pushable blocks. This is not mandatory, you’d be able to push objects without it, but this component will allow you to have a dedicated push animation when pushing, and override default push values. For the animation to work, you’ll need to add a “Pushable” component on your pushable blocks.
-
Character Push Corgi Controller : a class that will allow you to push blocks equipped with the Pushable class and a Corgi Controller. You’ll also be able to pull them if you want. You can have it be button based (binding is the “3” keyboard key by default, feel free to change it in the InputSettings). You can see it in action in the RetroPush demo scene, among other places. Note that this ability does not (yet) support gravity modification.
-
CharacterRestartLevel : lets you restart the level at the press of a button (or at the call of a method), either killing the player, transitioning to another scene, or teleporting to the initial, last, or current checkpoint.
-
Character Roll : Add this component to a character and it’ll be able to “roll” along surfaces, with options to go through enemies, and keep controlling direction. If you’d like your character to dash along slopes, that’s the ability for it.
-
Character Run : This component allows your character to change speed (defined in its inspector) when pressing the run button
-
Character SimpleDive : Same as CharacterDive, without the need to press the dash button. This ability is a good example and reference of how you can very simply extend an existing ability to implement your own specific rules and behaviours.
-
Character SlopeOrientation : This component, added to a Character, allows you to have your model (or any node on your character) perpendicular to the slope it’s on. It’s very easy to setup, make sure you bind your model container to its ObjectToRotate slot, and then you can define its rotation speed, the min and max angles allowed, whether the weapon should rotate too, and whether or not the angle should reset in the air. You’ll find an example of this in use in the MinimalSlopes demo scene, and a recipe to set that up on the Recipes page.
- Character Speed : This ability lets you specify speed modifiers based on current state. *
-
Character Speed Analysis : This ability computes and exposes various speed metrics at runtime (controller speed, transform speed, previous frame speeds, customizable rolling average based on frame count or time.
-
Character Stairs : This ability enables the ability to climb stairs on your character.
-
Character Stun : this ability will let your character be stunned if in contact with a StunZone component. This will place your Character in a Stunned state, during which most other abilities will be prevented. You can of course trigger feedbacks when entering or exiting that state to communicate that info to the player. You’ll find an example of that in the RetroPush demo scene, where a proximity mine activated stun zone will temporarily stun your Character.
-
Character Surface Feedbacks : Add this component to a character and it’ll let you define a number of surfaces and associate walk and run feedbacks to them. It will also let you trigger events when entering or exiting these surfaces. Note that surfaces are evaluated from top to bottom. The first surface definition that matches the current detected ground will be considered the current surface. So make sure your order them accordingly.
-
Character Swap : This ability, used along with a CharacterSwapManager, will let you swap control between multiple characters in the same scene (think Lost Vikings). You can see it in action in the MinimalCharacterSwap demo scene.
-
Character Swim : Gives your character the ability to swim in Water. From its inspector you’ll be able to define how much force you want to apply when swimming (the “swim height”), and the duration of the accompanying animation. You can also bind effects for water entry/exit, as well as define the force on Water exit. The RetroCorgi is setup with proper animations for it if you want to take a look at it in action.
-
Character Switch Model : This component allows your character to have its model replaced at a press of a button. If you want to have it change its whole prefab instead, look for the CharacterSwitchManager class.
-
Character Time Control : Allows your character to slow (or speed up) down time while pressing the time control button (K by default). Requires a Time Manager in your scene to work.
-
Character Wall Clinging : Add this component to your character and it’ll be able to cling to walls, slowing down its fall. From its inspector you can define the slow factor (close to 0 : super slow, 1 : normal fall) and the tolerance (to account for tiny holes in the wall
- Character Walljump : This component allows your character to perform an extra jump while wall clinging only. Here you can determine the force to apply to that jump
Ability overview
So what does an ability do ? If you’re interested in code, the easiest way to find out is to look at the CharacterAbility.cs class, from which all abilities inherit. It has a few methods, let’s go over the main ones quickly :
- Initialization : as the name implies, that’s where the base class will get parts of the Character or Scene that’ll be regularly useful in children abilities. Stuff like the camera, CorgiController component, input manager, etc. A child ability will typically use this to grab additional components or initialize variables (number of jumps, etc).
- Animation methods : InitializeAnimatorParameters and UpdateAnimator : use the first one to register animation parameters, and the second to update them. This is done in two steps to avoid checking for the existence of each parameter every frame, which would end up causing performance issues.
- HandleInput : overridden by each ability to check for the state of relevant buttons and axis. If a certain button is pressed/released, this method will call other methods in the ability.
- Early/Process/Late Process ability : these methods are called by the state machine at each update.
- Reset : this method will be called when the character dies. Useful to reset counters etc.
- Play/Stop Sfx : methods used to trigger the abilities sounds. By default each ability comes with 3 sounds (defined per ability in their inspector) : one when it starts, one while it’s used, and one when it stops. You can of course only use one or none of these. If you create your own ability, you’ll need to call these methods to trigger the sounds.
Organizing abilities
From v6.6 onwards, you can now split abilities across multiple game objects (or nodes).
It’s quite easy to setup. By default, the Character component will look for abilities on its own node (usually the top level of your Character’s hierarchy). Additionnally, you can bind any number of additional nodes you want to its AdditionalAbilityNodes array. Usually you’ll have (like in the image above) a number of empty game objects with abilities on them. Simply drag these into that array, and the Character component will register them on initialization.
This can be useful for a number of things. It avoids having too many components on a single object, which can become confusing when looking for a specific ability or field. It also lets you enable/disable entire ability nodes at once, or even swap entire ability sets. The spine-brobro prefab, which you’ll find in action in the Brobro demo scene, is built that way, and also uses the AbilityNodeSwap ability, which lets it replace an entire ability set for a complete new one, at the press of a button (P, by default).
The State Machine
The Character component is responsible for triggering the various abilities. It does so using state machines. A StateMachine is a design pattern that will basically store a current state and the previous one (if you’re curious for more, look at the code or API documentation). By default a Character uses two state machines :
- MovementState : accessed via the _movement property from any Ability, it represents the current action the Character is performing.
- ConditionState : accessed via the _condition property from any Ability, it stores the current status of the Character (normal, dead, paused, etc).
The way it works is that at the start of a Scene, the Character will initialize all Abilities, then every frame, call their EarlyProcess, Process and LateProcess methods, and eventually reset them on Death. Other State Machine implementations would usually only call the current state’s ability on Update. This one doesn’t, for a number of reasons, but mostly to make it easy to extend the system, without having to rewrite everything or modify existing classes. This means that each ability is responsible for handling its own input, preventing the entry into its methods (by testing if the current state allows it - you can’t walk while not grounded for example). Most abilities included in the engine don’t use EarlyProcess or LateProcess, but it’s still a possibility if you need it.
Condition abilities
Most abilities define their own base restrictions, which are states in the state machines described above from which you can’t transition to the ability. For example, by default, you can’t jump while jetpacking. These default restrictions cover the most requested use cases, but in your game you may want to restrict things further. This can be done by extending the base ability to change the condition check, or simply, from the inspector, by adding states to the Blocking Movement States and Blocking Condition States lists, as pictured in the image above.
Permitting abilities
Thanks to the ability API, it’s very easy to authorize or prevent an ability. Simply calling an ability’s PermitAbility(bool) method, passing it true or false, will let your character use it, or will prevent it from using it. You can do that from any script of course, and if you’d like your Character to be able to pickup items that enable a certain ability, you can use the PickableAbility class. It will let you pick from a list of abilities in your project, and will let you select whether you want to permit or forbid that ability on pick.
Create your own ability
The easiest way to create your own ability is to extend CharacterAbility, the same way all abilities in the engine right now do. Override methods (make sure you call the base one at the start then) when needed. To test your new ability, you just have to add it to an existing Character, and it’ll be automatically added to the state machine, and processed like the others. One thing to keep in mind is the interaction with the other abilities. You may want to extend other abilities to prevent or authorize certain state changes. Additionnally, your Ability may require new states. You can declare these in CharacterStates.cs (anywhere in the MovementStates or CharacterConditions enums, the order doesn’t matter).
For inspiration you can have a look at the current Character Abilities, as they are exactly what you’re trying to create, and are good examples of what you can achieve. Here’s a good basis I used to create them, as I found out these methods were the ones I used to override the most. Make sure you replace all the TODOs with your stuff :
using UnityEngine;
using System.Collections;
using MoreMountains.Tools;
namespace MoreMountains.CorgiEngine // you might want to use your own namespace here
{
/// <summary>
/// TODO_DESCRIPTION
/// </summary>
[AddComponentMenu("Corgi Engine/Character/Abilities/TODO_REPLACE_WITH_ABILITY_NAME")]
public class TODO_NEW_ABILITY_NAME : CharacterAbility
{
/// This method is only used to display a helpbox text
/// at the beginning of the ability's inspector
public override string HelpBoxText() { return "TODO_HELPBOX_TEXT."; }
[Header("TODO_HEADER")]
/// declare your parameters here
public float randomParameter = 4f;
public bool randomBool;
// Animation parameters
protected const string _todoParameterName = "TODO";
protected int _todoAnimationParameter;
/// <summary>
/// Here you should initialize our parameters
/// </summary>
protected override void Initialization()
{
base.Initialization();
randomBool = false;
}
/// <summary>
/// Every frame, we check if we're crouched and if we still should be
/// </summary>
public override void ProcessAbility()
{
base.ProcessAbility();
}
/// <summary>
/// Called at the start of the ability's cycle, this is where you'll check for input
/// </summary>
protected override void HandleInput()
{
// here as an example we check if we're pressing down
// on our main stick/direction pad/keyboard
if (_inputManager.PrimaryMovement.y < -_inputManager.Threshold.y)
{
DoSomething();
}
}
/// <summary>
/// If we're pressing down, we check for a few conditions to see if we can perform our action
/// </summary>
protected virtual void DoSomething()
{
// if the ability is not permitted
if (!AbilityPermitted
// or if we're not in our normal stance
|| (_condition.CurrentState != CharacterStates.CharacterConditions.Normal)
// or if we're grounded
|| (!_controller.State.IsGrounded)
// or if we're gripping
|| (_movement.CurrentState == CharacterStates.MovementStates.Gripping))
{
// we do nothing and exit
return;
}
// if we're still here, we display a text log in the console
MMDebug.DebugLogTime("We're doing something yay!");
}
/// <summary>
/// Adds required animator parameters to the animator parameters list if they exist
/// </summary>
protected override void InitializeAnimatorParameters()
{
RegisterAnimatorParameter(_todoParameterName, AnimatorControllerParameterType.Bool, out _todoAnimationParameter);
}
/// <summary>
/// At the end of the ability's cycle,
/// we send our current crouching and crawling states to the animator
/// </summary>
public override void UpdateAnimator()
{
MMAnimatorExtensions.UpdateAnimatorBool(_animator, _todoAnimationParameter, (_movement.CurrentState == CharacterStates.MovementStates.Crouching), _character._animatorParameters);
}
}
}
Character Abilities Recipes
Here are some abilities related recipes :
Adding and setting up a Damage Dash ability
- in Unity 6000.0.23f1 (or higher), create a new project and import Corgi Engine v9.1 via the Package Manager
- open the MinimalLevel demo scene
- drag a MinimalShootingRangeTarget prefab in the scene, position it at -6,-4,0
- edit the Rectangle prefab
- add an empty child object to it, name it MyDamage, set its layer to Player, position it at 0,0,0
- add a box collider 2D to it, set it to IsTrigger:true
- press the AddComponent button, look for DamageOnTouch, add it
- on its TargetLayerMask, check Enemies
- on the top level object (Character), remove the CharacterDash ability
- add a CharacterDamageDash ability
- drag the MyDamage node into its TargetDamageOnTouch slot
- set InvincibleWhileDashing to true
- save your prefab, press play, press F to dash into the target
How to setup a character for slope orientation
- in Unity 6000.0.23f1 (or higher), create a new project and import Corgi Engine v9.1 via the Package Manager
- open the MinimalSlopes demo scene
- open the NewCorgi3D prefab, create an empty game object inside it, position it at 0,-0.4,0, name it RotationNode, and parent the SquashAndStretch node to it, without changing its position
- save your prefab, return to the MinimalSlopes scene, and drag the NewCorgi3D prefab into the LevelManager’s PlayerPrefabs[0] slot
- press play