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

Return to the regular view of this page.

Framework

1 - GS_Core

The foundation gem for the GS_Play framework — game lifecycle, save system, stage management, input, actions, and utility libraries.

Overview

GS_Core is the foundation of the GS_Play framework. Every other GS gem depends on it. It provides the game lifecycle management, persistence, level loading, input handling, triggerable actions, and a rich utility library that all other systems build upon.

If you are building anything with GS_Play, you are using GS_Core.


Quick Start

  1. Follow the Installation Guide to add GS_Core to your project.
  2. Set up your project environment (collision layers, physics config).
  3. Create a Game Manager prefab and place it in your level.
  4. Add the managers you need (Save, Stage, Options) to the Game Manager’s startup list.
  5. Refer to the Core Set Up Guide for detailed walkthrough.

Features

SubsystemComponents
GS_ManagersGame Manager, Manager (Base)
GS_SaveSave Manager, Savers, Record Keeper
GS_StageManagerStage Manager, Stage Data
GS_OptionsOptions Manager, Input Profiles
GS_ActionsAction (Base), Built-in Actions
UtilitiesPhysics Trigger Volume, Springs, Curves

See Also

Subsystems

1.1 - Setup

Setting up the gem.

Overview

GS_Core includes template files that give you a head start when creating custom components. Each template provides a pre-structured C++ class (header, implementation, and interface) or a Script Graph file that follows the GS_Play patterns and conventions.

All C++ templates are unregistered — after copying them into your project, you need to complete the standard O3DE Component Setup: update your module descriptor and CMakeLists.txt.

1.2 - GS_Managers

The game lifecycle management system — startup sequencing, systemic navigation, and the extensible manager pattern.

Overview

The Managers system is the backbone of every GS_Play project. It provides a controlled startup sequence that initializes game systems in the right order, global access to those systems via EBus, and systemic navigation (New Game, Load Game, Quit) from a single point of control.

Every GS_Play gem that has a manager component (Save, Stage, UI, Unit, Camera, Audio, etc.) plugs into this system automatically.


Architecture

GameManager Entity Hierarchy

GS_GameManager                   Top-level lifecycle controller
  ├── GS_SaveManager             Persistence (save/load game data)
       ├── GS_Saver             Per-entity save components
       └── RecordKeeper         Simple key-value progression records
  ├── GS_StageManager            Level loading and navigation
       ├── GS_StageData         Per-level anchor and spin-up controller
       └── StageExitPoint       Spawn/exit position markers
  ├── GS_OptionsManager          Options and input profile management
       └── GS_InputProfile      Data asset for input bindings
  └── (Your Custom Managers)     Extend GS_Manager for your own systems

All managers follow the same pattern: they are spawned by the Game Manager, go through a two-stage initialization, and are globally accessible via their EBus interfaces.

Staged Initialization

GS_GameManager (singleton, placed in every level as a prefab)
  ├── Spawns manager prefabs from its "Startup Managers" list
  ├── Stage 1: Initialize
  │     Each manager runs Activate(), then reports OnRegisterManagerInit()
  │     GM waits for ALL managers to report before proceeding
  ├── Stage 2: Startup
  │     GM broadcasts OnStartupManagers()
  │     Each manager connects to other managers, then reports OnRegisterManagerStartup()
  │     GM waits for ALL managers to report before proceeding
  └── Stage 3: Complete
        GM broadcasts OnStartupComplete()
        Game systems are fully ready — UI can spawn, gameplay can begin

This two-stage initialization guarantees that during Stage 2, every manager is already initialized and safe to reference. This eliminates race conditions between interdependent systems.


Components

ComponentPurposeDocumentation
GS_GameManagerComponentTop-level lifecycle controller. Spawns managers, handles New Game / Load / Quit / Standby.Game Manager
GS_ManagerComponentBase class for all game system managers. Handles the two-stage init pattern automatically.Manager

Quick Start

In-level Assembly

  1. Create a Game Manager prefab with the GS_GameManagerComponent.
  2. Create Manager prefabs for each system you need.
  3. Add the manager prefab spawnables to the Game Manager’s Startup Managers list.
  4. Place the Game Manager prefab at the top of every level.
  5. The system handles initialization ordering automatically.

Creating Unique Managers

  1. Use the Manager ClassWizard template, or extend the GS_Manager class manually.
  2. Override any initialization stage methods, or utility methods.
  3. Be sure to call parent methods in any override, otherwise initialization will be missed.
  4. Create a new Prefab with your new Manager, add to the GameManager Prefab Managers list.

See Also

1.2.1 - Game Manager

The top-level game lifecycle controller — startup sequencing, systemic navigation, standby mode, and debug support.

Image showing the Game Manager component, with added manager prefabs, as seen in the Entity Inspector.

Overview

The Game Manager is the root component of every GS_Play project. It controls the game lifecycle from startup through shutdown: spawning and initializing all Manager components in the correct order, providing systemic navigation (New Game, Load Game, Return to Title, Quit), and coordinating standby mode for pausing gameplay during level transitions or other blocking operations.

Every level in your project should contain the same Game Manager prefab. This allows you to start the game from any level — in debug mode it stays in the current level, in release mode it navigates to your title stage.

How It Works

Startup Sequence

When the project starts, the Game Manager executes a three-stage startup:

  1. Spawn Managers — The Game Manager instantiates every prefab in its Startup Managers list. Each manager component runs its Activate() and reports back via OnRegisterManagerInit().
  2. Startup Managers — Once all managers report initialized, the Game Manager broadcasts OnStartupManagers(). Managers now connect to each other and report back via OnRegisterManagerStartup().
  3. Startup Complete — Once all managers report started up, the Game Manager broadcasts OnStartupComplete(). The game is fully initialized and ready to run.

At each stage, the Game Manager counts reports and only proceeds when every spawned manager has reported. This guarantees safe cross-referencing between managers in Stage 2.

Systemic Navigation

The Game Manager provides the high-level game flow methods that drive your project:

  • NewGame / NewGame(saveName) — Starts a new game, optionally with a named save file.
  • ContinueGame — Loads the most recent save and continues.
  • LoadGame(saveName) — Loads a specific save file.
  • ReturnToTitle — Returns to the title stage, tearing down the current game session.
  • SaveAndExitGame / ExitGame — Saves and/or exits the application.

These methods coordinate with the Save Manager and Stage Manager automatically.

Standby Mode

Standby is a global pause mechanism. When the Game Manager enters standby, it broadcasts OnEnterStandby() to all managers, which propagate the signal to their subsystems. This halts timers, physics, gameplay ticks, and other simulation while a blocking operation (like a stage change) completes. Calling ExitStandby reverses the process.

Debug Mode

When Debug Mode is enabled in the Inspector, the Game Manager skips navigating to the title stage and remains in the current level. This allows rapid iteration — you can start the game from any level without going through the full title-to-gameplay flow. If save data and unit management are enabled, it loads default save data but overrides position data with the current level’s default spawn point.


Setup

Image showing the Game Manager wrapper entity set as Editor-Only inside Prefab Edit Mode.

  1. Create an entity and attach the GS_GameManagerComponent (or your extended version).
  2. Turn the entity into a prefab.
  3. Enter prefab edit mode. Set the wrapper entity (the parent of your Game Manager entity) to Editor Only. Save the prefab.
  4. Create Manager prefabs for the systems you need (Save, Stage, Options, or custom managers).
  5. Add each Manager .spawnable to the Game Manager’s Startup Managers list in the Entity Inspector.
  6. Push your overrides into the prefab to propagate across all levels.

Place your Game Manager prefab at the top of every level in the Entity Outliner.

The premade manager prefabs for each GS_Play gem are located in that gem’s Assets/Prefabs directory.


Inspector Properties

PropertyTypeDescription
Project PrefixAZStd::stringSets the project prefix for generated files (e.g. save files). Example: "GS" produces GS_SaveGame.json.
Startup ManagersAZStd::vector<SpawnableAssetRef>The list of Manager prefab spawnables to instantiate on game start. Order does not matter — the two-stage init handles dependencies.
Debug ModeboolWhen enabled, the Game Manager stays in the current level instead of navigating to the title stage. Useful for rapid iteration.

API Reference

Request Bus: GameManagerIncomingEventBus

Commands you send to the Game Manager. This is a singleton bus (Single address, Single handler).

MethodParametersReturnsDescription
IsInDebugboolReturns whether debug mode is active.
IsStartedboolReturns whether the startup sequence has completed.
GetProjectPrefixAZStd::stringReturns the configured project prefix string.
EnterStandbyvoidBroadcasts standby to all managers, pausing gameplay.
ExitStandbyvoidBroadcasts standby exit, resuming gameplay.
NewGamevoidStarts a new game with default save name.
NewGameconst AZStd::string& saveNamevoidStarts a new game with a specified save file name.
ContinueGamevoidLoads the most recent save file and continues.
LoadGameconst AZStd::string& saveNamevoidLoads a specific save file by name.
ReturnToTitlevoidReturns to the title stage, tearing down the current session.
SaveAndExitGamevoidSaves the current game state and exits the application.
ExitGamevoidExits the application without saving.

Notification Bus: GameManagerOutgoingEventBus

Events broadcast by the Game Manager that other systems listen to. This is a multiple handler bus — any number of components can subscribe.

EventDescription
OnSetupManagersFired when all managers have reported initialized. Managers should now connect to each other.
OnStartupCompleteFired when the full startup sequence is complete. Safe to begin gameplay.
OnShutdownManagersFired when the game is shutting down. Managers should clean up.
OnBeginGameFired when a new game or loaded game begins.
OnEnterStandbyFired when entering standby mode. Pause your systems.
OnExitStandbyFired when exiting standby mode. Resume your systems.

ScriptCanvas Reflection

These methods are available as ScriptCanvas nodes for visual scripting:

NodeDescription
TriggerNewGameStarts a new game with default save.
TriggerNewGameWithName(saveName)Starts a new game with a named save.
TriggerContinueGameContinues from the most recent save.
TriggerLoadGame(saveName)Loads a specific save file.
TriggerReturnToTitleReturns to the title stage.
TriggerSaveAndExitGameSaves and exits.
TriggerExitGameExits without saving.

Local / Virtual Methods

Methods available when extending the Game Manager. Override these to customize behavior.

MethodDescription
InitializeManagers()Spawns the Startup Managers list. Override to add custom spawn logic.
ProcessFallbackSpawn()Handles fallback when a manager fails to spawn.
StartupManagers()Broadcasts OnStartupManagers. Override to inject logic between init and startup.
CompleteStartup()Final processing after startup. Broadcasts OnStartupComplete.
BeginGame()Called when transitioning into active gameplay.

Usage Examples

Starting a New Game from C++

#include <GS_Core/GS_CoreBus.h>

// Start a new game with a named save file
GS_Core::GameManagerIncomingEventBus::Broadcast(
    &GS_Core::GameManagerIncomingEventBus::Events::NewGame,
    AZStd::string("MySaveFile")
);

Listening for Startup Complete

#include <GS_Core/GS_CoreBus.h>

class MyComponent
    : public AZ::Component
    , protected GS_Core::GameManagerOutgoingEventBus::Handler
{
protected:
    void Activate() override
    {
        GS_Core::GameManagerOutgoingEventBus::Handler::BusConnect();
    }

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

    // Called once the game is fully initialized
    void OnStartupComplete() override
    {
        // Safe to access all managers and begin gameplay logic
    }

    // Called when entering standby (e.g. during level transitions)
    void OnEnterStandby() override
    {
        // Pause your gameplay systems
    }

    void OnExitStandby() override
    {
        // Resume your gameplay systems
    }
};

Extending the Game Manager

You can extend the Game Manager to add custom startup logic, additional lifecycle events, or project-specific behavior.

Header (.h)

#pragma once
#include <GS_Core/GS_CoreBus.h>
#include <Source/Managers/GS_GameManagerComponent.h>

namespace MyProject
{
    class MyGameManager : public GS_Core::GS_GameManagerComponent
    {
    public:
        AZ_COMPONENT_DECL(MyGameManager);

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

    protected:
        void InitializeManagers() override;
        void StartupManagers() override;
        void CompleteStartup() override;
        void BeginGame() override;
    };
}

Implementation (.cpp)

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

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

    void MyGameManager::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<MyGameManager, GS_Core::GS_GameManagerComponent>()
                ->Version(0);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<MyGameManager>("My Game Manager", "Custom game manager for MyProject")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
            }
        }
    }

    void MyGameManager::InitializeManagers()
    {
        // Call base to spawn the standard manager list
        GS_GameManagerComponent::InitializeManagers();

        // Add custom initialization logic here
    }

    void MyGameManager::StartupManagers()
    {
        // Call base to handle standard startup
        GS_GameManagerComponent::StartupManagers();
    }

    void MyGameManager::CompleteStartup()
    {
        // Call base to broadcast OnStartupComplete
        GS_GameManagerComponent::CompleteStartup();

        // Your post-startup logic here (e.g. connect to analytics, initialize online services)
    }

    void MyGameManager::BeginGame()
    {
        GS_GameManagerComponent::BeginGame();

        // Custom game start logic
    }
}

Module Registration

Register your custom Game Manager in your project’s module:

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

See Also

1.2.2 - Manager

The base class for all game system managers — automatic two-stage initialization and lifecycle integration with the Game Manager.

Image showing a Manager prefab with the wrapper entity set as Editor-Only inside Prefab Edit Mode.

Overview

The GS_ManagerComponent is the base class that all game system managers inherit from. It handles the two-stage initialization pattern with the Game Manager automatically, so you can focus on your system’s logic without worrying about startup ordering.

Every built-in GS_Play manager (Save, Stage, Options, UI, Unit, Camera, Audio, Performer) extends this class. When you need a custom game system manager, you extend it too.

How It Works

When the Game Manager spawns its list of manager prefabs, each Manager component goes through this lifecycle:

  1. Activate — The component initializes itself (connects to buses, sets up internal state). When done, it broadcasts OnRegisterManagerInit() to tell the Game Manager it is ready.

  2. OnStartupManagers — Called by the Game Manager after all managers have reported initialized. This is where you connect to other managers and set up cross-references. When done, broadcasts OnRegisterManagerStartup().

  3. OnStartupComplete — The Game Manager broadcasts this after all managers report started up. The game is fully ready.

  4. OnEnterStandby / OnExitStandby — Called when the Game Manager enters or exits standby mode. Pause and resume your subsystem here.

  5. OnShutdownManagers — Called when the game is shutting down. Clean up your subsystem.

The base class handles steps 1-2 automatically — you override them to add your own logic, then call the base implementation to complete the handshake.


Setup

Image showing the Manager wrapper entity set as Editor-Only inside Prefab Edit Mode.

  1. Create an entity. Attach your Manager component (built-in or custom) to it.
  2. Configure any properties needed in the Entity Inspector.
  3. Turn the entity into a prefab.
  4. Enter prefab edit mode. Set the wrapper entity (parent of your Manager entity) to Editor Only. Save.
  5. Delete the Manager entity from the level (it will be spawned by the Game Manager at runtime).
  6. In the Game Manager prefab, add your Manager .spawnable to the Startup Managers list.

