This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Utilities

A collection of utility libraries — easing curves, spring dampers, physics trigger volumes, gradients, and entity helpers.

Overview

GS_Core includes a rich set of utility libraries for common game development patterns. These are header-only (or lightweight) utilities that any component or system can use without additional setup.


Features

1 - Common Enums

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Overview

PhysicsTriggeringVolume is a base class that provides everything needed to process physics trigger overlaps and collision contacts. Inherit from it to create interactive volumes — damage zones, pickup areas, dialogue triggers, environmental hazards — without writing boilerplate physics code.

It handles entity tracking (so you only get one enter/exit per entity), supports both trigger overlaps and collision contacts, and provides optional hold/persist callbacks for continuous processing.

TypeId: {A0C4C982-1B31-4FD3-9386-AD1A57ABEF8E}

How It Works

  1. Your component inherits from PhysicsTriggeringVolume (which itself inherits from AZ::Component).
  2. The base class connects to the physics trigger and collision buses automatically.
  3. When an entity enters/exits the volume, the base class tracks it and calls your override methods.
  4. Optionally enable hold updates for continuous processing while entities remain inside.

Entity Tracking

The base class maintains an internal m_entities list that tracks which entities are currently inside the volume. This ensures:

  • TriggerEnter / CollisionEnter fires only once per entity (not on every physics tick).
  • TriggerExit / CollisionExit fires only when an entity that was previously inside leaves.

API Reference

Trigger Overlap Methods

Override these to react to PhysX trigger volume events.

MethodDescription
TriggerEnter()Called when a new entity enters the trigger volume.
TriggerExit()Called when an entity leaves the trigger volume.
TriggerHold()Called on each physics tick while an entity remains inside. Requires EnableTriggerHoldUpdate to be enabled.

Collision Contact Methods

Override these to react to PhysX rigid body collision events.

MethodDescription
CollisionEnter()Called when a new entity begins colliding with this volume.
CollisionExit()Called when an entity stops colliding with this volume.
CollisionHold()Called on each physics tick while collision persists. Requires EnableTriggerHoldUpdate to be enabled via OnCollisionPersist.

Configuration

PropertyDescription
EnableTriggerHoldUpdateEnables the TriggerHold() / CollisionHold() callbacks on each physics tick. Disabled by default for performance.

Extending Common Enums

Header (.h)

#pragma once
#include <GS_Core/Utilities/PhysicsTriggeringVolume.h>

namespace MyProject
{
    class DamageZoneComponent
        : public virtual GS_Core::PhysicsTriggeringVolume
    {
    public:
        AZ_COMPONENT_DECL(DamageZoneComponent);

        static void Reflect(AZ::ReflectContext* context);

    protected:
        // IMPORTANT: Call parent Activate/Deactivate
        void Activate() override;
        void Deactivate() override;

        // Trigger overrides
        void TriggerEnter() override;
        void TriggerExit() override;
        void TriggerHold() override;

    private:
        float m_damagePerSecond = 10.0f;
    };
}

Implementation (.cpp)

#include "DamageZoneComponent.h"
#include <AzCore/Serialization/SerializeContext.h>

namespace MyProject
{
    AZ_COMPONENT_IMPL(DamageZoneComponent, "DamageZoneComponent", "{YOUR-UUID-HERE}");

