Abilities
Overview
The AbilitiesSystem
executes custom actions relevant to a specific entity (usually an Actor).
Features and Properties
The FPS Template includes a set of features in its Ability implementation:
- every ability is an entity.
- an ability can be toggled on/off or of one-time use.
- an ability have a specific amount of charges and / or a recharge timer.
- an ability can be dynamically added and removed at runtime.
- the sequential execution of abilities is constrained by a global cooldown which is defined for each ability (can be 0)
- Multiple abilities can be active at the same time
The default ability implementation can be extended as needed and comes with the following properties by default:
Cooldown
: time needed to re-use abilityGlobal Cooldown
: global abilities cooldown after useRecharge
: controls if / when the ability chargesRecharge Time
: time needed to get 1 chargeMax Charges
: the amount of uses included with an ability's full chargeStart Charges
: the initial amount of charges provided to an entity when the abilitiy is addedActivation Frame
: controls if the ability is executed in predicted/verified frame
Execution Flow
An ability is not an immediate action. It is first written in into AbilitiesDesires
and executed from Update later in the same frame. The lifetime of an ability is controlled by the IsFinished
property; the ability is deactivated when the property is set to true
. The following diagram illustrates the API of the AbilityController
and its execution flow:
Example Implementation
This section exemplifies the AbilitiesSystem
by presenting the TeleportAbility
implementation included in the FPS Template.
C#
public unsafe sealed class TeleportAbilityController : AbilityController {
//========== PUBLIC MEMBERS ===================================================================================
public FP Distance = FP._10;
public FP MinimumTravelDistance = FP._1;
public AssetRefEntityPrototype Effect;
}
The TeleportAbility
includes the default properties and adds several specific ones (e.g.: Distance
). All properties can be adjusted in the Unity Inspector.
The TeleportAbility
executes the following logic when activated:
- The ability makes physics linecast to check travel distance forward.
- If the ability owner (entity) has a
Movement
component teleport is executed through this component, otherwise it writes directly to theTransform3D
component. - Optionally, an effect is added to the owner entity (for example a speed-up for few seconds)
C#
internal override bool Activate(Frame frame, EntityRef entity)
{
EntityRef ownerRef = frame.GetOwnerEntity(entity);
Transform3D* transform3D = frame.GetComponent<Transform3D>(ownerRef);
FPVector3 position = transform3D->Position;
FPVector3 direction = transform3D->Forward();
// 1. Calculate teleport destination
FPVector3 targetPosition = position + direction * Distance;
// 2. Visibility check for obstacles - simple linecast
QueryOptions options = QueryOptions.ComputeDetailedInfo | QueryOptions.HitDynamics | QueryOptions.HitKinematics | QueryOptions.HitStatics;
Physics3D.Hit3D? hit = frame.Physics3D.Linecast(position, targetPosition, ObjectLayerMask.Blocking, options);
// 3. Update destination based on hits / exit if the distance is too short
if (hit.HasValue == true)
{
direction = hit.Value.Point - position;
FP magnitude = direction.MagnitudePrecise();
if (magnitude < Distance)
{
if (magnitude < MinimumTravelDistance)
return false;
targetPosition = position + direction * FP._9 * FP._0_10;
}
}
// 4. Set Movement, otherwise direct update of Transform3D
Movement* movement = frame.TryGetComponent<Movement>(ownerRef);
if (movement != null)
{
movement->Teleport(targetPosition);
}
else
{
transform3D->Position = targetPosition;
}
// 5. Spawn an effect if linked
if (Effect.Id != 0)
{
Effects* effects = frame.TryGetComponent<Effects>(ownerRef);
if (effects != null)
{
effects->AddEffect(frame, ownerRef, ownerRef, Effect, out _);
}
}
// 6. Teleport was successful
return true;
}
Creating a New Ability
This is the step-by-step procedure to create a new Ability in the FPS Template.
- In Quantum the solution, create new
MyAbilityController
which inherits fromAbilityController
. - Add serializable fields to the controller (configuration).
- If needed, create a
MyAbility.qtn
which contains definition forMyAbility
component and other data structures such as roll-backable data. - Override base properties if needed and implement
Initialize()
,Deinitialize()
,Activate()
,Deactivate()
,Update()
,IsFinished()
methods in the controller. - Implement the available controller interfaces (e.g.
IAttributesProvider
, ...). - Recompile the
quantum.code
solution. - In Unity, create an empty GameObjectand add the visual components / model as a child object.
- Add the
Entity
,EntityPrototype
,EntityComponentController
andEntityComponentAbility
scripts to the GameObject. - On the
Entity
component, set:Transform Synchronization
toSimple
orError Correction
Require Owner
totrue
as the ability cannot exist without the owning entity
- On the
EntityComponentController
component, selectMyAbilityController
from theAbilityController
drop-down menu and fill out defined properties. - Fill out the defined properties on the
EntityComponentAbility
component - Add other entity components to define the behavior of the entity (
EntityComponentMyAbility
, ...) - Create a prefab of the entity (Optional)
- Execute the asset resource generation via the
Quantum > Generate Asset Resources
menu. - The ability is now ready to use - reference entity prototype.