The premade manager prefabs for each GS_Play gem are located in that gem’s Assets/Prefabs directory.


Inspector Properties

The base GS_ManagerComponent exposes no inspector properties of its own. Properties are defined by each inheriting manager component (e.g., the Save Manager exposes a Save System Version Number, the Stage Manager exposes a Stages list).


API Reference

Lifecycle Bus: GameManagerOutgoingEventBus

The Manager base class automatically subscribes to these events from the Game Manager:

EventWhen It FiresWhat You Do
OnStartupManagersAll managers are initializedConnect to other managers, set up cross-references, then call base to report.
OnStartupCompleteAll managers are started upBegin runtime operation.
OnEnterStandbyGame is entering standbyPause your subsystem (stop ticks, halt processing).
OnExitStandbyGame is exiting standbyResume your subsystem.
OnShutdownManagersGame is shutting downClean up resources, disconnect buses.

Registration Methods

These are called internally by the Manager base class to report back to the Game Manager:

MethodDescription
OnRegisterManagerInit()Called at the end of Activate(). Reports to the Game Manager that this manager has initialized.
OnRegisterManagerStartup()Called at the end of OnStartupManagers(). Reports to the Game Manager that this manager has started up.

Usage Examples

Checking if the Game Manager is Ready

#include <GS_Core/GS_CoreBus.h>

bool isStarted = false;
GS_Core::GameManagerIncomingEventBus::BroadcastResult(
    isStarted,
    &GS_Core::GameManagerIncomingEventBus::Events::IsStarted
);

if (isStarted)
{
    // Safe to access all managers
}

Extending the Manager Class

Create a custom manager whenever you need a singleton game system that initializes with the rest of the framework.

Header (.h)

#pragma once
#include <Source/Managers/GS_ManagerComponent.h>

// Define your EBus interface for other systems to call your manager
class MyManagerRequests
{
public:
    AZ_RTTI(MyManagerRequests, "{YOUR-UUID-HERE}");
    virtual ~MyManagerRequests() = default;

    virtual int GetSomeValue() = 0;
    virtual void DoSomething() = 0;
};

class MyManagerBusTraits : public AZ::EBusTraits
{
public:
    static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
    static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
};

using MyManagerRequestBus = AZ::EBus<MyManagerRequests, MyManagerBusTraits>;

namespace MyProject
{
    class MyManagerComponent
        : public GS_Core::GS_ManagerComponent
        , protected MyManagerRequestBus::Handler
    {
    public:
        AZ_COMPONENT_DECL(MyManagerComponent);

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

    protected:
        // Manager lifecycle
        void Activate() override;
        void Deactivate() override;
        void OnStartupManagers() override;
        void OnEnterStandby() override;
        void OnExitStandby() override;

        // Your bus implementation
        int GetSomeValue() override;
        void DoSomething() override;

    private:
        int m_someValue = 0;
    };
}

Implementation (.cpp)

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

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

    void MyManagerComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<MyManagerComponent, GS_Core::GS_ManagerComponent>()
                ->Version(0)
                ->Field("SomeValue", &MyManagerComponent::m_someValue);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<MyManagerComponent>("My Manager", "A custom game system manager")
                    ->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,
                        &MyManagerComponent::m_someValue, "Some Value", "Description of this property");
            }
        }
    }

    void MyManagerComponent::Activate()
    {
        // Connect your bus
        MyManagerRequestBus::Handler::BusConnect();

        // IMPORTANT: Call base Activate last — it reports OnRegisterManagerInit()
        GS_ManagerComponent::Activate();
    }

    void MyManagerComponent::Deactivate()
    {
        MyManagerRequestBus::Handler::BusDisconnect();
        GS_ManagerComponent::Deactivate();
    }

    void MyManagerComponent::OnStartupManagers()
    {
        // Access other managers here — they are all initialized by now
        // Example: query the Save Manager, connect to the Stage Manager, etc.

        // IMPORTANT: Call base last — it reports OnRegisterManagerStartup()
        GS_ManagerComponent::OnStartupManagers();
    }

    void MyManagerComponent::OnEnterStandby()
    {
        // Pause your subsystem
    }

    void MyManagerComponent::OnExitStandby()
    {
        // Resume your subsystem
    }

    int MyManagerComponent::GetSomeValue()
    {
        return m_someValue;
    }

    void MyManagerComponent::DoSomething()
    {
        // Your game system logic
    }
}

Module Registration

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

Then create a prefab for your manager and add it to the Game Manager’s Startup Managers list.


See Also

1.3 - GS_Save

The persistence system — save files, load data, and track progression with managers, savers, and record keepers.

Overview

The Save system is the universal means of storing, loading, and handling game data. It provides a centralized Save Manager for file operations, entity-level Saver components for automatic data capture, and a Record Keeper for lightweight progression tracking — all backed by JSON-formatted save files that work across PC, console, and mobile platforms.

Every component that needs to persist data plugs into this system through one of two patterns: extend the Saver base class for complex per-entity data, or use the Record Keeper for simple key-value records.

Architecture

GS_SaveManagerComponent (singleton, spawned by Game Manager)
  
  ├── Manages save files via O3DE SaveData gem
       CoreSaveData file tracks all saves + metadata
       Individual save files store game state as JSON
  
  ├── SaveManagerOutgoingEventBus
       Broadcasts OnSaveAll / OnLoadAll to all Savers
  
  ├── GS_SaverComponent (base class, on any entity)
       Listens for global save/load events
       Calls BuildSaveData()  SaveData() to persist
       Calls LoadLocalData()  ProcessLoad() to restore
       
       ├── BasicEntitySaverComponent (saves Transform)
       ├── BasicPhysicsEntitySaverComponent (saves Transform + Rigidbody)
       └── Your custom savers...
  
  └── RecordKeeperComponent (extends GS_SaverComponent)
        Simple key-value records (string  int)
        Lives on the Save Manager prefab entity

Components

ComponentPurposeDocumentation
GS_SaveManagerComponentCentral save/load controller. Manages save files, triggers global save/load events, provides data access to all savers.Save Manager
GS_SaverComponentBase class for entity-level save handlers. Override BuildSaveData() and ProcessLoad() to persist any component data.Savers
RecordKeeperComponentLightweight key-value store for progression tracking (string name → integer value). Extends GS_SaverComponent.Record Keeper
BasicEntitySaverComponentPre-built saver that persists an entity’s Transform (position, rotation).List of Savers
BasicPhysicsEntitySaverComponentPre-built saver that persists an entity’s Transform and Rigidbody state (velocity).List of Savers

Quick Start

  1. Create a Save Manager prefab with the GS_SaveManagerComponent.
  2. Optionally add a Record Keeper to the same prefab for progression tracking.
  3. Add the Save Manager .spawnable to the Game Manager’s Startup Managers list.
  4. Attach Saver components to any entities that need to persist data.
  5. Call NewGame / LoadGame through the Game Manager — the Save Manager handles the rest.

See Also

1.3.1 - Save Manager

The central save/load controller — manages save files, triggers global persistence events, and provides data access for all savers.

Image showing the Save Manager component, as seen in the Entity Inspector.

Overview

The Save Manager is the central controller of the save system. It manages save file creation, triggers global save/load events that all Saver components respond to, and provides data access methods for storing and retrieving serialized game state.

Save files use JSON formatting for easy human interpretation, and the system uses the O3DE SaveData gem to write to the target platform’s default user data directory — PC, console, or mobile with no additional configuration.

How It Works

Save File Structure

The Save Manager maintains a CoreSaveData file that acts as an index of all save files for the project. This file is identified by the combination of the Game Manager’s Project Prefix and the Save Manager’s Save System Version Number. Changing either value starts a fresh save data pass.

Each individual save file stores the full game state as a JSON document, including timestamped metadata for ordering.

Initialization

On startup, the Save Manager checks for existing save data using the Project Prefix + Version combination. It sets its internal IsContinuing flag to indicate whether previous save data is available to load. Other systems can query this to determine whether to show “Continue” or “Load Game” options.

New Game Flow

When the Game Manager calls NewGame:

  1. The Save Manager creates a new save file — either defaultSaveData (no name given) or a custom name via NewGame(saveName).
  2. The CoreSaveData index is updated with the new file entry.

Save Flow

  1. Game systems call SaveData(uniqueName, data) on the SaveManagerIncomingEventBus to store their data into the live save document.
  2. When a full save is triggered (via method call, OnSaveAll broadcast, or save-on-exit logic), the Save Manager serializes the complete document to disk.

Load Flow

  1. The Save Manager reads the target save file from disk into memory.
  2. It broadcasts OnLoadAll via the SaveManagerOutgoingEventBus.
  3. Each Saver component responds by calling LoadData(uniqueName, outData) to retrieve its portion of the save data.

Setup

Image showing the Manager wrapper entity set as Editor-Only inside Prefab Edit Mode.

  1. Create an entity. Attach the GS_SaveManagerComponent to it.
  2. Set the Save System Version Number (increment this when your save format changes to avoid loading incompatible data).
  3. Optionally add a RecordKeeperComponent to the same entity for progression tracking.
  4. Turn the entity into a prefab.
  5. Enter prefab edit mode. Set the wrapper entity (parent) to Editor Only. Save.
  6. Delete the Save Manager entity from the level.
  7. In the Game Manager prefab, add the Save Manager .spawnable to the Startup Managers list.

Inspector Properties

PropertyTypeDefaultDescription
Save System Version Numberint0Version stamp for save file compatibility. Increment when your save data format changes — the system will treat saves from a different version as a fresh start.
Full Save On DestroybooltrueWhen enabled, the Save Manager performs a full save when the component is destroyed (e.g., on level exit or game shutdown).

API Reference

Request Bus: SaveManagerIncomingEventBus

The primary interface for all save/load operations. Singleton bus — call via Broadcast.

MethodParametersReturnsDescription
NewGameSaveconst AZStd::string& uniqueNamevoidCreates a new save file with the given name. Pass empty string for default name.
LoadGameconst AZStd::string& uniqueNamevoidLoads the specified save file into memory and triggers data restoration.
SaveDataconst AZStd::string& uniqueName, const rapidjson::Value& datavoidStores a named data block into the live save document. Called by Saver components during save operations.
LoadDataconst AZStd::string& uniqueName, rapidjson::Value& outDataboolRetrieves a named data block from the loaded save document. Returns true if the data was found.
GetOrderedSaveListAZStd::vector<AZStd::pair<AZStd::string, AZ::u64>>Returns all save files ordered by timestamp (newest first). Each entry is a name + epoch timestamp pair.
ConvertEpochToReadableAZ::u64 epochSecondsAZStd::stringConverts an epoch timestamp to a human-readable date string.
GetEpochTimeNowAZ::u64Returns the current time as an epoch timestamp.
GetAllocatorrapidjson::Document::AllocatorType*Returns the JSON allocator for constructing save data values.
HasDataconst AZStd::string& uniqueNameboolChecks whether the specified data block exists in the current save.
IsContinuingboolReturns true if previous save data was found on startup.
RegisterSavingvoidRegisters that a save operation is in progress (used internally by the save counting system).

Notification Bus: SaveManagerOutgoingEventBus

Broadcast to all Saver components. Connect to this bus to participate in global save/load events.

EventParametersDescription
OnSaveAllBroadcast when a full save is triggered. All savers should gather and submit their data.
OnLoadAllBroadcast when a save file has been loaded into memory. All savers should retrieve and restore their data.

Local / Virtual Methods

These methods are available when extending the Save Manager. Override them to customize save file handling.

MethodDescription
SaveToFile(fileName, docFile)Serializes a JSON document to disk using the O3DE SaveData gem.
LoadFromFile(fileName, docFile)Deserializes a save file from disk into a JSON document.
FullSave()Triggers a complete save of all game data to disk.
UpdateCoreData(saveName)Updates the CoreSaveData index with the current save file entry.
FileExists(dataBufferName, localUserId)Static utility — checks if a save file exists on disk.
GetSaveFilePath(dataBufferName, localUserId)Static utility — returns the platform-appropriate file path for a save file.

Usage Examples

Saving Data from a Component

#include <GS_Core/GS_CoreBus.h>

// Store your component's data into the live save document
rapidjson::Document::AllocatorType* allocator = nullptr;
GS_Core::SaveManagerIncomingEventBus::BroadcastResult(
    allocator,
    &GS_Core::SaveManagerIncomingEventBus::Events::GetAllocator
);

if (allocator)
{
    rapidjson::Value myData(rapidjson::kObjectType);
    myData.AddMember("health", m_health, *allocator);
    myData.AddMember("level", m_level, *allocator);

    GS_Core::SaveManagerIncomingEventBus::Broadcast(
        &GS_Core::SaveManagerIncomingEventBus::Events::SaveData,
        "MyComponent_PlayerStats",
        myData
    );
}

Loading Data into a Component

#include <GS_Core/GS_CoreBus.h>

rapidjson::Value outData;
bool found = false;
GS_Core::SaveManagerIncomingEventBus::BroadcastResult(
    found,
    &GS_Core::SaveManagerIncomingEventBus::Events::LoadData,
    "MyComponent_PlayerStats",
    outData
);

if (found)
{
    if (outData.HasMember("health")) m_health = outData["health"].GetInt();
    if (outData.HasMember("level"))  m_level = outData["level"].GetInt();
}

Checking if a Save Exists

#include <GS_Core/GS_CoreBus.h>

bool hasSave = false;
GS_Core::SaveManagerIncomingEventBus::BroadcastResult(
    hasSave,
    &GS_Core::SaveManagerIncomingEventBus::Events::IsContinuing
);

if (hasSave)
{
    // Show "Continue" / "Load Game" in the main menu
}

Getting the Save File List

#include <GS_Core/GS_CoreBus.h>

AZStd::vector<AZStd::pair<AZStd::string, AZ::u64>> saves;
GS_Core::SaveManagerIncomingEventBus::BroadcastResult(
    saves,
    &GS_Core::SaveManagerIncomingEventBus::Events::GetOrderedSaveList
);

for (const auto& [name, epoch] : saves)
{
    AZStd::string readable;
    GS_Core::SaveManagerIncomingEventBus::BroadcastResult(
        readable,
        &GS_Core::SaveManagerIncomingEventBus::Events::ConvertEpochToReadable,
        epoch
    );
    AZ_TracePrintf("Save", "Save: %s — %s", name.c_str(), readable.c_str());
}

Extending the Save Manager

Extend the Save Manager when you need custom save file formats, encryption, cloud save integration, or platform-specific serialization.

Header (.h)

#pragma once
#include <Source/SaveSystem/GS_SaveManagerComponent.h>