    void DamageZoneComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<DamageZoneComponent, GS_Core::PhysicsTriggeringVolume>()
                ->Version(0)
                ->Field("DamagePerSecond", &DamageZoneComponent::m_damagePerSecond);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<DamageZoneComponent>(
                    "Damage Zone", "Deals damage to entities inside the trigger volume")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
                    ->DataElement(AZ::Edit::UIHandlers::Default,
                        &DamageZoneComponent::m_damagePerSecond,
                        "Damage Per Second", "How much damage to deal per second while inside");
            }
        }
    }

    void DamageZoneComponent::Activate()
    {
        // IMPORTANT: Call parent to connect to physics buses
        PhysicsTriggeringVolume::Activate();

        // Enable hold updates for continuous damage
        // EnableTriggerHoldUpdate = true;
    }

    void DamageZoneComponent::Deactivate()
    {
        // IMPORTANT: Call parent to disconnect from physics buses
        PhysicsTriggeringVolume::Deactivate();
    }

    void DamageZoneComponent::TriggerEnter()
    {
        // An entity entered the damage zone
        AZ_TracePrintf("DamageZone", "Entity entered damage zone");
    }

    void DamageZoneComponent::TriggerExit()
    {
        // An entity left the damage zone
        AZ_TracePrintf("DamageZone", "Entity exited damage zone");
    }

    void DamageZoneComponent::TriggerHold()
    {
        // Called each physics tick while entity is inside
        // Apply damage: m_damagePerSecond * deltaTime
    }
}

Module Registration

m_descriptors.insert(m_descriptors.end(), {
    MyProject::DamageZoneComponent::CreateDescriptor(),
});

Setup

  1. Create an entity with a PhysX Collider component (set as trigger).
  2. Attach your custom trigger volume component (e.g., DamageZoneComponent).
  3. Configure the collider shape and your component’s properties.
  4. Entities with rigidbodies that enter the collider will trigger your callbacks.

See Also

2 - Serialization Helpers

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Entity Utilities

Helper functions for finding entities by name.

FunctionDescription
EntityUtility::GetEntityByName(name)Returns the entity pointer for the named entity.
EntityUtility::GetEntityIdByName(name)Returns the EntityId for the named entity.

3 - Entity Helpers

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Overview

PhysicsTriggeringVolume is a base class that provides everything needed to process physics trigger overlaps and collision contacts. Inherit from it to create interactive volumes — damage zones, pickup areas, dialogue triggers, environmental hazards — without writing boilerplate physics code.

It handles entity tracking (so you only get one enter/exit per entity), supports both trigger overlaps and collision contacts, and provides optional hold/persist callbacks for continuous processing.

TypeId: {A0C4C982-1B31-4FD3-9386-AD1A57ABEF8E}

How It Works

  1. Your component inherits from PhysicsTriggeringVolume (which itself inherits from AZ::Component).
  2. The base class connects to the physics trigger and collision buses automatically.
  3. When an entity enters/exits the volume, the base class tracks it and calls your override methods.
  4. Optionally enable hold updates for continuous processing while entities remain inside.

Entity Tracking

The base class maintains an internal m_entities list that tracks which entities are currently inside the volume. This ensures:

  • TriggerEnter / CollisionEnter fires only once per entity (not on every physics tick).
  • TriggerExit / CollisionExit fires only when an entity that was previously inside leaves.

API Reference

Trigger Overlap Methods

Override these to react to PhysX trigger volume events.

MethodDescription
TriggerEnter()Called when a new entity enters the trigger volume.
TriggerExit()Called when an entity leaves the trigger volume.
TriggerHold()Called on each physics tick while an entity remains inside. Requires EnableTriggerHoldUpdate to be enabled.

Collision Contact Methods

Override these to react to PhysX rigid body collision events.

MethodDescription
CollisionEnter()Called when a new entity begins colliding with this volume.
CollisionExit()Called when an entity stops colliding with this volume.
CollisionHold()Called on each physics tick while collision persists. Requires EnableTriggerHoldUpdate to be enabled via OnCollisionPersist.

Configuration

PropertyDescription
EnableTriggerHoldUpdateEnables the TriggerHold() / CollisionHold() callbacks on each physics tick. Disabled by default for performance.

Extending Entity Helpers

Header (.h)

#pragma once
#include <GS_Core/Utilities/PhysicsTriggeringVolume.h>

