Systems
Core framework systems — the GS_Motion track-based animation engine and the GS_Actions triggerable behavior system.
For usage guides and setup examples, see The Basics: GS_Core.
Contents
GS_Motion
The track-based animation and tween engine. GS_Motion defines abstract base classes for tracks, assets, composites, and proxies. Domain gems (GS_UI, GS_Juice) extend it with concrete track types and custom asset formats. The system handles playback timing, per-track easing, proxy-based entity targeting, and deep-copy runtime instancing.
GS_Motion
API
See Also
For conceptual overviews and usage guides:
For related resources:
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
1 - GS_Motion
Track-based animation and tween system — abstract base classes for motions, tracks, composites, assets, and proxies with a domain extension pattern.
GS_Motion is the framework’s central track-based animation system. It defines abstract base classes that domain gems extend with concrete track types — GS_UI extends it with 8 UI animation tracks (.uiam assets), and GS_Juice extends it with 2 feedback tracks (.feedbackmotion assets). The system handles playback timing, per-track easing, proxy-based entity targeting, and deep-copy runtime instancing from data assets.
For usage guides and setup examples, see The Basics: GS_Core.
Contents
Architecture
GS_MotionTrack (abstract base)
├── UiMotionTrack (GS_UI domain base)
│ ├── UiPositionTrack
│ ├── UiScaleTrack
│ ├── UiRotationTrack
│ ├── UiElementAlphaTrack
│ ├── UiImageAlphaTrack
│ ├── UiImageColorTrack
│ ├── UiTextColorTrack
│ └── UiTextSizeTrack
└── FeedbackMotionTrack (GS_Juice domain base)
├── FeedbackTransformTrack
└── FeedbackMaterialTrack
GS_MotionAsset (abstract base)
├── UiAnimationMotionAsset (.uiam)
└── FeedbackMotionAsset (.feedbackmotion)
GS_MotionComposite (runtime instance)
└── Created by asset's CreateRuntimeComposite()
Core Classes
| Class | Description | Page |
|---|
| GS_MotionTrack | Abstract base for all animation tracks — fields, lifecycle, virtual methods, extension guide. | MotionTrack |
| GS_Motion | Playback engine — ticks tracks, computes per-track progress windows, applies easing, manages lifecycle. | Motion Engine |
| GS_MotionComposite | Runtime deep-copy instance with proxy entity overrides — created from GS_MotionAsset. | MotionComposite |
| GS_MotionAsset | Abstract base for motion data assets — holds track definitions and creates runtime composites. | MotionAsset |
| GS_MotionProxy | Serialized struct for track-to-entity redirection — allows designers to target named tracks at different entities. | MotionProxy |
Domain Extensions
GS_Motion is designed to be extended by domain gems. Each domain creates its own track hierarchy, asset type, and file extension.
| Domain | Gem | Base Track | Concrete Tracks | Asset Extension |
|---|
| UI Animation | GS_UI | UiMotionTrack | Position, Scale, Rotation, ElementAlpha, ImageAlpha, ImageColor, TextColor, TextSize (8) | .uiam |
| Feedback | GS_Juice | FeedbackMotionTrack | FeedbackTransformTrack, FeedbackMaterialTrack (2) | .feedbackmotion |
Extension Pattern
To create a new motion domain:
- Create a domain base track —
class MyTrack : public GS_Core::GS_MotionTrack - Create concrete tracks extending the domain base — each overrides
Update(float easedProgress) and GetTypeName() - Create a domain asset —
class MyAsset : public GS_Core::GS_MotionAsset with vector<MyTrack*> m_tracks - Create an instance wrapper struct (not a component) — holds
Asset<MyAsset> + vector<GS_MotionProxy>, manages Initialize / Unload / Play / Stop - Embed the wrapper — components embed the instance wrapper struct as a serialized field
Critical: All track vectors must use raw pointers: vector<MyTrack*>. Never use unique_ptr — O3DE SerializeContext requires raw pointers for polymorphic enumeration in the asset editor.
See Also
For conceptual overviews and usage guides:
For class references:
For domain extensions:
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
1.1 - GS_MotionTrack
Abstract base class for all animation tracks — fields, lifecycle, and virtual methods for domain extension.
GS_MotionTrack is the abstract base class for all animation tracks in the GS_Motion system. Each track animates one aspect of an entity over a time window within a motion. Domain gems extend this with concrete track types — GS_UI provides 8 LyShine tracks, GS_Juice provides 2 feedback tracks.
For usage guides and setup examples, see The Basics: GS_Core.
Fields
| Field | Type | Description |
|---|
| m_id | AZ::Uuid | Unique track identifier (auto-generated). |
| m_identifier | AZStd::string | Proxy label — if set, the track appears in the proxy list for entity override targeting. |
| curveType | CurveType | Easing curve applied to track progress (from the Curves utility library). |
| startTime | float | Time offset before the track begins playing (seconds). |
| duration | float | Track playback duration (seconds). |
| startVarianceMin | float | Minimum random variance added to start time. |
| startVarianceMax | float | Maximum random variance added to start time. |
Lifecycle
Each track goes through a fixed lifecycle managed by the parent GS_Motion:
| Phase | Method | Description |
|---|
| Initialize | Init(AZ::EntityId owner) | Stores the owner entity. Called once when the motion is initialized. |
| Start | Start() | Called when the track’s time window begins. |
| Update | Update(float easedProgress) | Called each frame with eased progress (0 → 1). Override this in concrete tracks. |
| End | End() | Called when the track’s time window completes. |
| Unload | Unload() | Cleanup. Called when the motion is unloaded. |
Virtual Methods
These methods must be overridden in concrete track implementations:
| Method | Returns | Description |
|---|
Update(float easedProgress) | void | Apply the animation at the given eased progress value (0 → 1). |
GetTypeName() | AZStd::string | Return the track’s display name for proxy labels in the editor. |
Extension Guide
To create a new domain of animation tracks:
- Create a domain base track:
class MyTrack : public GS_Core::GS_MotionTrack — this serves as the common base for all tracks in your domain. - Create concrete tracks extending the domain base — each overrides
Update(float easedProgress) to animate a specific property. - Reflect the track class using O3DE’s
SerializeContext and EditContext. The system discovers the new type automatically.
Critical: Track vectors must use raw pointers (vector<MyTrack*>). Never use unique_ptr — O3DE SerializeContext requires raw pointers for polymorphic enumeration in the asset editor.
See Also
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
1.2 - GS_Motion
The playback engine — ticks through tracks, computes per-track progress windows, and manages motion lifecycle.
GS_Motion is the playback engine that drives animation tracks. It ticks through all tracks each frame, computes per-track progress windows based on start time and duration, applies easing curves, and calls Update(easedProgress) on each active track. It handles motion lifecycle from initialization through completion.
For usage guides and setup examples, see The Basics: GS_Core.
API Reference
| Method | Parameters | Returns | Description |
|---|
Play | — | void | Begin playback from the start. |
PlayWithCallback | AZStd::function<void()> cb | void | Play and invoke callback on completion. |
Stop | — | void | Stop playback immediately. |
Initialize | AZ::EntityId owner | void | Initialize all tracks with the owner entity. |
Unload | — | void | Unload all tracks and clean up. |
Fields
| Field | Type | Description |
|---|
| motionName | AZStd::string | Display name for the motion. |
| tracks | vector<GS_MotionTrack*> | The tracks in this motion. |
| loop | bool | Whether playback loops. |
| onComplete | function<void()> | Completion callback (set via PlayWithCallback). |
How It Works
Each frame during playback:
- The motion calculates the global elapsed time.
- For each track, it computes the track-local progress based on
startTime and duration. - If the track is within its active window, the progress is eased using the track’s
curveType. Update(easedProgress) is called on the track with the eased value (0 → 1).- When all tracks complete,
OnMotionComplete() is called.
Virtual Methods
| Method | Description |
|---|
OnMotionComplete | Called when playback finishes. Override in subclasses for custom teardown. |
See Also
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
1.3 - GS_MotionComposite
Runtime deep-copy motion instance with proxy entity overrides — created from GS_MotionAsset for per-entity playback.
GS_MotionComposite is the runtime instance of a motion, created from a GS_MotionAsset. It deep-copies all tracks so each entity gets independent playback state, and applies proxy overrides to redirect specific tracks to different entities in the hierarchy.
For usage guides and setup examples, see The Basics: GS_Core.
GS_MotionComposite extends GS_Motion. When an asset’s CreateRuntimeComposite() is called, all tracks are SC-cloned (serialization-context cloned) into a new composite instance. This ensures each entity playing the same motion has its own independent track state — no shared mutation.
API Reference
| Method | Parameters | Returns | Description |
|---|
ApplyProxies | AZ::EntityId owner, vector<GS_MotionProxy> proxies | void | Matches proxy trackIds to tracks and overrides the target entity for each matched track. |
How It Works
- Creation:
GS_MotionAsset::CreateRuntimeComposite() deep-copies all asset tracks into a new composite. - Proxy Application:
ApplyProxies() walks the proxy list, matching each proxy’s m_trackId to a track’s m_id. Matched tracks redirect their owner entity to the proxy’s m_proxyEntity. - Playback: The composite plays exactly like a regular GS_Motion — it ticks tracks, applies easing, and calls
Update(). - Cleanup: On
Unload(), the composite cleans up all deep-copied tracks.
See Also
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
1.4 - GS_MotionAsset
Abstract base for motion data assets — holds track definitions and creates runtime composites.
GS_MotionAsset is the abstract base class for motion data assets. Domain gems extend this with their own asset type and file extension — GS_UI creates UiAnimationMotionAsset (.uiam), GS_Juice creates FeedbackMotionAsset (.feedbackmotion). Assets are created and edited in the O3DE Asset Editor.
Extends AZ::Data::AssetData. All subclasses require GS_AssetReflectionIncludes.h in their header — see Serialization Helpers.
For usage guides and setup examples, see The Basics: GS_Core.
API Reference
| Method | Returns | Description |
|---|
GetTrackInfos | vector<GS_TrackInfo> | Returns track UUID + label pairs for proxy list synchronization in the editor. |
CreateRuntimeComposite | GS_MotionComposite* | Factory — deep-copies all asset tracks into a new runtime GS_MotionComposite instance. |
GS_TrackInfo
Lightweight struct used for proxy synchronization between asset tracks and instance proxy lists.
| Field | Type | Description |
|---|
| id | AZ::Uuid | Track identifier (matches the track’s m_id). |
| label | AZStd::string | Track display name (from GetTypeName()). |
Domain Extensions
| Domain | Gem | Asset Class | Extension | Tracks |
|---|
| UI Animation | GS_UI | UiAnimationMotionAsset | .uiam | 8 LyShine-specific tracks |
| Feedback | GS_Juice | FeedbackMotionAsset | .feedbackmotion | 2 feedback tracks |
Extension Guide
To create a new domain asset type:
- Create
class MyAsset : public GS_Core::GS_MotionAsset with vector<MyDomainTrack*> m_tracks. Include GS_AssetReflectionIncludes.h in your asset’s header — see Serialization Helpers. - Implement
GetTrackInfos() — iterate tracks and return UUID + label pairs. - Implement
CreateRuntimeComposite() — deep-copy tracks into a new GS_MotionComposite. - Register the asset type in your gem’s
DataAssetsSystemComponent. - Add a
.setreg entry for the asset processor to recognize your file extension.
Critical: Track vectors must use raw pointers (vector<MyTrack*>). O3DE SerializeContext requires raw pointers for polymorphic enumeration.
See Also
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
1.5 - GS_MotionProxy
Serialized struct for track-to-entity redirection — allows designers to target named tracks at different entities.
GS_MotionProxy is a serialized struct that allows designers to redirect a named track to a different entity in the hierarchy. This enables a single motion asset to animate multiple entities — for example, a UI animation that moves one element while fading another.
For usage guides and setup examples, see The Basics: GS_Core.
Fields
| Field | Type | Description |
|---|
| m_trackId | AZ::Uuid | References the track’s m_id. Matched during ApplyProxies(). |
| m_label | AZStd::string | Read-only display label (synced from track info at edit time via GetTrackInfos()). |
| m_proxyEntity | AZ::EntityId | The entity this track should target instead of the motion’s owner entity. |
How It Works
- Edit time: The proxy list syncs from the asset’s track info. Each track with an
m_identifier (non-empty label) appears as a proxy slot in the inspector. - Designer assignment: The designer drags an entity reference into the proxy’s
m_proxyEntity field. - Runtime: When
GS_MotionComposite::ApplyProxies() runs, it matches each proxy’s m_trackId to a track’s m_id and overrides that track’s target entity. - Playback: The track now animates the proxy entity instead of the motion’s owner.
Usage
Proxies are embedded in instance wrapper structs (e.g., UiAnimationMotion, FeedbackMotion) alongside the asset reference. Components serialize the proxy list, and the wrapper handles synchronization with the asset.
Only tracks with a non-empty m_identifier appear in the proxy list. Tracks without an identifier always animate the owner entity.
See Also
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
2 - GS_Actions
A utility system for triggerable single-purpose actions — fire discrete behaviors from any system without recoding logic for each component.
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.
For usage guides and setup examples, see The Basics: GS_Core.
Architecture
Entity with Actions
│
├── ToggleMouseCursor_GSAction (channel: "enter_menu")
├── PlaySound_GSAction (channel: "enter_menu")
└── PrintLog_GSAction (channel: "debug")
Any system calls:
ActionRequestBus::Event(entityId, DoAction, "enter_menu")
→ Both ToggleMouseCursor and PlaySound fire (matching channel)
→ PrintLog does NOT fire (different channel)
Components
| Component | Purpose | Documentation |
|---|
| GS_ActionComponent | Base class for all actions. Handles channel matching, completion broadcasting, and action chaining. | Action |
| ToggleMouseCursor_GSActionComponent | Toggles the mouse cursor on or off. | Core Actions |
| PrintLog_GSActionComponent | Prints a message to the console log. | Core Actions |
Quick Start
- Attach one or more Action components to an entity.
- Set the Channel on each action to control when it fires.
- From any system, call
DoAction(channelName) on the entity’s ActionRequestBus to trigger matching actions. - Optionally enable Broadcast On Complete and set an On Complete Channel Chain for action sequencing.
See Also
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
2.1 - Action
The base class for triggerable actions — channel-based firing, completion broadcasting, and action chaining for lightweight behavior sequencing.
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.
For usage guides and setup examples, see The Basics: GS_Core.
Contents
How It Works
Firing an Action
- A system calls
DoAction(targetChannel) on an entity’s ActionRequestBus. - Every Action component on that entity evaluates whether its channel matches the
targetChannel. - Matching actions call
ProcessAction() to execute their behavior. - When done,
CompleteAction() handles completion:- If Broadcast On Complete is enabled, it fires
OnActionComplete on the ActionNotificationBus. - 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
| Property | Type | Default | Description |
|---|
| Channel | AZStd::string | "" | The channel this action responds to. Only DoAction calls with a matching channel will trigger this action. |
| Broadcast On Complete | bool | false | When enabled, broadcasts OnActionComplete on the ActionNotificationBus when this action finishes. |
| On Complete Channel Chain | AZStd::string | "" | When non-empty, fires a new DoAction with this channel name after completing. Enables action chaining. |
API Reference
Request Bus: ActionRequestBus
Entity-addressed bus — call via Event(entityId, ...).
| Method | Parameters | Returns | Description |
|---|
DoAction | AZStd::string targetChannel | void | Triggers all actions on the target entity whose channel matches targetChannel. |
Notification Bus: ActionNotificationBus
Entity-addressed bus — connect via BusConnect(entityId).
| Event | Parameters | Description |
|---|
OnActionComplete | — | Broadcast when an action with Broadcast On Complete enabled finishes its work. |
Local / Virtual Methods
Override these when creating custom actions.
| Method | Description |
|---|
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::ActionRequestBus::Event(
targetEntityId,
&GS_Core::ActionRequestBus::Events::DoAction,
AZStd::string("open_door")
);
Listening for Action Completion
#include <GS_Core/GS_CoreBus.h>
class MyActionListener
: public AZ::Component
, protected GS_Core::ActionNotificationBus::Handler
{
protected:
void Activate() override
{
// Listen for action completions on a specific entity
GS_Core::ActionNotificationBus::Handler::BusConnect(m_targetEntityId);
}
void Deactivate() override
{
GS_Core::ActionNotificationBus::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.
#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:
- Incoming Channel — The
channel property (inherited, reflected by base class) - Feature Properties — Your action-specific properties (effect name, duration, etc.)
- Outgoing Channel — The
broadcastOnComplete and onCompleteChannelChain properties (inherited, reflected by base class)
See Also
For component references:
For related resources:
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.
2.1.1 - Core Actions
Pre-built actions included in GS_Core — ready-to-use triggerable behaviors for common game functionality.
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.
For usage guides and setup examples, see The Basics: GS_Core.
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
| Property | Type | Default | Description |
|---|
| Channel | AZStd::string | "" | Inherited. The channel this action responds to. |
| Broadcast On Complete | bool | false | Inherited. Broadcasts OnActionComplete when the toggle finishes. |
| On Complete Channel Chain | AZStd::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::ActionRequestBus::Event(
uiEntityId,
&GS_Core::ActionRequestBus::Events::DoAction,
AZStd::string("toggle_cursor")
);
Print Log
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
| Property | Type | Default | Description |
|---|
| Channel | AZStd::string | "" | Inherited. The channel this action responds to. |
| Message | AZStd::string | "" | The message to print to the console log. |
| Broadcast On Complete | bool | false | Inherited. Broadcasts OnActionComplete when the print finishes. |
| On Complete Channel Chain | AZStd::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::ActionRequestBus::Event(
debugEntityId,
&GS_Core::ActionRequestBus::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
Get GS_Core
GS_Core — Explore this gem on the product page and add it to your project.