namespace MyProject
{
    class MySaveManagerComponent
        : public GS_Core::GS_SaveManagerComponent
    {
    public:
        AZ_COMPONENT_DECL(MySaveManagerComponent);

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

    protected:
        // Override save/load to add custom behavior (e.g., encryption, compression)
        void SaveToFile(AZStd::string fileName, rapidjson::Document& docFile) override;
        void LoadFromFile(AZStd::string fileName, rapidjson::Document& docFile) override;

        // Override to customize the full save sequence
        void FullSave() override;
    };
}

Implementation (.cpp)

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

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

    void MySaveManagerComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<MySaveManagerComponent, GS_Core::GS_SaveManagerComponent>()
                ->Version(0);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<MySaveManagerComponent>(
                    "My Save Manager", "Custom save manager with encryption support")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
            }
        }
    }

    void MySaveManagerComponent::SaveToFile(AZStd::string fileName, rapidjson::Document& docFile)
    {
        // Example: encrypt the JSON before writing
        // ... your encryption logic ...

        // Call base to perform the actual file write
        GS_SaveManagerComponent::SaveToFile(fileName, docFile);
    }

    void MySaveManagerComponent::LoadFromFile(AZStd::string fileName, rapidjson::Document& docFile)
    {
        // Call base to perform the actual file read
        GS_SaveManagerComponent::LoadFromFile(fileName, docFile);

        // Example: decrypt the JSON after reading
        // ... your decryption logic ...
    }

    void MySaveManagerComponent::FullSave()
    {
        // Example: add a timestamp or checksum before saving
        // ... your custom logic ...

        GS_SaveManagerComponent::FullSave();
    }
}

Module Registration

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

Then create a prefab for your custom Save Manager and add it to the Game Manager’s Startup Managers list (replacing the default Save Manager).


See Also

  • Game Manager — Drives New Game / Load Game / Continue flows
  • Savers — Entity-level save handlers that plug into global save/load events
  • Record Keeper — Lightweight key-value records for progression
  • Manager — Base class lifecycle pattern
  • Templates — Starter files for custom save components

1.3.2 - Savers

The base class for entity-level save handlers — override BuildSaveData() and ProcessLoad() to persist any component data automatically.

Image showing the Basic Physics Saver component, an example of a Saver-inherited class, as seen in the Entity Inspector.

Overview

The GS_SaverComponent is the base class for all entity-level save handlers. By extending it, you can create companion components that save and load data alongside your entities, or inherit directly into gameplay components for built-in persistence (as done by GS_Inventory).

Savers automatically participate in the global save/load cycle — when the Save Manager broadcasts OnSaveAll or OnLoadAll, every active Saver component responds by gathering or restoring its data.

How It Works

Save Cycle

When a save event fires (global OnSaveAll, local trigger, or saveOnDestroy):

  1. BeginSave() — Primes the save data container.
  2. BuildSaveData() — Your override. Gather your component’s data and write it into localData using the Save Manager’s JSON allocator.
  3. CompleteSave() — Submits the data to the Save Manager via SaveData(uniqueName, localData).

Load Cycle

When a load event fires (global OnLoadAll or loadOnActivate):

  1. LoadLocalData() — Retrieves this Saver’s data block from the Save Manager via LoadData(uniqueName, outData).
  2. ProcessLoad() — Your override. Read the retrieved data and restore your component’s state.

Automatic Identity

Each Saver generates a unique save key from the entity name and GetSubComponentName(). This ensures multiple Savers on different entities (or multiple Saver types on the same entity) don’t collide.


Setup

  1. Attach a Saver component (built-in or custom) to any entity that needs to persist data.
  2. Configure the Saver properties:
    • Load On Activate — restore data automatically when the entity spawns.
    • Save On Destroy — save data automatically when the entity is destroyed.
  3. Ensure the Save Manager is set up and running (it handles the file I/O).

Inspector Properties

PropertyTypeDefaultDescription
Load On ActivatebooltrueAutomatically loads and restores this Saver’s data when the component activates. Useful for entities that spawn mid-game and need to resume their saved state.
Save On DestroybooltrueAutomatically saves this Saver’s data when the component is destroyed. Ensures data is captured even if a global save hasn’t been triggered.

API Reference

Global Event Handlers

These are called automatically when the Save Manager broadcasts global save/load events. Override to customize behavior.

MethodDescription
OnSaveAll()Called when the Save Manager broadcasts a global save. Default implementation calls the full save cycle (BeginSave → BuildSaveData → CompleteSave).
OnLoadAll()Called when the Save Manager broadcasts a global load. Default implementation calls the full load cycle (LoadLocalData → ProcessLoad).

Save Methods

Override these to control how your component’s data is saved.

MethodDescription
BeginSave()Prepares the save data container. Called before BuildSaveData().
BuildSaveData()Your primary override. Write your component’s data into localData using the JSON allocator.
CompleteSave()Submits localData to the Save Manager. Called after BuildSaveData().

Load Methods

Override these to control how your component’s data is restored.

MethodDescription
LoadLocalData()Retrieves this Saver’s data block from the Save Manager into localData.
ProcessLoad()Your primary override. Read localData and restore your component’s state.

Utility Methods

MethodReturnsDescription
SetUniqueName()voidGenerates the unique save key from the entity name and sub-component name. Override for custom key generation.
GetSubComponentName()AZStd::stringReturns "Saver" by default. Override to distinguish multiple Saver types on the same entity.

Components

Pre-built Saver components included in GS_Core:

ComponentSavesDocumentation
BasicEntitySaverComponentEntity Transform (position, rotation)List of Savers
BasicPhysicsEntitySaverComponentEntity Transform + Rigidbody (position, rotation, linear velocity, angular velocity)List of Savers
RecordKeeperComponentKey-value records (string → integer)Record Keeper

Usage Examples

Responding to Global Save/Load Events

If your component doesn’t extend GS_SaverComponent but still needs to react to save/load events, connect to the SaveManagerOutgoingEventBus:

#include <GS_Core/GS_CoreBus.h>

class MyComponent
    : public AZ::Component
    , protected GS_Core::SaveManagerOutgoingEventBus::Handler
{
protected:
    void Activate() override
    {
        GS_Core::SaveManagerOutgoingEventBus::Handler::BusConnect();
    }

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

    void OnSaveAll() override
    {
        // Gather and submit data to the Save Manager
    }

    void OnLoadAll() override
    {
        // Retrieve and restore data from the Save Manager
    }
};

Extending the Saver Class

Create a custom Saver whenever you need to persist component-specific data that the built-in savers don’t cover.

Header (.h)

#pragma once
#include <Source/SaveSystem/GS_SaverComponent.h>

namespace MyProject
{
    class MyEntitySaverComponent
        : public GS_Core::GS_SaverComponent
    {
    public:
        AZ_COMPONENT_DECL(MyEntitySaverComponent);

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

    protected:
        // Saver overrides
        void BuildSaveData() override;
        void ProcessLoad() override;
        AZStd::string GetSubComponentName() const override { return "MyEntitySaver"; }
    };
}

Implementation (.cpp)

#include "MyEntitySaverComponent.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <GS_Core/GS_CoreBus.h>

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

    void MyEntitySaverComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<MyEntitySaverComponent, GS_Core::GS_SaverComponent>()
                ->Version(0);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<MyEntitySaverComponent>(
                    "My Entity Saver", "Saves custom entity data")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
            }
        }
    }

    void MyEntitySaverComponent::BuildSaveData()
    {
        // Get the JSON allocator from the Save Manager
        rapidjson::Document::AllocatorType* allocator = nullptr;
        GS_Core::SaveManagerIncomingEventBus::BroadcastResult(
            allocator,
            &GS_Core::SaveManagerIncomingEventBus::Events::GetAllocator
        );

        if (!allocator) return;

        // Write your data into localData (inherited member)
        localData.SetObject();
        localData.AddMember("myValue", 42, *allocator);
        localData.AddMember("myFlag", true, *allocator);

        // Example: save a string
        rapidjson::Value nameVal;
        nameVal.SetString("hello", *allocator);
        localData.AddMember("myString", nameVal, *allocator);
    }

    void MyEntitySaverComponent::ProcessLoad()
    {
        // Read your data from localData (populated by LoadLocalData)
        if (localData.HasMember("myValue"))
        {
            int myValue = localData["myValue"].GetInt();
            // ... restore state ...
        }

        if (localData.HasMember("myFlag"))
        {
            bool myFlag = localData["myFlag"].GetBool();
            // ... restore state ...
        }
    }
}

Module Registration

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

Attach your custom Saver to any entity that needs persistence. The save/load cycle handles the rest automatically.


See Also

1.3.2.1 - List of Savers

Pre-built saver components included in GS_Core — ready-to-use entity persistence without writing code.

Overview

GS_Core includes pre-built Saver components that handle common persistence scenarios out of the box. Attach one to any entity that needs to remember its state between save/load cycles — no custom code required.

All built-in Savers inherit from GS_SaverComponent and participate in the global save/load cycle automatically.


Basic Entity Saver

A companion component that saves and loads an entity’s Transform data: position and rotation.

When to Use

Use the Basic Entity Saver for any entity that can be moved or rotated during gameplay and needs to retain its position across saves — collectibles, furniture, doors, NPCs with fixed patrol points, etc.

Inspector Properties

PropertyTypeDefaultDescription
Load On ActivatebooltrueInherited from GS_SaverComponent. Automatically restores the saved Transform on activation.
Save On DestroybooltrueInherited from GS_SaverComponent. Automatically saves the current Transform on destruction.

What It Saves

DataTypeDescription
PositionAZ::Vector3World-space position of the entity.
RotationAZ::QuaternionWorld-space rotation of the entity.

Setup

  1. Attach the BasicEntitySaverComponent to any entity that needs Transform persistence.
  2. Ensure the Save Manager is running.
  3. That’s it — the component saves and loads automatically.

Basic Physics Entity Saver

Image showing the Basic Physics Saver component, as seen in the Entity Inspector.

A companion component that saves and loads an entity’s Transform and Rigidbody physics state.

When to Use

Use the Basic Physics Entity Saver for physics-driven entities that need to retain both their position and their motion state across saves — throwable objects, rolling boulders, physics puzzles, ragdolls, etc.

Inspector Properties

PropertyTypeDefaultDescription
Load On ActivatebooltrueInherited from GS_SaverComponent. Automatically restores saved state on activation.
Save On DestroybooltrueInherited from GS_SaverComponent. Automatically saves current state on destruction.

What It Saves

DataTypeDescription
PositionAZ::Vector3World-space position of the entity.
RotationAZ::QuaternionWorld-space rotation of the entity.
Linear VelocityAZ::Vector3Current linear velocity of the rigidbody.
Angular VelocityAZ::Vector3Current angular velocity of the rigidbody.

Setup

  1. Attach the BasicPhysicsEntitySaverComponent to any entity with a Rigidbody component.
  2. Ensure the Save Manager is running.
  3. The component saves and loads automatically.

Creating Your Own Saver

Need to persist data that the built-in Savers don’t cover? See the Extending the Saver Class guide for a complete walkthrough with header, implementation, and module registration.

See Also

  • Savers — Base class documentation and extension guide
  • Save Manager — Central save/load controller
  • Record Keeper — Lightweight key-value records for simple progression data

1.3.3 - Record Keeper

Lightweight key-value progression tracking — store and retrieve named integer records without writing a custom saver.

Image showing the Record Keeper component with its unique variables and inherited Saver properties, as seen in the Entity Inspector.

Overview

The Record Keeper is a companion component that provides a simple key-value store for tracking progression, switch states, quest stages, or any other data that doesn’t require a complex Saver implementation. Each record is a SaveRecord — a name/value pair of recordName (string) and recordProgress (integer).

Because it extends GS_SaverComponent, the Record Keeper saves and loads automatically with the rest of the save system. No custom serialization code needed.

How It Works

  1. The Record Keeper lives on your Save Manager prefab entity (recommended) or any entity with save system access.
  2. Game systems call SetRecord, GetRecord, HasRecord, and DeleteRecord via the RecordKeeperIncomingEventBus.
  3. On each call to SetRecord, the Record Keeper broadcasts RecordChanged on the RecordKeeperOutgoingEventBus so listeners can react.
  4. When a global save event fires (OnSaveAll), the Record Keeper serializes all its records into the save file automatically.
  5. On load, it deserializes its records and makes them available immediately.

SaveRecord Data Structure

struct SaveRecord
{
    AZStd::string recordName;    // Unique key (e.g., "quest_village_rescue", "switch_bridge_01")
    AZ::s32       recordProgress; // Integer value (progression stage, state, count, etc.)
};

TypeId: {F6F4F258-819A-468A-B015-CAF51D8289BF}


Setup

  1. Open your Save Manager prefab in prefab edit mode.
  2. Add the RecordKeeperComponent to the Save Manager entity.
  3. Set the Record Keeper Name — this drives unique save/load identification and allows multiple Record Keepers if needed.
  4. Enable the Saver booleans as needed:
    • Load On Activate — automatically loads records when the component activates (recommended: on).
    • Save On Destroy — automatically saves records when the component is destroyed (recommended: on).

Inspector Properties

PropertyTypeDefaultDescription
Record Keeper NameAZStd::string""Unique identifier for this Record Keeper. Drives the save/load key. Required if using multiple Record Keepers.
Load On ActivatebooltrueInherited from GS_SaverComponent. Automatically loads records from save data on activation.
Save On DestroybooltrueInherited from GS_SaverComponent. Automatically saves records to the save system on destruction.

API Reference

Request Bus: RecordKeeperIncomingEventBus

The primary interface for reading and writing records.

MethodParametersReturnsDescription
HasRecordconst AZStd::string& recordNameboolReturns true if a record with the given name exists.
SetRecordconst AZStd::string& recordName, AZ::s32 recordProgressvoidCreates or updates a record. Broadcasts RecordChanged on success.
GetRecordconst AZStd::string& recordNameAZ::s32Returns the value of the named record. Returns 0 if the record does not exist.
DeleteRecordconst AZStd::string& recordNamevoidRemoves the named record from the store.

Notification Bus: RecordKeeperOutgoingEventBus

Connect to this bus to react when records change.

EventParametersDescription
RecordChangedconst AZStd::string& recordName, AZ::s32 recordValueBroadcast whenever SetRecord is called. Use this to update UI, trigger gameplay events, or log progression.

Usage Examples

Setting a Progression Record

#include <GS_Core/GS_CoreBus.h>

// Mark quest stage 2 as complete
GS_Core::RecordKeeperIncomingEventBus::Broadcast(
    &GS_Core::RecordKeeperIncomingEventBus::Events::SetRecord,
    "quest_village_rescue",
    2
);

Reading a Record

#include <GS_Core/GS_CoreBus.h>

AZ::s32 questStage = 0;
GS_Core::RecordKeeperIncomingEventBus::BroadcastResult(
    questStage,
    &GS_Core::RecordKeeperIncomingEventBus::Events::GetRecord,
    "quest_village_rescue"
);

if (questStage >= 2)
{
    // The player has completed stage 2 — unlock the bridge
}