namespace MyProject
{
    class DamageZoneComponent
        : public virtual GS_Core::PhysicsTriggeringVolume
    {
    public:
        AZ_COMPONENT_DECL(DamageZoneComponent);

        static void Reflect(AZ::ReflectContext* context);

    protected:
        // IMPORTANT: Call parent Activate/Deactivate
        void Activate() override;
        void Deactivate() override;

        // Trigger overrides
        void TriggerEnter() override;
        void TriggerExit() override;
        void TriggerHold() override;

    private:
        float m_damagePerSecond = 10.0f;
    };
}

Implementation (.cpp)

#include "DamageZoneComponent.h"
#include <AzCore/Serialization/SerializeContext.h>

namespace MyProject
{
    AZ_COMPONENT_IMPL(DamageZoneComponent, "DamageZoneComponent", "{YOUR-UUID-HERE}");

    void DamageZoneComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<DamageZoneComponent, GS_Core::PhysicsTriggeringVolume>()
                ->Version(0)
                ->Field("DamagePerSecond", &DamageZoneComponent::m_damagePerSecond);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<DamageZoneComponent>(
                    "Damage Zone", "Deals damage to entities inside the trigger volume")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
                    ->DataElement(AZ::Edit::UIHandlers::Default,
                        &DamageZoneComponent::m_damagePerSecond,
                        "Damage Per Second", "How much damage to deal per second while inside");
            }
        }
    }

    void DamageZoneComponent::Activate()
    {
        // IMPORTANT: Call parent to connect to physics buses
        PhysicsTriggeringVolume::Activate();

        // Enable hold updates for continuous damage
        // EnableTriggerHoldUpdate = true;
    }

    void DamageZoneComponent::Deactivate()
    {
        // IMPORTANT: Call parent to disconnect from physics buses
        PhysicsTriggeringVolume::Deactivate();
    }

    void DamageZoneComponent::TriggerEnter()
    {
        // An entity entered the damage zone
        AZ_TracePrintf("DamageZone", "Entity entered damage zone");
    }

    void DamageZoneComponent::TriggerExit()
    {
        // An entity left the damage zone
        AZ_TracePrintf("DamageZone", "Entity exited damage zone");
    }

    void DamageZoneComponent::TriggerHold()
    {
        // Called each physics tick while entity is inside
        // Apply damage: m_damagePerSecond * deltaTime
    }
}

Module Registration

m_descriptors.insert(m_descriptors.end(), {
    MyProject::DamageZoneComponent::CreateDescriptor(),
});

Setup

  1. Create an entity with a PhysX Collider component (set as trigger).
  2. Attach your custom trigger volume component (e.g., DamageZoneComponent).
  3. Configure the collider shape and your component’s properties.
  4. Entities with rigidbodies that enter the collider will trigger your callbacks.

See Also

4 - Gradients

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Gradients

Multi-stop gradient utilities for sampling color, float, or vector values along a 0.0–1.0 range.

GradientDescription
ColorGradientMulti-stop color gradient with ColorGradientMarker entries (color + position). Sample at any point for interpolated color.
FloatGradientFloat value gradient for numeric curves.
Vector2Gradient2D vector gradient.

5 - Spline Helper

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Spline Utilities

Helper functions for working with O3DE spline components.

FunctionDescription
SplineUtility::FindClosestWorldPoint(spline, worldPos)Finds the closest point on the spline in world space.
SplineUtility::FindClosestLocalPoint(spline, localPos)Finds the closest point on the spline in local space.
SplineUtility::FindClosestFraction(spline, worldPos)Returns the 0.0–1.0 fraction along the spline closest to the given world position.

6 - Curves

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Curves Utility

A library of 40+ easing curve functions for animation, transitions, and interpolation. Each curve type comes in three variants: Ease In, Ease Out, and Ease In-Out.

All functions take and return clamped values between 0.0 and 1.0 (available as both float and double).

When to Use

  • Animation timing — Control the acceleration and deceleration of animated values
  • UI transitions — Fade, slide, and scale with natural-feeling motion
  • Gameplay curves — Difficulty scaling, damage falloff, speed ramps
  • Procedural generation — Shape noise, terrain height curves, gradient falloff

Available Curves