Checking if a Record Exists

#include <GS_Core/GS_CoreBus.h>

bool exists = false;
GS_Core::RecordKeeperIncomingEventBus::BroadcastResult(
    exists,
    &GS_Core::RecordKeeperIncomingEventBus::Events::HasRecord,
    "switch_bridge_01"
);

if (!exists)
{
    // First time encountering this switch — initialize it
    GS_Core::RecordKeeperIncomingEventBus::Broadcast(
        &GS_Core::RecordKeeperIncomingEventBus::Events::SetRecord,
        "switch_bridge_01",
        0
    );
}

Listening for Record Changes

#include <GS_Core/GS_CoreBus.h>

// In your component header:
class MyQuestTrackerComponent
    : public AZ::Component
    , protected GS_Core::RecordKeeperOutgoingEventBus::Handler
{
protected:
    void Activate() override
    {
        GS_Core::RecordKeeperOutgoingEventBus::Handler::BusConnect();
    }

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

    // RecordKeeperOutgoingEventBus
    void RecordChanged(const AZStd::string& recordName, AZ::s32 recordValue) override
    {
        if (recordName == "quest_village_rescue" && recordValue >= 3)
        {
            // Quest complete — trigger reward
        }
    }
};

Extending the Record Keeper


See Also

1.4 - GS_StageManager

The level navigation system — handles loading, unloading, and staged startup of game levels with exit point traversal.

Overview

The Stage Manager system handles the process of changing levels, loading and unloading their assets, and starting up levels in the correct sequence. It supports incremental loading so that complex levels with generative components can spin up reliably without massive frame-time spikes.

Each level contains a Stage Data component that acts as the level’s anchor — connecting the Stage Manager to level-specific references, startup sequences, and navigation data. Exit Points mark spawn positions for player placement when transitioning between stages.

Architecture

GS_StageManagerComponent (singleton, spawned by Game Manager)
  
  ├── Stages list: maps stage names  spawnable prefabs
  
  ├── ChangeStageRequest(stageName, exitName)
       1. Unloads current stage (if any)
       2. Spawns target stage prefab
       3. Queries for GS_StageDataComponent in the new level
       4. Stage Data runs layered startup sequence
  
  ├── GS_StageDataComponent (one per level, in the level prefab)
       Holds level-specific settings, NavMesh ref, priority layers
       Runs staged activation: SetUpStage  ActivateByPriority  Complete
       
       └── StageLazyLoaderComponent (optional, priority-based activation)
  
  └── StageExitPointComponent (multiple per level)
        Marks spawn/exit positions for entity placement
        Can be flagged as Default for fallback positioning

Components

ComponentPurposeDocumentation
GS_StageManagerComponentSingleton level controller. Manages the Stages list, handles change requests, coordinates loading/unloading.Stage Manager
GS_StageDataComponentPer-level anchor. Holds stage name, NavMesh reference, and runs the level’s startup sequence.Stage Data
StageExitPointComponentMarks spawn/exit positions within a level. Registered by name for cross-stage entity placement.Stage Manager: Exit Points
StageLazyLoaderComponentPriority-based entity activation during level load. Spreads heavy initialization across frames.Stage Data: Priority Loading

Quick Start

  1. Create a Stage Manager prefab with the GS_StageManagerComponent.
  2. Add stage entries to the Stages list (name + spawnable prefab for each level).
  3. Set the Default Stage to your starting level.
  4. Add the Stage Manager .spawnable to the Game Manager’s Startup Managers list.
  5. In each level prefab, add a Stage Data component as the level’s anchor.
  6. Optionally place Exit Points in each level for spawn positioning.

See Also

1.4.1 - Stage Manager

The singleton level controller — manages a list of stages, handles level loading/unloading, and coordinates staged startup with the Stage Data component.

Image showing the Stage Manager component, as seen in the Entity Inspector.

Overview

The Stage Manager is the singleton that handles all the logistics of loading and unloading game levels. It maintains a list of stages (name-to-spawnable mappings) and processes change requests by unloading the current stage and spawning the target. Once loaded, it connects to the level’s Stage Data component to run the staged startup sequence.

The system operates as a “one in, one out” format — loading one level automatically unloads the previous one.

How It Works

Startup

At game startup, the Stage Manager checks the Game Manager’s Debug Mode flag:

  • Debug Mode OFF — The Stage Manager loads the Default Stage automatically. This is the normal game flow.
  • Debug Mode ON — The Stage Manager stays connected to whatever level is already loaded in the editor. This lets developers iterate within a stage without waiting for a full level load cycle.

Change Stage Flow

When ChangeStageRequest(stageName, exitName) is called:

  1. Unload — The current stage is destroyed (if one is loaded).
  2. Spawn — The Stage Manager looks up stageName in the Stages list and spawns the matching prefab.
  3. Connect — The Stage Manager queries the newly spawned level for its Stage Data component.
  4. Startup — The Stage Data component runs the level’s staged activation sequence (priority layers, NavMesh generation, etc.).
  5. Notify — The Stage Manager broadcasts LoadStageComplete when the level is fully ready.

Exit Points

Exit Points are simple components placed within a level that mark positions for entity placement after a stage loads. They support cross-stage traversal — when changing stages, you specify an exit point name, and entities can be repositioned to that point.

  • Exit Points register themselves with the Stage Manager by name via RegisterExitPoint.
  • One Exit Point can be flagged as Default — it serves as the fallback if no specific exit point is requested.
  • Call GetExitPoint(exitName) to retrieve the entity at a named position.

Setup

Image showing the Manager wrapper entity set as Editor-Only inside Prefab Edit Mode.

  1. Create an entity. Attach the GS_StageManagerComponent to it.
  2. Configure the Stages list — add an entry for each level (stage name + spawnable prefab reference).
  3. Set the Default Stage to your starting level. This name must also exist in the Stages list.
  4. Turn the entity into a prefab.
  5. Enter prefab edit mode. Set the wrapper entity (parent) to Editor Only. Save.
  6. Delete the Stage Manager entity from the level.
  7. In the Game Manager prefab, add the Stage Manager .spawnable to the Startup Managers list.

Inspector Properties

PropertyTypeDefaultDescription
StagesAZStd::vector<StageEntry>[]List of stage entries. Each entry maps a stage name to a spawnable prefab asset.
Default StageAZStd::string""The stage to load automatically on game startup (when not in Debug Mode). Must match a name in the Stages list.

API Reference

Request Bus: StageManagerIncomingEventBus

The primary interface for level navigation. Singleton bus — call via Broadcast.

MethodParametersReturnsDescription
ChangeStageRequestAZStd::string stageName, AZStd::string exitNamevoidUnloads the current stage and loads the named stage. The exitName specifies which Exit Point to use for entity placement. Pass empty string for default.
LoadDefaultStagevoidLoads the Default Stage. Typically called internally during game startup.
RegisterExitPointAZStd::string exitName, AZ::EntityId exitEntityvoidRegisters a named exit point entity. Called by StageExitPointComponents on activation.
UnregisterExitPointAZStd::string exitNamevoidRemoves a named exit point. Called by StageExitPointComponents on deactivation.
GetExitPointAZStd::string exitName = ""AZ::EntityIdReturns the entity ID of the named exit point. Pass empty string to get the default exit point.

Notification Bus: StageManagerOutgoingEventBus

Connect to this bus to react to level loading events.

EventParametersDescription
BeginLoadStageBroadcast when a stage change has started. Use this to show loading screens, disable input, etc.
LoadStageCompleteBroadcast when the new stage is fully loaded and its startup sequence is complete. Safe to begin gameplay.
StageLoadProgressfloat progressBroadcast during the loading process with a 0.0–1.0 progress value. Use for loading bar UI.

Local / Virtual Methods

These methods are available when extending the Stage Manager.

MethodDescription
ChangeStage(stageName, exitName)Internal stage change logic. Override to add custom behavior around stage transitions.
LoadStage()Spawns the target stage prefab. Override for custom spawning logic.
ContinueLoadStage()Called after the stage prefab has spawned. Queries for Stage Data and starts the level’s activation sequence.
UpdateStageData()Connects to the Stage Data component in the loaded level.
GetStageByName(stageName)Returns the spawnable asset reference for a named stage from the Stages list.

Usage Examples

Changing Stages

#include <GS_Core/GS_CoreBus.h>

// Load "ForestVillage" and place the player at the "main_entrance" exit point
GS_Core::StageManagerIncomingEventBus::Broadcast(
    &GS_Core::StageManagerIncomingEventBus::Events::ChangeStageRequest,
    AZStd::string("ForestVillage"),
    AZStd::string("main_entrance")
);

Getting an Exit Point Entity

#include <GS_Core/GS_CoreBus.h>

AZ::EntityId exitEntity;
GS_Core::StageManagerIncomingEventBus::BroadcastResult(
    exitEntity,
    &GS_Core::StageManagerIncomingEventBus::Events::GetExitPoint,
    AZStd::string("cave_exit")
);

if (exitEntity.IsValid())
{
    // Reposition the player entity to the exit point's transform
}

Listening for Stage Load Events

#include <GS_Core/GS_CoreBus.h>

class MyLoadScreenComponent
    : public AZ::Component
    , protected GS_Core::StageManagerOutgoingEventBus::Handler
{
protected:
    void Activate() override
    {
        GS_Core::StageManagerOutgoingEventBus::Handler::BusConnect();
    }

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

    void BeginLoadStage() override
    {
        // Show loading screen, disable player input
    }

    void LoadStageComplete() override
    {
        // Hide loading screen, enable player input
    }

    void StageLoadProgress(float progress) override
    {
        // Update loading bar: progress is 0.0 to 1.0
    }
};

Extending the Stage Manager

Extend the Stage Manager when you need custom stage transition logic, procedural level generation, or multi-stage loading.

Header (.h)

#pragma once
#include <Source/StageManager/GS_StageManagerComponent.h>

namespace MyProject
{
    class MyStageManagerComponent
        : public GS_Core::GS_StageManagerComponent
    {
    public:
        AZ_COMPONENT_DECL(MyStageManagerComponent);

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

    protected:
        // Override stage change behavior
        void ChangeStage(AZStd::string stageName, AZStd::string exitName) override;
        void ContinueLoadStage() override;
    };
}

Implementation (.cpp)

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

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

    void MyStageManagerComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<MyStageManagerComponent, GS_Core::GS_StageManagerComponent>()
                ->Version(0);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<MyStageManagerComponent>(
                    "My Stage Manager", "Custom stage manager with transition effects")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
            }
        }
    }

    void MyStageManagerComponent::ChangeStage(AZStd::string stageName, AZStd::string exitName)
    {
        // Example: trigger a fade-out transition before changing stages
        // ... your transition logic ...

        // Call base to perform the actual stage change
        GS_StageManagerComponent::ChangeStage(stageName, exitName);
    }

    void MyStageManagerComponent::ContinueLoadStage()
    {
        // Call base to connect to Stage Data and start level activation
        GS_StageManagerComponent::ContinueLoadStage();

        // Example: trigger a fade-in transition after loading
        // ... your transition logic ...
    }
}

Module Registration

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

Then create a prefab for your custom Stage Manager and add it to the Game Manager’s Startup Managers list (replacing the default).


See Also

1.4.2 - Stage Data

The per-level anchor component — holds stage configuration, NavMesh references, and runs the level’s staged activation sequence.

Image showing the Stage Data component, as seen in the Entity Inspector.

Overview

The Stage Data component is the anchor that connects the Stage Manager to a loaded level. It holds level-specific configuration — stage name, NavMesh reference, priority layers — and runs the staged activation sequence that brings the level online incrementally rather than all at once.

Every level that loads through the Stage Manager should have exactly one Stage Data component at the root of its prefab hierarchy.

How It Works

Connection

When the Stage Manager spawns a stage prefab, it queries the level for the Stage Data component via StageDataIncomingEventBus. The Stage Data returns its reference, and the Stage Manager hands off the startup process.

Staged Activation

Rather than activating every entity in the level at once (which would cause a massive frame spike), the Stage Data component supports priority-based activation:

  1. SetUpStage() — Initial stage configuration. Sets up references, connects internal systems.
  2. ActivateByPriority — Entities tagged with priority layers activate in sequence, spreading heavy initialization across multiple frames. This allows NavMesh generation, procedural content, and complex entity hierarchies to spin up without blocking the main thread.
  3. LoadStageComplete — The Stage Manager broadcasts this once all priority layers have finished and the level is fully ready.

The Stage Data component holds a reference to the level’s Recast NavMesh entity. This allows the Stage Manager to trigger NavMesh generation at the appropriate point during the startup sequence — after the level geometry is loaded but before AI systems begin pathfinding.


Setup

  1. In your level prefab, create an entity at the root level.
  2. Attach the GS_StageDataComponent to it.
  3. Set the Stage Name to match the name used in the Stage Manager’s Stages list.
  4. Assign the NavMesh entity reference if your level uses Recast Navigation.
  5. Configure priority layers for incremental entity activation if needed.

Inspector Properties

PropertyTypeDefaultDescription
Stage NameAZStd::string""The name of this stage. Must match the corresponding entry in the Stage Manager’s Stages list.
NavMeshAZ::EntityIdInvalidReference to the entity with the Recast Navigation component. Used to trigger NavMesh generation during staged startup.

API Reference

Request Bus: StageDataIncomingEventBus

Used by the Stage Manager to connect to and control the level’s startup.

MethodParametersReturnsDescription
GetStageDataStageData*Returns a reference to this Stage Data component. Used by the Stage Manager during initial connection.
BeginSetUpStagevoidStarts the stage’s setup sequence. Called by the Stage Manager after connecting.
GetStageNameAZStd::stringReturns the configured stage name.
GetStageNavMeshAZ::EntityIdReturns the NavMesh entity reference for this stage.

Notification Bus: StageDataOutgoingEventBus

Connect to this bus for level-specific lifecycle events.

EventParametersDescription
OnLoadStageCompleteBroadcast when this stage’s activation sequence has finished.
OnBeginSetUpStageBroadcast when the stage setup begins.
OnTearDownStageBroadcast when the stage is being unloaded. Use for level-specific cleanup.
ActivateByPriorityint priorityBroadcast for each priority layer during staged activation. Entities at this priority level should activate.

Local / Virtual Methods

These methods are available when extending the Stage Data component.

MethodReturnsDescription
SetUpStage()boolRuns the stage’s initial setup. Override to add custom startup logic. Returns true when setup is complete.
BeginLoadStage()voidCalled when the stage begins loading. Override for custom load-start behavior.
LoadStageComplete()voidCalled when loading is complete. Override for custom post-load behavior.

Usage Examples

Listening for Stage Events in a Level Component

#include <GS_Core/GS_CoreBus.h>