CurveCharacter
LinearConstant speed — no easing
QuadraticGentle acceleration/deceleration
CubicModerate acceleration/deceleration
QuarticStrong acceleration/deceleration
QuinticVery strong acceleration/deceleration
SinusoidalSmooth, natural-feeling motion
ExponentialSharp acceleration, gradual deceleration (or vice versa)
CircularBased on circular arc — distinct feel
ElasticOvershoots and springs back
BackPulls back before moving forward
BounceBounces at the end

Curve Type Enum

The CurveType enum ({A9718DE5-BFD6-4206-A8B8-0DEB7B7162F2}) can be reflected to the inspector, allowing designers to select easing curves from a dropdown in the Entity Inspector.

7 - Springs

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Springs Utility

A collection of critically damped spring functions for smooth, controlled blending between current and target values. Springs are ideal for camera follow, UI animations, procedural motion, and any system that needs to track a moving target without oscillation.

Supports float, AZ::Vector2, AZ::Vector3, and AZ::Quaternion data types.

When to Use

  • Camera smoothing — Follow a target position/rotation without jitter
  • UI animation — Smoothly move UI elements to new positions
  • Procedural motion — Smooth velocity changes, weapon sway, head bob
  • Value blending — Smoothly interpolate any numeric value toward a target

Available Springs

SpringDescription
SimpleSpringBasic critically damped spring. Good for most smoothing needs.
AccelerationSpringSpring with acceleration control. Good for smooth starts.
DoubleSpringTwo-pass spring for extra-smooth results. Good for camera follow.
TimedSpringSpring that reaches the target in a specified time. Good for UI transitions.
VelocitySpringSpring with explicit velocity tracking. Good for physics-driven motion.
QuaternionSpringRotation-specific spring. Good for camera and character rotation smoothing.

8 - Angles Helper

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Angle/Orientation Utilities

Helper functions for angle-based section mapping and orientation conversion.

FunctionDescription
SectionByAngle::PickByAngle(angle, config)Maps an angle to a section index using a predefined config. Useful for 4-directional or 8-directional animation selection.
SectionByAngle::YawFromDir(direction)Converts a direction vector to a yaw angle.
SectionByAngle::QuatFromYaw(yaw)Converts a yaw angle to a quaternion rotation.

Includes 22 predefined SectionConfig presets for common angle-to-section mapping patterns (4-way, 8-way, etc.).

9 - Physics Trigger Volume

A base class for physics trigger and collision handling — inherit to create interactive volumes with enter, exit, and hold callbacks.

Overview

PhysicsTriggeringVolume is a base class that provides everything needed to process physics trigger overlaps and collision contacts. Inherit from it to create interactive volumes — damage zones, pickup areas, dialogue triggers, environmental hazards — without writing boilerplate physics code.

It handles entity tracking (so you only get one enter/exit per entity), supports both trigger overlaps and collision contacts, and provides optional hold/persist callbacks for continuous processing.

TypeId: {A0C4C982-1B31-4FD3-9386-AD1A57ABEF8E}

How It Works

  1. Your component inherits from PhysicsTriggeringVolume (which itself inherits from AZ::Component).
  2. The base class connects to the physics trigger and collision buses automatically.
  3. When an entity enters/exits the volume, the base class tracks it and calls your override methods.
  4. Optionally enable hold updates for continuous processing while entities remain inside.

Entity Tracking

The base class maintains an internal m_entities list that tracks which entities are currently inside the volume. This ensures:

  • TriggerEnter / CollisionEnter fires only once per entity (not on every physics tick).
  • TriggerExit / CollisionExit fires only when an entity that was previously inside leaves.

API Reference

Trigger Overlap Methods

Override these to react to PhysX trigger volume events.

MethodDescription
TriggerEnter()Called when a new entity enters the trigger volume.
TriggerExit()Called when an entity leaves the trigger volume.
TriggerHold()Called on each physics tick while an entity remains inside. Requires EnableTriggerHoldUpdate to be enabled.