class MyLevelController
    : public AZ::Component
    , protected GS_Core::StageDataOutgoingEventBus::Handler
{
protected:
    void Activate() override
    {
        GS_Core::StageDataOutgoingEventBus::Handler::BusConnect();
    }

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

    void OnBeginSetUpStage() override
    {
        // Level setup is starting — initialize level-specific systems
    }

    void OnLoadStageComplete() override
    {
        // Level is fully ready — spawn enemies, start ambient audio, etc.
    }

    void OnTearDownStage() override
    {
        // Level is being unloaded — clean up level-specific resources
    }
};

Priority-Based Activation

#include <GS_Core/GS_CoreBus.h>

class MyPriorityActivator
    : public AZ::Component
    , protected GS_Core::StageDataOutgoingEventBus::Handler
{
    int m_myPriority = 2; // Activate during priority layer 2

protected:
    void Activate() override
    {
        GS_Core::StageDataOutgoingEventBus::Handler::BusConnect();
    }

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

    void ActivateByPriority(int priority) override
    {
        if (priority == m_myPriority)
        {
            // This is our turn — initialize heavy resources
            // (procedural generation, AI setup, physics volumes, etc.)
        }
    }
};

Extending the Stage Data Component

Extend Stage Data when you need custom level startup sequences, procedural generation hooks, or level-specific configuration.

Header (.h)

#pragma once
#include <Source/StageManager/GS_StageDataComponent.h>

namespace MyProject
{
    class MyStageDataComponent
        : public GS_Core::GS_StageDataComponent
    {
    public:
        AZ_COMPONENT_DECL(MyStageDataComponent);

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

    protected:
        // Override to add custom stage setup
        bool SetUpStage() override;
        void LoadStageComplete() override;

    private:
        bool m_enableProceduralContent = false;
    };
}

Implementation (.cpp)

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

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

    void MyStageDataComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<MyStageDataComponent, GS_Core::GS_StageDataComponent>()
                ->Version(0)
                ->Field("EnableProceduralContent", &MyStageDataComponent::m_enableProceduralContent);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<MyStageDataComponent>(
                    "My Stage Data", "Custom stage data with procedural generation support")
                    ->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,
                        &MyStageDataComponent::m_enableProceduralContent,
                        "Enable Procedural Content",
                        "Generate procedural content during stage setup");
            }
        }
    }

    bool MyStageDataComponent::SetUpStage()
    {
        if (m_enableProceduralContent)
        {
            // Run procedural generation for this level
            // ... your generation logic ...
        }

        // Call base to continue the standard setup sequence
        return GS_StageDataComponent::SetUpStage();
    }

    void MyStageDataComponent::LoadStageComplete()
    {
        // Custom post-load behavior
        // ... your logic ...

        GS_StageDataComponent::LoadStageComplete();
    }
}

Module Registration

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

Use your custom Stage Data component in level prefabs instead of the default GS_StageDataComponent.


See Also

1.5 - GS_Options

The configuration system — input profiles, input readers, and runtime settings management for gameplay and accessibility.

Overview

The Options system is the central pillar for handling background systemic configuration. At its simplest, it lets you set up user-facing options like volume, graphics settings, subtitles, and other standard configurable settings. Beyond that, it manages development-side functionality — language settings, current input device type, and other runtime-aware systems — providing that data to hook components throughout the game.

Currently, the Options system’s primary feature is the Input Profile system, which replaces O3DE’s raw input binding files with a more flexible, group-based approach that supports runtime rebinding and per-group enable/disable toggling.

Architecture

GS_OptionsManagerComponent (singleton, spawned by Game Manager)
  ├── Holds the active GS_InputProfile data asset
  ├── GS_InputProfile (data asset, created in Asset Editor)
  │     Contains InputBindingGroups
  │       Each group contains EventInputMappings
  │         Each mapping: eventName + bindings + deadzone + processUpdate
  └── GS_InputReaderComponent (on any entity)
        Reads input from the active profile
        Fires events based on matched bindings
        Can enable/disable input groups at runtime

Components

ComponentPurposeDocumentation
GS_OptionsManagerComponentSingleton manager. Holds the active Input Profile and provides it to Input Readers.Options Manager
GS_InputProfileData asset defining input groups, event mappings, and key bindings. Created in the Asset Editor.Input Profiles
GS_InputReaderComponentReads input through the active profile and fires gameplay events. Can be extended for specialized input handling.Input Options

Quick Start

  1. Create an Options Manager prefab with the GS_OptionsManagerComponent.
  2. Create a GS_InputProfile data asset in the Asset Editor.
  3. Add input groups and event mappings to the profile.
  4. Assign the Input Profile to the Options Manager.
  5. Add the Options Manager .spawnable to the Game Manager’s Startup Managers list.
  6. Attach GS_InputReaderComponents to entities that need to process input.

See Also

1.5.1 - Options Manager

The singleton options controller — holds the active Input Profile and provides runtime configuration data to game systems.

Image showing the Options Manager component, as seen in the Entity Inspector.

Overview

The Options Manager is the central anchor of the Options system. It holds the active Input Profile data asset and provides it to Input Readers and other game systems that need runtime configuration data.

As a Manager, it is spawned by the Game Manager and participates in the standard two-stage initialization lifecycle.

How It Works

  1. The Options Manager initializes during the Game Manager’s startup sequence.
  2. It loads the configured Input Profile data asset.
  3. Input Readers across the game query the Options Manager for the active profile via OptionsManagerIncomingEventBus.
  4. The Options Manager responds to standby events — pausing and resuming input processing when the game enters or exits standby mode.

Setup

Image showing the Manager wrapper entity set as Editor-Only inside Prefab Edit Mode.

  1. Create an entity. Attach the GS_OptionsManagerComponent to it.
  2. Assign your Input Profile data asset.
  3. Turn the entity into a prefab.
  4. Enter prefab edit mode. Set the wrapper entity (parent) to Editor Only. Save.
  5. Delete the Options Manager entity from the level.
  6. In the Game Manager prefab, add the Options Manager .spawnable to the Startup Managers list.

Inspector Properties

PropertyTypeDefaultDescription
Input ProfileAZ::Data::Asset<GS_InputProfile>NoneThe active Input Profile data asset. Input Readers across the game will use this profile for event-to-binding mappings.

API Reference

Request Bus: OptionsManagerIncomingEventBus

Singleton bus — call via Broadcast.

MethodParametersReturnsDescription
GetActiveInputProfileAZ::Data::Asset<GS_InputProfile>Returns the currently active Input Profile data asset. Called by Input Readers on activation.

Lifecycle Events (from GameManagerOutgoingEventBus)

EventDescription
OnStartupCompleteThe Options Manager is fully ready. Input Readers can now query for the active profile.
OnEnterStandbyPause input processing.
OnExitStandbyResume input processing.

Usage Examples

Getting the Active Input Profile

#include <GS_Core/GS_CoreBus.h>

AZ::Data::Asset<GS_Core::GS_InputProfile> profile;
GS_Core::OptionsManagerIncomingEventBus::BroadcastResult(
    profile,
    &GS_Core::OptionsManagerIncomingEventBus::Events::GetActiveInputProfile
);

if (profile.IsReady())
{
    // Use the profile to look up bindings, check group states, etc.
}

Extending the Options Manager

Extend the Options Manager when you need additional runtime settings (graphics quality, audio levels, accessibility), custom options persistence, or platform-specific configuration.

Header (.h)

#pragma once
#include <Source/OptionsSystem/GS_OptionsManagerComponent.h>

namespace MyProject
{
    class MyOptionsManagerComponent
        : public GS_Core::GS_OptionsManagerComponent
    {
    public:
        AZ_COMPONENT_DECL(MyOptionsManagerComponent);

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

    protected:
        void OnStartupComplete() override;

    private:
        float m_masterVolume = 1.0f;
        int m_graphicsQuality = 2; // 0=Low, 1=Med, 2=High
    };
}

Implementation (.cpp)

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

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

    void MyOptionsManagerComponent::Reflect(AZ::ReflectContext* context)
    {
        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
        {
            serializeContext->Class<MyOptionsManagerComponent, GS_Core::GS_OptionsManagerComponent>()
                ->Version(0)
                ->Field("MasterVolume", &MyOptionsManagerComponent::m_masterVolume)
                ->Field("GraphicsQuality", &MyOptionsManagerComponent::m_graphicsQuality);

            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
            {
                editContext->Class<MyOptionsManagerComponent>(
                    "My Options Manager", "Extended options with audio and graphics settings")
                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                        ->Attribute(AZ::Edit::Attributes::Category, "MyProject")
                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
                    ->DataElement(AZ::Edit::UIHandlers::Slider,
                        &MyOptionsManagerComponent::m_masterVolume, "Master Volume", "Global audio volume")
                        ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
                        ->Attribute(AZ::Edit::Attributes::Max, 1.0f)
                    ->DataElement(AZ::Edit::UIHandlers::ComboBox,
                        &MyOptionsManagerComponent::m_graphicsQuality, "Graphics Quality", "Rendering quality preset");
            }
        }
    }

    void MyOptionsManagerComponent::OnStartupComplete()
    {
        // Apply saved options on startup
        // ... load from save system and apply settings ...

        GS_OptionsManagerComponent::OnStartupComplete();
    }
}

Module Registration

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

See Also

1.5.2 - GS_InputOptions

The input subsystem — Input Profiles for grouped event-to-binding mappings and Input Readers for processing input in gameplay.

Overview

Input Options is the input subsystem within the Options system. It provides two core pieces:

  • Input Profiles — Data assets that map input bindings to named events, organized into toggleable groups. These replace O3DE’s raw input binding files with a more flexible system that supports runtime rebinding and per-group enable/disable toggling.
  • Input Readers — Components that read input through the active profile and fire gameplay events. They can be extended for specialized input handling (player controllers, UI navigation, etc.).

Architecture

GS_OptionsManagerComponent
  └── Active GS_InputProfile (data asset)
        ├── InputBindingGroup: "Movement"
        │     ├── EventInputMapping: "MoveForward" → [keyboard_key_alphanumeric_W]
        │     ├── EventInputMapping: "MoveBack"    → [keyboard_key_alphanumeric_S]
        │     └── EventInputMapping: "Sprint"      → [keyboard_key_modifier_shift_l]
        ├── InputBindingGroup: "Combat"
        │     ├── EventInputMapping: "Attack"   → [mouse_button_left]
        │     └── EventInputMapping: "Block"    → [mouse_button_right]
        └── InputBindingGroup: "UI"
              ├── EventInputMapping: "Confirm"  → [keyboard_key_alphanumeric_E]
              └── EventInputMapping: "Cancel"   → [keyboard_key_navigation_escape]

GS_InputReaderComponent (on player entity, UI entity, etc.)
  ├── Queries OptionsManager for active profile
  ├── Listens for raw O3DE input events
  ├── Matches bindings → fires named events
  └── EnableInputGroup / DisableInputGroup for runtime control

Components

ComponentPurposeDocumentation
GS_InputProfileData asset defining input groups, event mappings, and key bindings.Input Profiles
GS_InputReaderComponentReads input through the active profile and fires gameplay events.See below

Input Reader

The Input Reader component sits on any entity that needs to process input. It queries the Options Manager for the active Input Profile, then listens for raw O3DE input events and matches them against the profile’s bindings to fire named gameplay events.

Key Features

  • Group toggling — Enable or disable entire input groups at runtime. For example, disable “Combat” inputs during a dialogue sequence, or disable “Movement” inputs during a cutscene.
  • Claim input — The ClaimAllInput flag causes the reader to absorb matched input events, preventing them from reaching other readers. Useful for layered input (e.g., UI absorbs input before gameplay).
  • Extensible — Extend the Input Reader for specialized input handling. GS_Play uses this internally for player controller input and UI navigation input.

API: InputReaderIncomingEventBus

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

MethodParametersReturnsDescription
EnableInputGroupconst AZStd::string& groupNamevoidEnables a named input group for this reader. Events in this group will fire on matched input.
DisableInputGroupconst AZStd::string& groupNamevoidDisables a named input group. Events in this group will be ignored until re-enabled.
IsGroupDisabledconst AZStd::string& groupNameboolReturns true if the named group is currently disabled.

Usage Example

#include <GS_Core/GS_CoreBus.h>

// Disable combat inputs during dialogue
GS_Core::InputReaderIncomingEventBus::Event(
    playerEntityId,
    &GS_Core::InputReaderIncomingEventBus::Events::DisableInputGroup,
    AZStd::string("Combat")
);

// Re-enable when dialogue ends
GS_Core::InputReaderIncomingEventBus::Event(
    playerEntityId,
    &GS_Core::InputReaderIncomingEventBus::Events::EnableInputGroup,
    AZStd::string("Combat")
);

Setup

  1. Create a GS_InputProfile data asset in the Asset Editor.
  2. Add input groups and event mappings to the profile.
  3. Assign the profile to the Options Manager.
  4. Attach a GS_InputReaderComponent to any entity that needs to process input.
  5. The Input Reader automatically queries the Options Manager for the active profile on activation.

See Also

1.5.2.1 - Input Profiles

Data assets for input binding configuration — map key bindings to named events, organized into toggleable groups for advanced runtime input control.

Image showing the InputProfile data asset, as seen in the Asset Editor.

Overview

Input Profiles are data assets that map key bindings to named events, organized into groups that can be enabled and disabled at runtime. They replace O3DE’s raw input binding files with a more flexible system that supports runtime rebinding, per-group toggling, and options menu customization.

How It Works

An Input Profile contains a list of InputBindingGroups. Each group contains EventInputMappings that define the relationship between a named event, its key bindings, and processing options.

Data Structure

GS_InputProfile
  └── InputBindingGroups[]
        ├── groupName: "Movement"
        └── eventMappings[]
              ├── EventInputMapping
              │     ├── eventName: "MoveForward"
              │     ├── inputBindings: ["keyboard_key_alphanumeric_W"]
              │     ├── deadzone: 0.0
              │     └── processUpdate: true
              └── EventInputMapping
                    ├── eventName: "Jump"
                    ├── inputBindings: ["keyboard_key_alphanumeric_Space"]
                    ├── deadzone: 0.0
                    └── processUpdate: false

EventInputMapping

FieldTypeDescription
eventNameAZStd::stringThe name used to fire this event in gameplay through Input Readers. This is the identifier your gameplay code listens for.
inputBindingsAZStd::vector<AZStd::string>One or more raw O3DE input binding names (e.g., keyboard_key_alphanumeric_W, mouse_button_left, gamepad_button_a). Multiple bindings allow the same event to fire from different input sources.
deadzonefloatDead zone threshold for analog inputs (joysticks, triggers). Values below this threshold are treated as zero. Set to 0.0 for digital inputs (keys, buttons).
processUpdateboolWhen true, the event fires continuously while the input is held. When false, the event fires only on initial press and release. Use true for movement and camera input; false for discrete actions like jumping or interacting.

TypeId: {61421EC2-7B99-4EF2-9C56-2A7F41ED3474}

InputBindingGroup

FieldTypeDescription
groupNameAZStd::stringName of the group (e.g., “Movement”, “Combat”, “UI”). Used by Input Readers to enable/disable groups at runtime.
eventMappingsAZStd::vector<EventInputMapping>The event mappings belonging to this group.

TypeId: {37E880D1-9AB4-4E10-9C3C-020B5C32F75B}


Creating and Using an Input Profile

For initial startup instructions refer to the Core Set Up Guide.

  1. In the Asset Editor, open the New menu and select GS_InputProfile. This creates a blank Input Profile.
  2. Add Input Groups — Create groups that cluster related input events (e.g., “Movement”, “Combat”, “UI”). Groups can be toggled on/off at runtime by Input Readers.
  3. Add Event Mappings — Within each group, add EventInputMappings. Set the Event Name to the identifier your gameplay code will listen for.
  4. Set Bindings — Add the raw O3DE input bindings that should trigger each event. These are standard O3DE raw mapping names.
  5. Configure Deadzone — For gamepad joystick or trigger inputs, set an appropriate deadzone value (e.g., 0.15). Leave at 0.0 for keyboard and mouse inputs.
  6. Set Process Update — Enable for continuous input (movement, camera look). Disable for discrete actions (jump, interact, attack).
  7. Assign to Options Manager — Add the profile to the Options Manager’s Input Profile property.

API Reference

These methods are available on the GS_InputProfile data asset class for runtime binding queries and modifications.

MethodParametersReturnsDescription
GetMappingFromBindingconst AZStd::string& binding, const AZStd::string& groupName = ""const EventInputMapping*Looks up the event mapping associated with a raw binding. Optionally restrict the search to a specific group. Returns nullptr if not found.
GetGroupNameFromBindingconst AZStd::string& bindingconst AZStd::string*Returns the group name that contains the given binding. Returns nullptr if not found.
HasBindingconst AZStd::string& bindingboolReturns true if the binding exists anywhere in the profile.
ReplaceEventInputBindingconst AZStd::string& eventName, const AZStd::string& newBindingboolReplaces the existing binding for the named event with a new one. For runtime rebinding in options menus. Returns true on success.
AddEventInputBindingconst AZStd::string& eventName, const AZStd::string& newBindingboolAdds an additional binding to the named event. Returns true on success.
RemoveEventBindingconst AZStd::string& eventName, const AZStd::string& bindingToRemoveboolRemoves a specific binding from the named event. Returns true on success.

Usage Examples

Runtime Rebinding (Options Menu)

#include <GS_Core/GS_CoreBus.h>

// Get the active input profile
AZ::Data::Asset<GS_Core::GS_InputProfile> profile;
GS_Core::OptionsManagerIncomingEventBus::BroadcastResult(
    profile,
    &GS_Core::OptionsManagerIncomingEventBus::Events::GetActiveInputProfile
);

if (profile.IsReady())
{
    // Rebind the "Jump" event from Space to E
    profile.Get()->ReplaceEventInputBinding(
        "Jump",
        "keyboard_key_alphanumeric_E"
    );

    // Add an alternative binding (gamepad A button)
    profile.Get()->AddEventInputBinding(
        "Jump",
        "gamepad_button_a"
    );
}

Checking if a Binding Exists

if (profile.IsReady() && profile.Get()->HasBinding("keyboard_key_alphanumeric_W"))
{
    // This binding is mapped to an event in the profile
}

See Also

1.6 - 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.6.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

1.6.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.

1.6.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

1.6.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.

1.6.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.

1.6.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.

1.6.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.

1.6.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.).

1.6.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

1.6.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

1.6.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

1.6.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

1.6.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

1.7 - Templates

Starter template files for creating custom GS_Core components — C++ classes and Script Graph files for managers, savers, stage data, and actions.

Overview

/Templates/

ClassWizard templaets created for GS_Core specific class creation.

2 - AI

Bringing the world to life.

Overview

The AI System.


Set Up

Refer to the AI Set Up Guide in Get Started section.


Features

3 - Cinematics

Build dramatic and detailed experiences.

Overview

The Cinematics System.


Set Up

Refer to the Cinematics Set Up Guide in Get Started section.


Features

3.1 - GS_DialogueSystem

Overview of the incredibly powerful Dialogue System and all of it’s contributing parts.

Dialogue Data Structure

Sequences

Clusters of dialogue meant to represent a single dialogue encounter. Used for Dialogue Selection, as well as sorting/organizing in the Editor.

Dialogue

The core of the system. The actual dialogue that will be presented within the game runtime.

Selectors

A system to allow branching out from a starting point into many destinations. Defined by the unique selectors processing of those branches.

Selector paths are affected by the preceding conditions, culling options that are invalid.

Option Selector & Options

The standard “Dialogue Option” method of allowing a player to select their responses.

Random Selector

A randomizer selector. This selector does not pause for input, it simply randomly selects where the branch will lead.

Pure Random & Weighted variants.

Events

Triggered functionality that encompasses many systemic outcomes, used intertwined with the dialogue paths being navigated.

Records

Integrated with the GS_Save record keeper system. This event type allows for changing records that affect the rest of gameplay, as well as dialogue conditions.

Actions

A broad reaching type. Using GS_Actions and likely more. Actions can drive unique behaviour in and outside of the Dialogue Sequence.

Things like activating and deactivating things, spawning, character moving and pathfinding.

Additionally allowing the use of custom actions, like RPGStats changing stat, or vitality. Inventory giving/taking an item, or affecting currency.

Posing

A targeted event type that supports the many data points used for shifting and changing an actors state.

Pointed at actors it affects:

Poses, Emotional state, Profile Images, and the blending of all these things.

Conditions

A very critical system. Conditions are meant to evaluate MANY possible data points as a means to filter possible outcomes of navigating dialogue.

As a standard, uses Records to determine if a dialogue path is available to the navigation system. Greater than, Less than, equal, not equal evaluations used with the Records integer format.

Additionally allowing the use custom conditional evaluators, like RPGStats enabling stat or vitality checks. Inventory enabling “hasItem”, and “hasCurrency” checks.


Dialogue Editor

The in O3DE Editor GUI for authoring dynamic and complex branching dialogue.

It includes interfaces for editing settings, as well as defining actors and many meta-data points around presenting the actors in dialogue sequences.


Dialogue Sequencer

The actual system that pressents the dialogue at runtime, using the dialogue data to properly deliver the dialogue experience.


Built in Dialogue UI

Overlay UI

In-world UI

Subtitle UI


Dialogue Predefined Actors

Actors Outline

Actors Names, Profile images, pose animations, pose images

Pose groupings based on emotion category.

3.1.1 - Data Structure

Meta Structure

DialogueDatabase{
  "settings":
  {
    "ui":{
      #IF USING_GS_UI
      "windowOpenUiProfile": "../DisplayProfiles/SoftFade.uianimprofile",
      "profileImageUiProfile": "../DisplayProfiles/AvatarChange.uianimprofile"
      #ENDIF
    },
    "sequencing":{
      "defaultDelay": "0.5",
      "defaultContinue": "instant",
      "defaultTypewriterSpeed": "6"
    }
  },
  
  "actors":
  {[
    {
      "actorName": "perry",
      "actorDisplayName": "Perry",
      "profileImages":{[
        {
          "profileAttitude": "default",
          "imagePath": "../image/path/i.png"
        }
      ]},
      "poses":{[
        {
          "poseAttitude": "default",
          "poseName": "idleStand"
        }
      ]}
    }
  ]},

  "sequences":
  {[
    {
      "sequenceId": "0001",
      "sequenceName": "FirstSequence",
      "nodes":{[...]}
    }
  ]}
}

Sequences

Outline

You’ve upset Perry so they’ve confronted you.

You can antagonize Perry further, driving them to be angry with you.

Or you can apologize. They’ll brush you off normally, but if your charisma is high enough, you’ll leave on good favour.

Delays in the conversation pace between nodes.

Text can remain open, or close, then reopen at next text.