Collision Contact Methods

Override these to react to PhysX rigid body collision events.

MethodDescription
CollisionEnter()Called when a new entity begins colliding with this volume.
CollisionExit()Called when an entity stops colliding with this volume.
CollisionHold()Called on each physics tick while collision persists. Requires EnableTriggerHoldUpdate to be enabled via OnCollisionPersist.

Configuration

PropertyDescription
EnableTriggerHoldUpdateEnables the TriggerHold() / CollisionHold() callbacks on each physics tick. Disabled by default for performance.

Extending Physics Trigger Volume

Header (.h)

#pragma once
#include <GS_Core/Utilities/PhysicsTriggeringVolume.h>

namespace MyProject
{
    class DamageZoneComponent
        : public virtual GS_Core::PhysicsTriggeringVolume
    {
    public:
        AZ_COMPONENT_DECL(DamageZoneComponent);

        static void Reflect(AZ::ReflectContext* context);

    protected:
        // IMPORTANT: Call parent Activate/Deactivate
        void Activate() override;
        void Deactivate() override;

        // Trigger overrides
        void TriggerEnter() override;
        void TriggerExit() override;
        void TriggerHold() override;

    private:
        float m_damagePerSecond = 10.0f;
    };
}

Implementation (.cpp)

#include "DamageZoneComponent.h"
#include <AzCore/Serialization/SerializeContext.h>

namespace MyProject
{
    AZ_COMPONENT_IMPL(DamageZoneComponent, "DamageZoneComponent", "{YOUR-UUID-HERE}");

    void DamageZoneComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<DamageZoneComponent, GS_Core::PhysicsTriggeringVolume>()
                ->Version(0)
                ->Field("DamagePerSecond", &DamageZoneComponent::m_damagePerSecond);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<DamageZoneComponent>(
                    "Damage Zone", "Deals damage to entities inside the trigger volume")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
                    ->DataElement(AZ::Edit::UIHandlers::Default,
                        &DamageZoneComponent::m_damagePerSecond,
                        "Damage Per Second", "How much damage to deal per second while inside");
            }
        }
    }

    void DamageZoneComponent::Activate()
    {
        // IMPORTANT: Call parent to connect to physics buses
        PhysicsTriggeringVolume::Activate();

        // Enable hold updates for continuous damage
        // EnableTriggerHoldUpdate = true;
    }

    void DamageZoneComponent::Deactivate()
    {
        // IMPORTANT: Call parent to disconnect from physics buses
        PhysicsTriggeringVolume::Deactivate();
    }

    void DamageZoneComponent::TriggerEnter()
    {
        // An entity entered the damage zone
        AZ_TracePrintf("DamageZone", "Entity entered damage zone");
    }

    void DamageZoneComponent::TriggerExit()
    {
        // An entity left the damage zone
        AZ_TracePrintf("DamageZone", "Entity exited damage zone");
    }

    void DamageZoneComponent::TriggerHold()
    {
        // Called each physics tick while entity is inside
        // Apply damage: m_damagePerSecond * deltaTime
    }
}

Module Registration

m_descriptors.insert(m_descriptors.end(), {
    MyProject::DamageZoneComponent::CreateDescriptor(),
});

Setup

  1. Create an entity with a PhysX Collider component (set as trigger).
  2. Attach your custom trigger volume component (e.g., DamageZoneComponent).
  3. Configure the collider shape and your component’s properties.
  4. Entities with rigidbodies that enter the collider will trigger your callbacks.

See Also

10 - GS_Random

Random utilities — weighted random selection and deterministic random value generation for procedural and gameplay systems.

Overview

The Random utilities provide weighted random selection for gameplay and procedural systems. The primary utility is GetRandomWeighted, a template function that selects an item from a collection based on assigned weights — higher weights mean higher probability of selection.

API Reference

RandomUtils (Template Utility)

FunctionDescription
GetRandomWeighted<T>(collection)Selects a random item from a weighted collection. Each item must provide a weight value. Items with higher weights are proportionally more likely to be selected.