"nodes":{[
  {
    "id": "0001",
    "type": "start",
    "connections": 
    {
      {
        "to":"0002",
        "conditions":{}
      }
    }
  },
  {
    "id": "0002",
    "type": "text",
    "actor": "perry",
    "actorTargetId": "perryStandingOnTheBalcony",
    "text": "How dare you!?",
    "closeText": "false",
    "continue": {
      "type": "displayAndDelay",
      "delay": "default"
      },
    "connections": 
    {
      {
        "to":"0003",
        "conditions":{}
      }
    }
  },
  {
    "id": "0003",
    "type": "options",
    "options": 
    {[
      {
        "optionNumber": "0001",
        "text":"Because I can!",
        "connections": {[
            {
              "to":"0004",
              "conditions":{}
            }
        ]}
      },
      {
        "optionNumber": "0002",
        "text":"I'm so sorry!",
        "connections": {[
            {
              "to":"0005",
              "conditions":{}
            },
            {
              "to":"0006",
              "weight", "10",
              "conditions":{[
                "greaterThan":{
                  "checkValue": "charisma"
                  "evaluationValue": "10"
                }
              ]}
            }
        ]}
      }
    ]}
  },
  {
    "id": "0004",
    "type": "SetRecord",
    "recordName": "PerryAngry",
    "recordValue": "1",
    "connections": 
    {
      {
        "to":"etc..",
        "conditions":{}
      }
    }
  },
  {
    "id": "0005",
    "type": "text",
    "actor": "perry"
    "text": "How can I forgive you after that?",
    "closeText": "true",
    "continue": {
      "type": "waitForInput",
      },
    "connections": 
    {
      {
        "to":"etc...",
        "conditions":{}
      }
    }
  },
  {
    "id": "0006",
    "type": "text",
    "actor": "perry"
    "text": "Charisma Success: Oh, that's okay, you were doing your best.",
    "closeText": "true",
    "continue": {
      "type": "afterSeconds",
      "time": "3.5"
    "connections": 
    {
      {
        "to":"etc...",
        "conditions":{}
      }
    }
  }
]}

Types

  • Systemic

  • Start

  • Restart

  • Jump to New Sequence = “name/id“

  • Jump to Specific Node = “name/id“

  • Random

  • Dialogue

  • Text

  • Performance

  • Sound

  • Options

  • Actions

  • SetRecord

  • Trigger Something…

Sequence 2

Outline

You return to Perry. They greet you based on their disposition.

The delay in image/pose change makes a painful gap between when they talk to you.

"nodes":{[
  {
    "id": "0001",
    "type": "start",
    "connections": 
    {
      {
        "to":"0002",
        "conditions":{}
      },
      {
        "to":"0003",
        "conditions":{[
          "checkRecord":{
            "recordName":"PerryAngry",
            "evaluation":"equalTo",
            "recordValue":"1"
          }
        ]}
      }
    }
  },
  {
    "id": "0002",
    "type": "text",
    "actor": "perry",
    "targetActorId": "perryInTheWorkshop",
    "text": "What do you need?",
    "closeText": "false",
    "continue": {
      "type": "displayAndDelay",
      "delay": "default"
      },
    "connections": 
    {
      {
        "to":"0005",
        "conditions":{}
      }
    }
  },
  {
    "id": "0003",
    "type": "performance",
    "actor": "perry",
    "pose": "aggravated",
    "profileImage": "aggravated",
    "continue": {
      "type": "displayAndDelay",
      "delay": "0.1"
      }
    "connections": 
    {
      {
        "to":"0004",
        "conditions":{}
      }
    }
  },
  {
    "id": "0004",
    "type": "text",
    "actor": "perry",
    "targetActorId": "perryInTheWorkshop",
    "text": "Oh, it's you again...",
    "closeText": "false",
    "continue": {
      "type": "displayAndDelay",
      "delay": "default"
      },
    "connections": 
    {
      {
        "to":"0005",
        "conditions":{}
      }
    }
  }
]}

Notes

Whenever a sequence hits a dead end, it closes the dialogue.

Maybe we add an “onEnd” possible processing.

Condition branches will select the most elaborate first, stepping down until no condition. Connection “Weight“ will manually set prio.

I have gone for categorical “types”, but we may want to just go hyper granular and do one type per node.

So “Performance” would become:

  • ProfileImage

  • Pose (body idle anim)

  • Expression (facial idle anim)

  • Emote (one and done full animation)

  • PathTo

Actions would become:

  • SetRecord

  • AffectStats

  • AffectCurrency

  • Affect Inventory/Equipment

  • Enable/Disable Entity

  • Start/Stop Animation

  • Start/Stop Timeline

  • Change PhantomPriority, Enable/Disable Cam

  • etc.


Options can have conditions, and if they are not met, they will not be available. Options Nodes will require at least ONE non-conditional.

(Or we just exit with no options, like no continuation to a chain.)


actorTargetId is to allow for multiple performers of the same name in a level, but target the particular sequence to THAT particular version of the actor. This is for performance commands, and for inworld speech bubble tracking.


Conditions

Any node can have any number of conditions.

Has can always be !Has.

Numerical conditions always have the operators: greater, less, equal, not-equal

  • Record

  • HasItem

  • StatsValue

  • HasCurrency

  • HasEquipped

  • HasStatusEffect

3.1.2 - Editor

Information on the in engine GUI.

Performer

Performer Targeting

Sequences

Localization

Nodes

Start / End

End

Restart

New Sequence

Specific Node

Dialogue

Text

Animated/Stylized Text

Performance

Posing

Pathing

Portrait

Trigger Timeline

Sound

2D

3D

Option Menu + Choices

Actions + Set Record

Conditions

How to use conditions to determine path.

Not always related to player choice.

Timeline Integration

Start Sequence

Continue Dialogue

UI

O3DE Editor Menu

Editor GUI

Adding icon to editor is a complex problem, outlined by Nick.

Sequence Window

The Inspector Window can have any number of sub-tabs. Which might be valuable for adding unique settings, or multiple Conditions, for example. Basically component-ize different node types.

System Window

Actors Window

Ingame UI

3.1.3 - Sequencer

The runtime system that presents the dialogue in gameplay.

Sequences

3.1.4 - Built-in UI

UI

3.1.4.1 - Typewriter

3.1.5 - Actors

Meta Info

Emotion Categories

Profile Image Sets

Animated Posing Sets

3.2 - Timeline Expansion

4 - Environment

The environment gem for the GS_Play framework.

5 - Interaction

Touch the world, have the world touch back.

Overview

The Inetraction System.


Set Up

Refer to the Interaction Set Up Guide in Get Started section.


Features

Pulsors

Targeting

World Triggers

5.1 - Pulsors

Overview

Setup

Refer to the Interaction Set Up Guide in Get Started section.

Features

5.1.1 - Pulses

Image showing the Pulse component, as seen in the Entity Inspector.

Pulse Overview

Functionality

Pulsors are the thematic components that represent a single Pulse Interaction.

Each component carries the relevant information necessary to exchange data with the Reactors that are able to detect said Pulse.

Get “PulseType” event for each type.

By unifying these exchanges, we open up a workflow to add any number of new Pulsors and related Reactors.

Pulsors can be stacked on a Trigger Collider. What can be reacted to will all do it in tandem. Pulsors effectively define living properties of the world and apply that impact to the receiving entity.

Pulse Types

Impulse

Physically launches things.

Needs Impulse Dir entity.

Can be facing, or radiate from entity point.

Collide

Informs the Reactor of a Collision.

This can do non-damage effects like destroy destructible objects.

Hit (Damage)

Attack Pulse. Passes on Attacker, weapon, damage rolls, etc.

Burned

Processes burn. Can alight the object, or melt.

This is not damage based but the physical effect of burn.

Can be combined with Hit for Burn and Damage. Status Effect for burning status.

Status Effect

Applies Status Effects, with their various modes of affecting the user. Toggle, Timed, Stacking, etc.

Can be combined with Hit & Burn.

Emotion

Emits an emotion to react to.

This could be putting garbage in a trash can (yay!), or harming a civiian npc (AHHH!).

Deflect

Weapon bounces off large objects/environment, interrupts Attack. Not a Pulse?

Could be used against Shields, Armours, Stone Beasts.


Using Pulses


API


Extending Pulses

5.1.2 - Reactors

Image showing the Reactor component, as seen in the Entity Inspector.

Pulse Reactor Overview

Functionality

All Reactor

Receives a pulse event and starts processing.

If incoming entity is a “pulse” tag.

Based on “canBe X“ evaluation, each valid type then checks for granular pulse tags:

  • Impulse
  • Hit
  • Burned
  • etc.

Unique Reactors

Perhaps we make custom clusters of Pulse processing based on common types:

  • Unit
  • Item
  • Dynamic Object
  • Field

These would still use canBe X booleans to enable and disable specific pulses, but would allow for less logic in any given Reactor.

Single Reactors

Would be a performance consideration. With Pulsors also being independent components, it may lead to quite a lot of components. But is an option.


Using Reactors


API


Extending Pulse Reactor

5.2 - Targeting

Image showing a Unit with Vision, Hearing, and TrueSight arragned from FoV and Radial Sense Fields.

Introduction

The targeting system is a, usually centralized around a unit, scanning and selection system that gathers information and sets up an array of values for easy processing by other systems.

Using “Targets” of different types allows the system to filter and categorize lists of targets based on what they represent.

Using an array of sensory fields, the system is able to scan the environment in different ways, and send targets to different sensory destinations in the categorization system.

GS_TargetingHandler

The central processor for the Targeting system. You only need one per source of sensory processing.

The Targeting Handler receives registrations from the Senses Fields, and gathers lists of targets based on categorical and tagging info around them. This creates a dense spatial context around the owner of the handler, usually Character Units, and allows for complex representation of the target through a sensory lense.

Memories, lack of skill to be aware, vision vs auditory awareness; all can be implemented as a means of depicting the world through fallible measures rather than through absolute representation of the world at all times.

Sense & Targeting Fields

A collection of collision volumes that detect and process targets that fall within and without their field of view.

Has Line of Sight features to better process targets.

Has filters to disallow certain overlaps, like self, select entities, or tags.

Targets

A simple but critical component to the targeting system. Targets serve as the element that makes the system awar of targets.

Extending from this class allows for basic functionality of overlap and registration. Followed by more unique processing through the child classes extended attributes.

Setup

Refer to the Interaction Set Up Guide in Get Started section.

Features

5.2.1 - Targeting Handling

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality

Selection Offset

Knowledge Processing

Stat driven values that determine how long a target needs to be detected to beceome part of the “active knowledge” of the unit. Applies to players as well, as the targets will be non-selectable if unknown.

Memory Processing

Targeting Cursor

Interaction Range Field

A unique Sense Field.

Collider that defines the most immediate interactable targets.

Gives “In Interaction Range“ cross reference to the senses targets.


Setting Up Your Targeting Handler


API

// GS_TargetingHandlerIncomingEvents::Handler overrides
bool RegisterTarget(AZ::EntityId entity) override;

bool UnregisterTarget(AZ::EntityId entity) override;

bool RegisterInteractionRangeTarget(AZ::EntityId entity) override;

bool UnregisterInteractionRangeTarget(AZ::EntityId entity) override;

AZ::EntityId GetInteractTarget() override { return interactTarget; };

// Local Methods
virtual void CheckForTick();

virtual void ProcessClosestInteractable();

ProcessAllInteractables

Determines the closest target, then processes the type that target is: Interactable/Item. Then sets the SelectedTarget to that single target.

Get Nearest methods fulfil the Processing, as well as AI checks.

Has Target of type. Query the database.


Extending Targeting Handler

5.2.2 - Senses and Targeting Fields

Image showing an Interaction Field component, as seen in the Entity Inspector.

Sense Fields Overview

Functionality

Fields always need to point to their designated Targeting Handler. Because fields all use their own collision layers, it is not advised to have any fields on the main unit capsule root. As they would then be forced to use the Units physics layer.

Interaction Fields

A specific type of field that populates the “In Interaction Range” list, enabling very atrgeted interact distance, and sorting limits.

Can be a uniquely shaped collider, which gives more control than the sensors.

FoV Detector

Fulcrum shaped cone facing a direction. Resolution and range define the arch of the end of the Fulcrum.

Has Line of Sight toggle.

Radial Detector

Radial, range sets radius.

Has Line of Sight

Probe Detector

A shape/ray cast that returns all overlaps.

Has Line of Sight (prevents detection through walls.)

Mesh Detector

A new one, use a mesh to define a collider?

Has Line of Sight


Using Sense Fields


API

GS_TargetingInteractionFieldComponent

// PhysicsTriggerVolumeComponent
bool TriggerEnter(AZ::EntityId entity) override;

bool TriggerExit(AZ::EntityId entity) override;

Extending Sense Fields

5.2.3 - Targets

Image showing the Target component, as seen in the Entity Inspector.

Target Overview

Functionality

Interact Target

Item Target

Enabled from GS_Item on “#IF GS_INTERACTION“

Unit Target

Enabled from GS_Unit on “#IF GS_INTERACTION“

PoI Target


Using Targets


API


Extending Targets

5.2.4 - Interact Input Reader

Image showing the InteractInputReader component, as seen in the Entity Inspector.

Interact Input Reader Overview

Extends Input Reader.

This component is a standalone input detector utility to enable Interact Input firing outside of the GS_Unit Gem.

It intakes the Event Name for your chosen interaction input, as defined in your Input Profile and ignores all other input.

Functionality

Interact Input Reader needs to be put in the same entity as the Targeting Handler.

After firing the correct Input Event, the Interact Input Reader requests the current InteractTarget from the Targeting Handler. It then sends a DoInteractAction event, with a reference to its EntityId as caller, to the InteractTriggerAction component that should be present on the Interact Target.

The World Trigger and Trigger Action system takes care of the rest.


Setting Up Your Interact Input Reader

Assure that the component is placed on the same Entity as the Targeting Handler.

Image showing the Interact Input Reader Component alongside the Targeting Handler Component.

Fill the Interact Event Name with the Event Name for your chosen interaction input, as defined in your Input Profile.

So long as the Input Profile that’s set in your GS_OptionsManager has that event set. The rest should work as is.


API

// OptionsManagerOutgoingEventBus
void HandleStartup() override;

// Local Methods (Parent Input Reader Component Class)
void HandleFireInput(AZStd::string eventName, float value) override;

Extending Interact Input Reader

5.3 - WorldTriggers

Overview

The World Trigger System is a simple, but robust, pattern of using Trigger Actions as reactors, or nerve endings, to evaluate and fire off a triggering event to a World Trigger.

Using trigger channels, the Trigger Actions and World Triggers can target and respond to specific events, regardless of if they are arranged on the same entity.

The power of the pattern of Trigger Actions and World Triggers is that nearly any Trigger Action, can trigger any World Trigger.

Their extensibility allows for easy implementation of unique features, while allowing the underlying class structure to process, and handle, the interaction between the two component types.

Trigger Actions

The Sensory component of the World Trigger pattern. TAs (Trigger Actions) receive/or monitor for changes in the world state, then, if everything is right, fires off to the receiving World Trigger to enact the outcome back into the world. Hence the naming: “World” triggers.

There are a couple extended TAs to create some common sub-types for regular patterns in use: Interact Trigger Actions, and Collision Trigger Actions. They both follow the same triggering process, but have an additional layer of functionality to interface with Interaction, or Collision/Triggering processing.

World Triggers

The Output component of the World Trigger Pattern. WTs (World Triggers) receive Trigger or Reset events from the TAs, and process how they will affect the world state.

They can be as simple at toggling an entity on and off. Or as complex as holding a multi-staged Activation/Deactivation tree to display a changing piece of environment over multiple stages of growth or destruction.

Wts never need to know what is driving the reason for firing. They simply do the act of changing the world environment. Hence the naming: “World” triggers.

Setup

Refer to the Interaction Set Up Guide in Get Started section.

Features

5.3.1 - Trigger Actions

Image showing a Player Interact TriggerAction component, as seen in the Entity Inspector.

Trigger Actions Overview

Functionality

Action Types

Environmental

  • Difficulty (Game, Dungeon, Quest, Boss)
  • Population (AG Village)
  • Time
  • Record
  • Dungeon In/Complete
  • Quest In/Complete
  • Has Quest
  • Weather
  • OnStart
  • Player Stats
  • Player HasItem
  • Every Seconds
  • OnQuestStart
  • OnQuestEnd

Collision Fields

  • Proximity
  • Correct InWorldObject Conditions
  • Unit Stats
  • Unit Inventory
  • Pulse React

On Unit

  • OnDeath
  • Has/HasNot Stats
  • Has/HasNot Item/Inventory
  • Has/HasNot Equip

Interact Trigger Actions

Specific Trigger Actions activated by The Interact Input.

Passes Source interactor to DoActonFromInteractor, which fires the regular DoAction.

Interact Types

Self Firing

  • World Item Interact
  • Carry
  • Pilot Unit
  • Bonfire/Menu Open

Triggering

  • Select
  • Give Item
  • Channel
  • Craft

Using Trigger Actions


API

//! Override to establish your own DoAction process
//! Call Parent DoAction to see if Action Succeeds. Then use returned bool to process final behaviour.
virtual bool DoAction();
//! Override to establish your own ResetAction process
//! Call Parent ResetAction to see if Reset Succeeds. Then use returned bool to process final behaviour.
virtual bool DoResetAction();

//! Override to establish your own evaluation criteria.
virtual bool EvaluateAction() { return true; };
//! Override to establish your own reset evaluation criteria.
virtual bool EvaluateResetAction() { return true; };

Extending Trigger Actions

5.3.1.1 - Record Trigger Action

Image showing the RecordTriggerAction component, as seen in the Entity Inspector.

Record Trigger Action Overview

A component that triggers when the a defined record is changed to a certain value. Useful for connecting events on story progression flags, world variables etc.

Functionality

Inheriting from the RecordKeeperComponent’s Incoming Event Bus. It will automatically take in record updates and call TriggerActionComponent’s DoAction() if the changed record matches the local record’s name and value saved on the component.


Using Record Trigger Action

  1. Create an Entity and add the Record Trigger Action Component in the inspector.
  2. Set the Trigger Channel to the channel where you want your trigger to fire.
  3. Define the record name, this is the name of the record you are looking at for changes.
  4. Define the record value, set this value to what value the recordName needs to be at in order to trigger the action.

API

From TriggerActionComponent

//! Returns true if the values of the changed record's data is the same as our local record (This runs when DoAction() is called).
//! Override to establish your own evaluation criteria.
virtual bool EvaluateAction() { return true; };

From RecordKeeperIncomingEvents

//! Calls TriggerActionComponent's Definition of DoAction() when the defined record is changed.
//! Override to trigger when your record is changed.
virtual void RecordChanged() { return true; };

Extending Record Trigger Action

5.3.2 - World Triggers

Image showing a PrintLog World Trigger component, as seen in the Entity Inspector.

World Trigger Overview

Functionality

World Triggers do the actual processing once an input fires.

Triggers can Trigger Other Triggers.

Triggers can have Reset Enabled or Disabled.

Reset can be by toggle, or by direct command.

Repeated Activations can just keep firing the same Trigger if valid.

Triggers have logic for:

  • Trigger

  • Sets Triggered

  • Updates Listening

  • Reset

  • Removes Triggered

  • Updates Listening

Some can have boolean logic like “Any“ or “All“ as requirements to finally activate. Allowing the need for multiple Switches.

TriggerTypes

Basic Types

  • Toggle (On/Off)
  • ProgressionSwitch (Count Up/Down)
  • Make Sound
  • Play/Stop Cinematic/Timeline
  • Dialogue
  • Item Spawn
  • Spawn Object
  • Spawn Pulse
  • GS_Action
  • SetRecord
  • Play/Stop Animation

Unit Facing

Has various means of targeting a Unit.

  • Equip Item
  • Affect Stats
  • Give Item
  • Status Effect
  • Level Up
  • Change Stage (player only)
  • Teleport

Repeater/Boolean Types

  • Invert Trigger
  • Any/All Trigger
  • Timeout Trigger

Using World Triggers


API

// WorldTriggerIncomingEvents
//! Trigger intakes a list of channels to determine if it should fire.
//! If onlyOnce, will not fire a second time.
//! Call parent to determine if Trigger goes through before handling additional logic.
bool Trigger(AZStd::unordered_set<AZStd::string> channels) override;

//! Reset takes channels like Trigger, but processes the reveral of what the Trigger did.
//! If can't reset, will not fire.
//! Call parent to determine if Reset goes through before handling additional logic.
bool Reset(AZStd::unordered_set<AZStd::string> channels) override;


// Local Methods
// Utility that quickly checks for channels being fired.
virtual bool EvaluateChannels(AZStd::unordered_set<AZStd::string> channels);
virtual bool IsTriggered() { return isTriggered; };

Extending World Triggers

6 - Item

Enrich your characters, fill your world.

6.1 - Equipment

6.2 - Inventory

7 - Performer

Composit your characters with equipment, control their performance with cinematic utilities.

7.1 - Avatar Performer

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

7.2 - Paper Performer

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

7.3 - Mesh Swap

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

7.4 - Head Tracking

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality

Height Definintion


Setting Up Your Targeting Handler


API


Extending Targeting Handler

7.5 - Babble

8 - PhantomCam

Giving deep control over your cameras.

Overview

The Phantom Cam system is a dynamic camera solution that uses camera placeholders, “Phantoms” to represent dozens or hundreds of cameras.

Through a priority system, the top phantom camera at any given time becomes the camera whos settings are what the actual play camera mimics.

Using definitions for blending, the transitions between dominant cameras can be instantaneous or do all sorts of blend easing over all sorts of different timing.


Set Up

Refer to the PhantomCam Set Up Guide in Get Started section.


Features

8.1 - Cam Manager

Image showing the Cam Manager component, as seen in the Entity Inspector.

Cam Manager Overview

Functionality


Setting Up Your Cam Manager

For initial startup instructions refer to the PhantomCam Set Up Guide in Get Started section.


API

// CamManagerOutgoingEventBus
void OnStartupComplete() override;
void OnEnterStandby() override;
void OnExitStandby() override;

// CamManagerIncomingEventBus
void RegisterPhantomCam(AZ::EntityId cam) override;

void UnRegisterPhantomCam(AZ::EntityId cam) override;

void ChangeCameraPriority(AZ::EntityId cam, AZ::u32 priority) override;

void AddCameraInfluence(AZStd::string camName, float influence) override;

void RemoveCameraInfluence(AZStd::string camName, float influence) override;

void SetTarget(AZ::EntityId targetEntity) override;

AZ::EntityId GetTarget() override { return camTarget; };

// Local Method
virtual void EvaluatePriority();

Extending Cam Manager

8.2 - Cam Core

Image showing the Cam Core component, as seen in the Entity Inspector on the MainCamera entity.

Cam Core Overview

The Cam Core is a central pillar to the Phantom Cam system, it is housed on the MainCamera and drives the processes that blend and match it’s properties to the dominant Phantom Cam.

Functionality

When cameras activate, spawn, or change their priority, the Camera Manager sorts through and determines the dominant camera. It then broadcasts that change for the Camera Core to follow.

Using the Blend Profile set to the Cam Core, the Camera will find the most valid blend to use to move towards the destination Phantom Camera. If no blend is available it falls back to defaults defined on the component.

Finally, when the camera arrives at the Phantom Cam, it becomes a child entity, and locks to the Phantom Cam, so that all movements and rotations are matched exactly.

The Cam Core, then waits until the next transition is fired.


Setting Up Your Cam Core

For initial startup instructions refer to the PhantomCam Set Up Guide in Get Started section.

Add the Cam Core component to your Main Camera. There can only be one Main Camera in the game. In order to assure that there is only one, it’s best to make the Main Camera a child of the Cam Manager, and exclude all cameras from your levels.

When your Cam Manager is created, your Cam Core follows and now is ready to interface with your Phantom Cameras.

To configure your Cam Core, edit the Cam Manager Prefab.

Set your Cam Core Defaults.

Create a Blend Profile Data Asset and add it to the CamCore Blend Profile inspector slot.


API

//CamCoreIncomingEventBus::Handler
void SetPhantomCam(AZ::EntityId targetCam) override;

AZ::EntityId GetCamCore() override { return GetEntityId(); };

Extending Cam Core

8.3 - Blend Profiles

Image showing the Blend Profile data asset, as seen in the Asset Editor.

Blend Profile Overview

Blend Profiles are an asset that can be created through the Asset Editor. It’s a simple file that allows the creation of any number of blends between cameras.

Using a From -> To selection format, blends between cameras can be uniquely customized even between 2 cameras, blending one way, from another.

Functionality

When you define a blend from one camera to another, you can then set a target blend time, along with a blend easing type.

There are many Easing Types, as can be seen in the Curves Utility doc.

Best Target Blend

The Blend system has layers of selection based on most specialized blend.

The first evaluation is for an exact From -> To blend.

Following that, the system checks for “Any” -> To blend definition.

Next is a From -> “Any”.

And lastly, is the fall back to default, as defined in the CamCore Component.


Creating and using a Blend Profile

For initial startup instructions refer to the PhantomCam Set Up Guide in Get Started section.

In the [ New ] menu of the Asset Editor, you can select “GS_BlendProfile”, this will create a blank Blend Profile.

Add as many Blends as you wish, using the + button.

In each blend you can set the From target camera, the To target camera, and blending details. The Camera Names are the entity names you set up on your Phantom Camera Entities.

Blank text, or “any”, indicates to the system that this is a broad, less specific target. Allowing “any” -> to cam, or From -> “any” cam blend selection.

Set the blend time you wish the blend to take to span the distance and properties between your from cam an to camera.


API

const PhantomBlend* GetBestBlend(AZStd::string fromCam, AZStd::string toCam) const;

8.4 - Phantom Cameras

Image showing the Phantom Camera component, as seen in the Entity Inspector.

Phantom Camera Overview

Phantom Cameras are the powerful force behind the PhantomCam system, hence its naming. Phantom Cameras represent Cameras, their states, settings, and advanced behaviour.

When a Phantom Cam takes dominance through it’s priority hitting the top of other Phantom Cameras, the MainCamera blends to it based on Blend Settings. The phantom camera itself drives complex behaviour, like orbit inputs, tracking and lookat settings, damping, screen shake, and more.

The robust library of Phantom Camera Types should be able to satisfy most Cinematic and Camera needs.

Functionality


Using Phantom Cameras

For initial startup instructions refer to the PhantomCam Set Up Guide in Get Started section.

Any number of Phantom Cameras can be created and exist in any part of the game state. They will all be connected to the system and be available for blending, priority filtration, and everything else.

To bring a particular phantom cam to become the desired camera, you can affect it or neighbouring cameras in a few ways.

  • You can change the desired phantom cam priority to be above the rest of the cameras. Say priority: 100.
  • You can change the neighbouring phantom cameras priority to fall below the desired phantom cam. Say reducing neighbours priority to 5, because the desired camera priority is 10.
  • You can call Disable Camera on the current dominant camera. This drops it to 0 while it’s disabled. If your desired camera is the next in line, it will then take over.
  • You can call Enable Camera on your desired camera, while it has a higher than the rest priority. Say your desired cam has priority 100, but is disabled, leaving it at 0. When you enable it, it’ll take priority.

Blending


API

// CamManagerOutgoingEventBus
void HandleStartup() override;

// PhantomCameraIncomingEventBus
void EnableCamera() override;

void DisableCamera() override;

void SetCameraPriority(AZ::s32 newPriority) override;

AZ::s32 GetCameraPriority() override { return priority; };

void SetCameraTarget(AZ::EntityId targetEntity) override;

void SetTargetFocusGroup(AZ::EntityId targetFocusGroup) override;

const PhantomCamData* GetCameraData() override { return &camData; };

// Local Methods
virtual void StartupCheck();

virtual void EvaluateCamTick();

//Follow
virtual void ProcessTransformFollow(AZ::Vector3& desiredPos, float deltaTime);

virtual void ProcessPhysicsFollow(float deltaTime);

virtual void ProcessFollowOffset(AZ::Vector3& destFollowPos, AZ::Transform destWorldTMFollow);

//LookAt
virtual void ProcessTransformLookAt(AZ::Quaternion& desiredRot, AZ::Transform curWorldTM, float deltaTime);

virtual void ProcessPhysicsLookAt(float deltaTime);

virtual void ProcessLookAtOffset(AZ::Vector3& destLookPos, AZ::Transform curWorldTM, AZ::Transform destWorldTMLook);

Extending Phantom Cameras

8.4.1 - Orbit Cam

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

8.4.2 - Static Orbit Cam

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

8.4.3 - First Person Cam

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

8.5 - Cam Influence Fields

Image showing a Camera Influence Field component, as seen in the Entity Inspector.

Camera Influence Fields Overview

Functionality


Using Camera Influence Fields


API


Extending Camera Influence Fields

9 - RP Stats

Make your characters more vivid.

9.1 - Status Effect

The core gem for the GS_Play framework.

10 - UI

Making UI easy!

Overview

The UI System.


Set Up

Refer to the UI Set Up Guide in Get Started section.


Features

10.1 - GS_UIManager

Manager

Image showing the GS_UIManager component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// GameManagerOutgoingEventBus
void OnSetupManagers() override;
void OnStartupComplete() override;
void OnShutdownManagers() override;

void OnBeginGame() override;

// UIManagerIncomingEventBus
// The UI systems themselves
void LoadGSUI(const AZStd::string& uiName, const AZStd::string& uiPath) override; //If no string included, does not cause defaultFocus
bool RegisterHub(AZ::EntityId hubEntity) override;
void UnloadGSUI(const AZStd::string& uiName) override;

AZ::EntityId GetUIEntity(const AZStd::string& uiName) override;

// Any windows inside UIManager.
void ToggleUI(const AZStd::string& hubName, bool on) override;
void FocusUI(const AZStd::string& hubName) override;
void NavLastUI() override;

// StageManagerOutgoingEventBus
void BeginLoadStage() override;
void LoadStageComplete() override;

Extending Targeting Handler

Load Unload GSUI

10.2 - UI Hubs

Image showing a UI_HUB component, as seen in the UI Editor.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// UIHubIncomingEventBus
void ToggleHub(bool on) override;

void FocusHub() override;

void FocusInteractable(AZ::EntityId interactable, AZStd::string name) override;

//Any windows inside UIHub.
bool RegisterWindow(AZ::EntityId windowEntity) override;

void FocusWindow(AZ::EntityId targetWindow) override;

void FocusWindowByName(const AZStd::string& windowName) override;

void NavLastWindow() override;

AZStd::string GetUIName() override {return UIName;};

AZ::EntityId GetWindowByName(AZStd::string windowName) override;

Extending Targeting Handler

PlayUIAnims

Plays a range of UI Anims when fired. (Broken)

10.3 - Windows

Image showing the UIWindow component, as seen in the UI Editor.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// UIWindowIncomingEvents
void ToggleInteractable(bool interactable) override;

void FocusWindow(bool resumePosition = false) override;

bool RegisterPage(AZ::EntityId pageEntity) override;

void ChangePage(AZ::EntityId targetPage, bool takeFocus, bool resume) override;

Extending Targeting Handler

10.4 - Pages

Image showing the UIPage component, as seen in the UI Editor.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// UIPageIncomingEventBus
//Page handling
void ShowPage() override;

void HidePage() override;

//Nav
void FocusPage(bool resumePosition = false) override;

void NewInteractableTarget(AZ::EntityId target) override;

// Local Methods
virtual void RegisterThisPage();

virtual void ToggleInteractable(bool interactable);

Extending Targeting Handler

10.5 - Buttons

Image showing the TargetingHandler component, as seen in the UI Editor.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

10.6 - UI Animation

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

10.7 - UI Actions

List of UI Actions

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

PlayUIAnims

Plays a range of UI Anims when fired. (Broken)

11 - Unit

Craft the characters of the world.

Overview

The Unit System.


Set Up

Refer to the Unit Set Up Guide in Get Started section.


Features

11.1 - Unit Manager

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// GameManagerGlobalEventBus
void OnSetupManagers() override;
void OnShutdownManagers() override;
void OnStartupComplete() override;

void OnEnterStandby() override;
void OnExitStandby() override;

// UnitManagerIncomingEventBus
void RequestSpawnNewUnit(AZ::EntityId callingEntityId, AzFramework::Scripts::SpawnableScriptAssetRef unitPrefab, AZ::EntityId spawnParentEntityId) override;

bool CheckIsUnit(AZ::EntityId unitId) override;

Extending Targeting Handler

11.2 - Unit Controllers

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality

Player Controller

AI Controller


Setting Up Your Targeting Handler


API

// UnitControllerIncomingEventBus
void PossessUnit(AZ::EntityId targetUnit) override;

void DePossessUnit() override;

AZ::EntityId GetUnit() override { return possessedUnit; };

AZStd::string GetUniqueName() override { return uniqueName; };

// UnitManagerOutgoingEventBus
void ReturnNewUnit(AZ::EntityId caller, AZ::EntityId newUnit) override;

// Local Methods
virtual void PostActivateProcessing();

virtual void SetUniqueName();

Extending Targeting Handler

11.3 - Input Data

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.3.1 - Input Reactor

Image showing an InputReactor component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.3.2 - Player Input Reader

Image showing a Player Input Reader component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.4 - Units

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler

Unit Collider Configuration

Image showing the collision layers used for a unit collider, as seen in the Entity Inspector.

If you have not set up your PhysX Collision Layers or Groups yet. Refer to the Setting Up Your Project Environment guide.


API

// UnitIncomingEventBus
void Possess(AZ::EntityId possessingController) override;

void DePossess() override;

AZ::EntityId GetController() override { return owningController; };

AZStd::string GetUniqueName() override { return uniqueName; };

// Local Methods
virtual void SetUniqueName();

Extending Targeting Handler

11.4.1 - Movement

Overview

Setup

Refer to the Unit Set Up Guide in Get Started section.

Features

11.4.1.1 - Mover Context

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// UnitOutgoingEventBus
void UnitPossessed(AZ::EntityId possessingController) override;

// MoverContextIncomingEvents
void ChangeMovementMode(AZStd::string targetMoveMode) override;

void RevertToLastMovementMode() override;

void ChangeRotationMode(AZStd::string targetRotateMode) override;

void RevertToLastRotationMode() override;

void ChangeGroundingMode(AZStd::string targetRotateMode) override;

void RevertToLastGroundingMode() override;

void SetMoveInputAxis(AZStd::string axisName, float axisValue) override;

AZ::Vector2* GetMoveInputAxis() override { return &rawMoveInput; };

AZ::Vector3* GetModifiedMoveInputAxis() override { return &modifiedMoveInput; };

AZ::Vector3* GetGroundMoveInputAxis() override { return &groundedMoveInput; };

void SetGroundNormal(AZ::Vector3 newNormal) override;

AZ::Vector3* GetGroundNormal() override { return &groundNormal; };

AZ::Vector3* GetSlopeDirection() override { return &slopeDir; };

float* GetSlopeAngle() override { return &slopeAngle; };

float* GetMaxWalkAngle() override { return &maxWalkAngle; };

void SetMoverState(AZStd::string stateName, AZ::u32 stateValue) override;

AZ::u32 GetMoverState(AZStd::string stateName) override;

// Local Methods
virtual void ModifyInputAxis();

virtual void GroundInputAxis();

Extending Targeting Handler

11.4.1.2 - Movers

Image showing a Mover component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// MoverContextOutgoingEventBus
void MovementModeChanged(AZStd::string modeName) override;
void RotationModeChanged(AZStd::string modeName) override;

//Local Methods
virtual void ToggleMovement(bool on);
virtual void ToggleRotation(bool on);

virtual void HandleMovement();
virtual void HandleRotation();

virtual bool CheckCanOperate();

Extending Targeting Handler

11.4.1.2.1 - 3D Free Mover

Image showing a 3D Free Mover component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.4.1.2.2 - 3D Strafe Mover

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.4.1.2.3 - Slide Mover

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.4.1.2.4 - Side Scroller Mover

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.4.1.2.5 - Grid Step Mover

Image showing the TargetingHandler component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler

11.4.1.3 - Grounders

Image showing a Grounder component, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API

// MoverContextOutgoingEventBus
void GroundingModeChanged(AZStd::string modeName) override;
        
// Local Methods
virtual void ToggleGrounder(bool on);
virtual void HandleGrounding();
virtual void GroundingStateChange(AZ::u32 newState);

virtual bool CheckCanOperate();

Extending Targeting Handler

11.4.1.3.1 - 3DFree Grounder

Image showing a 3D Physics Ray Grounder, as seen in the Entity Inspector.

Targeting Handler Overview

Functionality


Setting Up Your Targeting Handler


API


Extending Targeting Handler