Usage Example

#include <GS_Core/Utilities/RandomUtils.h>

// Define weighted items (e.g., loot drops)
struct LootEntry
{
    AZStd::string itemName;
    float weight; // Higher = more common
};

AZStd::vector<LootEntry> lootTable = {
    { "Common Sword",   50.0f },
    { "Rare Shield",    30.0f },
    { "Epic Helmet",    15.0f },
    { "Legendary Ring",  5.0f },
};

// Select a random item weighted by drop rate
auto selected = GS_Core::RandomUtils::GetRandomWeighted(lootTable);
// "Common Sword" is 10x more likely than "Legendary Ring"

See Also

11 - GS_Actions

A utility system for triggerable single-purpose actions — fire discrete behaviors from any system without recoding logic for each component.

Overview

The Actions system provides a simple, universal pattern for triggering discrete behaviors. Instead of recoding common functionality (toggle cursor, play sound, print log) into every component that needs it, you attach Action components to an entity and fire them by channel name.

Actions support chaining — when one action completes, it can fire another action on a different channel, enabling lightweight sequences without custom code.

Architecture

Entity with Actions
  ├── ToggleMouseCursor_GSAction (channel: "enter_menu")
  ├── PlaySound_GSAction           (channel: "enter_menu")
  └── PrintLog_GSAction            (channel: "debug")

Any system calls:
  ActionIncomingEventBus::Event(entityId, DoAction, "enter_menu")
    → Both ToggleMouseCursor and PlaySound fire (matching channel)
    → PrintLog does NOT fire (different channel)

Components

ComponentPurposeDocumentation
GS_ActionComponentBase class for all actions. Handles channel matching, completion broadcasting, and action chaining.Action
ToggleMouseCursor_GSActionComponentToggles the mouse cursor on or off.Core Actions
PrintLog_GSActionComponentPrints a message to the console log.Core Actions

Quick Start

  1. Attach one or more Action components to an entity.
  2. Set the Channel on each action to control when it fires.
  3. From any system, call DoAction(channelName) on the entity’s ActionIncomingEventBus to trigger matching actions.
  4. Optionally enable Broadcast On Complete and set an On Complete Channel Chain for action sequencing.

See Also

11.1 - Action

The base class for triggerable actions — channel-based firing, completion broadcasting, and action chaining for lightweight behavior sequencing.

Overview

GS_ActionComponent is the base class for all triggerable actions. Actions are single-purpose behaviors that fire when their channel matches an incoming DoAction call. They support completion broadcasting and optional chaining to sequence multiple actions together.

Every GS_Play action (Toggle Mouse Cursor, Print Log, and actions from other gems) extends this class. When you need a custom action, you extend it too.

How It Works

Firing an Action

  1. A system calls DoAction(targetChannel) on an entity’s ActionIncomingEventBus.
  2. Every Action component on that entity evaluates whether its channel matches the targetChannel.
  3. Matching actions call ProcessAction() to execute their behavior.
  4. When done, CompleteAction() handles completion:
    • If Broadcast On Complete is enabled, it fires OnActionComplete on the ActionOutgoingEventBus.
    • If On Complete Channel Chain is set, it fires a new DoAction with that channel name, triggering the next action in the chain.

Chaining Actions

By setting the On Complete Channel Chain to a different channel name, an action can trigger the next step in a sequence when it completes. This enables lightweight behavior chains without custom sequencing code.

Action A (channel: "step1", onCompleteChain: "step2")
  → fires → Action B (channel: "step2", onCompleteChain: "step3")
    → fires → Action C (channel: "step3")

Important: Every custom action must call CompleteAction() when its work is done. If you forget, the action will never complete and chains will break.


Inspector Properties

PropertyTypeDefaultDescription
ChannelAZStd::string""The channel this action responds to. Only DoAction calls with a matching channel will trigger this action.
Broadcast On CompleteboolfalseWhen enabled, broadcasts OnActionComplete on the ActionOutgoingEventBus when this action finishes.
On Complete Channel ChainAZStd::string""When non-empty, fires a new DoAction with this channel name after completing. Enables action chaining.

API Reference

Request Bus: ActionIncomingEventBus

Entity-addressed bus — call via Event(entityId, ...).

MethodParametersReturnsDescription
DoActionAZStd::string targetChannelvoidTriggers all actions on the target entity whose channel matches targetChannel.

Notification Bus: ActionOutgoingEventBus

Entity-addressed bus — connect via BusConnect(entityId).

EventParametersDescription
OnActionCompleteBroadcast when an action with Broadcast On Complete enabled finishes its work.

Local / Virtual Methods

Override these when creating custom actions.

MethodDescription
DoAction(targetChannel)Evaluates whether this action should fire. Default compares channel to targetChannel. Override for custom evaluation logic.
ProcessAction()Your primary override. Executes the action’s behavior. Called when channel evaluation passes.
CompleteAction()Handles completion. Default checks broadcastOnComplete, fires OnActionComplete, then checks onCompleteChannelChain for chaining. Override to add custom completion logic, but always call base or handle chaining manually.

Usage Examples

Firing Actions on an Entity

#include <GS_Core/GS_CoreBus.h>

// Fire all actions on this entity that match the "open_door" channel
GS_Core::ActionIncomingEventBus::Event(
    targetEntityId,
    &GS_Core::ActionIncomingEventBus::Events::DoAction,
    AZStd::string("open_door")
);

Listening for Action Completion

#include <GS_Core/GS_CoreBus.h>

class MyActionListener
    : public AZ::Component
    , protected GS_Core::ActionOutgoingEventBus::Handler
{
protected:
    void Activate() override
    {
        // Listen for action completions on a specific entity
        GS_Core::ActionOutgoingEventBus::Handler::BusConnect(m_targetEntityId);
    }

    void Deactivate() override
    {
        GS_Core::ActionOutgoingEventBus::Handler::BusDisconnect();
    }

    void OnActionComplete() override
    {
        // An action on the target entity has completed
        // React accordingly (advance dialogue, open next door, etc.)
    }

private:
    AZ::EntityId m_targetEntityId;
};

Extending the Action Class

Create a custom action whenever you need a reusable, triggerable behavior that can be attached to any entity.

Header (.h)

#pragma once
#include <Source/ActionSystem/GS_ActionComponent.h>

namespace MyProject
{
    class PlayEffect_GSActionComponent
        : public GS_Core::GS_ActionComponent
    {
    public:
        AZ_COMPONENT_DECL(PlayEffect_GSActionComponent);

        static void Reflect(AZ::ReflectContext* context);

    protected:
        // Action overrides
        void ProcessAction() override;
        void CompleteAction() override;

    private:
        AZStd::string m_effectName;
        float m_duration = 1.0f;
    };
}

Implementation (.cpp)

#include "PlayEffect_GSActionComponent.h"
#include <AzCore/Serialization/SerializeContext.h>

namespace MyProject
{
    AZ_COMPONENT_IMPL(PlayEffect_GSActionComponent, "PlayEffect_GSActionComponent", "{YOUR-UUID-HERE}",
        GS_Core::GS_ActionComponent);

    void PlayEffect_GSActionComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<PlayEffect_GSActionComponent, GS_Core::GS_ActionComponent>()
                ->Version(0)
                ->Field("EffectName", &PlayEffect_GSActionComponent::m_effectName)
                ->Field("Duration", &PlayEffect_GSActionComponent::m_duration);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                // Pattern: Incoming Channel → Feature Properties → Outgoing Channel
                editContext->Class<PlayEffect_GSActionComponent>(
                    "Play Effect Action", "Triggers a named particle effect")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject/Actions")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
                    ->DataElement(AZ::Edit::UIHandlers::Default,
                        &PlayEffect_GSActionComponent::m_effectName, "Effect Name",
                        "Name of the particle effect to trigger")
                    ->DataElement(AZ::Edit::UIHandlers::Default,
                        &PlayEffect_GSActionComponent::m_duration, "Duration",
                        "How long the effect plays (seconds)");
            }
        }
    }

    void PlayEffect_GSActionComponent::ProcessAction()
    {
        // Your action logic — trigger the particle effect
        AZ_TracePrintf("Action", "Playing effect: %s for %.1f seconds",
            m_effectName.c_str(), m_duration);

        // ... spawn particle effect, start timer, etc. ...

        // IMPORTANT: Call CompleteAction when done
        // If the action is instant, call it here.
        // If it takes time (animation, timer), call it when the effect finishes.
        CompleteAction();
    }

    void PlayEffect_GSActionComponent::CompleteAction()
    {
        // Custom completion logic (if any)
        // ...

        // IMPORTANT: Call base to handle broadcast and chaining
        GS_ActionComponent::CompleteAction();
    }
}

Module Registration

m_descriptors.insert(m_descriptors.end(), {
    MyProject::PlayEffect_GSActionComponent::CreateDescriptor(),
});

Reflection Pattern

When reflecting custom action properties in the Edit Context, follow this organization pattern so all actions have a consistent inspector layout:

  1. Incoming Channel — The channel property (inherited, reflected by base class)
  2. Feature Properties — Your action-specific properties (effect name, duration, etc.)
  3. Outgoing Channel — The broadcastOnComplete and onCompleteChannelChain properties (inherited, reflected by base class)

See Also

11.1.1 - Core Actions

Pre-built actions included in GS_Core — ready-to-use triggerable behaviors for common game functionality.

Overview

GS_Core includes pre-built Action components for common behaviors. Attach them to any entity, set a channel, and fire them from any system without writing code.

All built-in actions inherit from GS_ActionComponent and follow the same channel-matching, completion, and chaining patterns.


Toggle Mouse Cursor

Toggles the operating system mouse cursor on or off.

When to Use

Use this action when transitioning between gameplay (cursor hidden) and menus (cursor visible). Common triggers include opening an inventory, entering a pause menu, or starting a dialogue sequence.

Inspector Properties

PropertyTypeDefaultDescription
ChannelAZStd::string""Inherited. The channel this action responds to.
Broadcast On CompleteboolfalseInherited. Broadcasts OnActionComplete when the toggle finishes.
On Complete Channel ChainAZStd::string""Inherited. Fires a follow-up DoAction on completion for chaining.

Usage Example

#include <GS_Core/GS_CoreBus.h>

// Toggle the cursor when opening the pause menu
GS_Core::ActionIncomingEventBus::Event(
    uiEntityId,
    &GS_Core::ActionIncomingEventBus::Events::DoAction,
    AZStd::string("toggle_cursor")
);

Prints a configurable message to the console log. Useful for debugging action chains, verifying event flow, or logging gameplay milestones.

When to Use

Use this action during development to verify that action channels fire correctly, test chaining sequences, or log gameplay events without writing custom components.

Inspector Properties

PropertyTypeDefaultDescription
ChannelAZStd::string""Inherited. The channel this action responds to.
MessageAZStd::string""The message to print to the console log.
Broadcast On CompleteboolfalseInherited. Broadcasts OnActionComplete when the print finishes.
On Complete Channel ChainAZStd::string""Inherited. Fires a follow-up DoAction on completion for chaining.

Usage Example

#include <GS_Core/GS_CoreBus.h>

// Trigger a debug log message
GS_Core::ActionIncomingEventBus::Event(
    debugEntityId,
    &GS_Core::ActionIncomingEventBus::Events::DoAction,
    AZStd::string("debug")
);
// Console output: whatever message was configured on the PrintLog action

Creating Your Own Action

Need custom behavior? See the Extending the Action Class guide for a complete walkthrough with header, implementation, Reflect pattern, and module registration.

See Also

  • Action — Base class documentation and extension guide
  • GS_Actions — System overview