Manager

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

For usage guides and setup examples, see The Basics: GS_Core.

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

GS_GameManager
  └── (spawns) GS_ManagerComponent ◄ you are here — and all other managers extend this

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.

 

Contents


How It Works

The Game Manager drives startup in stages. At each stage it calls out to every manager over GameManagerNotificationBus, each manager performs its own logic, and each reports back over GameManagerRequestBus when it is done. The Game Manager counts those reports and only advances to the next stage once every manager has answered.

That counting handshake is the point of the system: no manager advances until every manager has registered, which is what makes cross-manager dependencies safe without you having to specify an order.

  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. OnSetupManagers — Called by the Game Manager once every manager has reported initialized. Connect to other managers and set up cross-references here. When done it broadcasts OnRegisterManagerSetup().

  3. OnStartupComplete — Broadcast by the Game Manager once every manager has reported setup. The framework is fully ready. The base implementation is empty — override it to begin runtime operation.

  4. OnBeginGame — Broadcast when the game itself begins. This is a cue, not a state: it is fired when your project decides play has actually started, which is a separate question from whether the managers finished starting up.

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

  6. OnShutdownManagers — Called when the game is shutting down. Clean up resources and disconnect buses. When done it broadcasts OnRegisterManagerShutdown().

The base class handles the registration side of steps 1, 2 and 6 for you. Override the hooks to add your logic, then call the base implementation to complete the handshake.


Headless Managers

Headless is the second way to run a manager, and it is a full alternative to the Game Manager — not a reduced one.

Every manager carries a Headless checkbox. Tick it and that manager takes responsibility for its own startup instead of being driven by a Game Manager:

Activate()
  └── calls OnSetupManagers() itself
        └── calls OnStartupComplete() itself

Deactivate()
  └── calls OnShutdownManagers() itself, then disconnects

The registration calls are skipped entirely — there is no Game Manager to report to, so the manager simply advances itself through each stage.

It reaches startup-complete under its own power, with no Game Manager in the project and no startup sequence to participate in. Everything the manager does after that point behaves exactly as it does under the Game Manager.

Why you would use it

The Game Manager path exists because a GS_Play game wants one coordinated startup across a stack of managers. A project that only wants one feature does not need any of that.

Headless is how a GS feature gem is deployed into a project that is not built on GS_Play:

  • Deploying a single feature manager. A project that wants GS_Audio’s audio system — and nothing else — adds the Audio Manager, ticks Headless, and has a working audio system. No Game Manager, no manager stack, no game-flow layer, no dependency on the GS_Play startup sequence.
  • Adding GS features to an existing project that already has its own game lifecycle, title flow and save system. Those responsibilities stay where they are; the GS manager simply starts itself and does its job.
  • Adopting the framework in stages. Take one manager now, headless. Add a Game Manager later and switch it over when the project is ready for the full lifecycle.
  • Runtimes with no game session at all — servers, tools, editor-side scenes.

Both routes are supported deployments. Use the Game Manager when you want the coordinated startup it provides; use Headless when a manager should simply run.

What you take on

The Game Manager’s counting handshake is what guarantees that no manager reaches setup until every manager has initialized. A headless manager has no such barrier — it drives straight through its own chain the moment it activates, independently of anything else in the level.

For a single deployed manager that is exactly what you want. If you run several headless managers that depend on each other, sequencing between them becomes your project’s responsibility. At that point, consider a Game Manager instead — coordinating a manager stack is the problem it exists to solve.

OnBeginGame is never self-fired. Setup and startup are infrastructure a manager can honestly resolve alone, but “the game has begun” is a cue rather than a state — it stays a signal the host project fires when it decides play has started. A headless manager reaches startup-complete and waits.

A headless manager does not report to the Game Manager at all — it self-drives instead of registering, so it takes no part in the counting handshake and cannot disturb one that is running. A project is free to have a Game Manager coordinating a manager stack and a headless manager running alongside it.

The flag lives on the base class, so it is inherited by every manager — Audio, Options, Cam, Save, Stage and any custom manager you write. One uniform mechanism, no per-gem variation.

A subclass that overrides OnSetupManagers, OnStartupComplete or OnShutdownManagers must still chain to the base implementation exactly as before. Headless changes who calls the hooks, not the contract.


Setup

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 contributes two properties to every manager that inherits it. All other properties are defined by the inheriting component (e.g. the Save Manager exposes a Save System Version Number, the Stage Manager exposes a Stages list).

PropertyTypeDefaultDescription
HeadlessboolfalseThis manager drives its own startup, with no Game Manager in the project. See Headless Managers.
Logging Depthint0Verbosity of this manager’s lifecycle logging. Above 0, the manager prints its entity name as it activates, sets up and shuts down — useful for diagnosing startup order on a single manager without making the whole framework chatty.

API Reference

Lifecycle Events: GameManagerNotificationBus

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

EventWhen It FiresWhat You Do
OnSetupManagersAll managers have reported initializedConnect to other managers, set up cross-references, then call base to report.
OnStartupCompleteAll managers have reported setupBegin runtime operation.
OnBeginGameThe project fires it to signal play has startedStart gameplay-only behaviour. Not fired by startup, and never self-fired in headless mode.
OnEnterStandbyGame is entering standbyPause your subsystem (stop ticks, halt processing).
OnExitStandbyGame is exiting standbyResume your subsystem.
OnShutdownManagersGame is shutting downClean up resources, disconnect buses, then call base to report.

Registration Methods

Called by the Manager base class to report back to the Game Manager over GameManagerRequestBus. Do not call these manually — call the base class method instead and it reports for you.

MethodDescription
OnRegisterManagerInit()Called at the end of Activate(). Reports that this manager has initialized.
OnRegisterManagerSetup()Called at the end of OnSetupManagers(). Reports that this manager has completed setup.
OnRegisterManagerShutdown()Called at the end of OnShutdownManagers(). Reports that this manager has shut down.

Manager Base Buses

Separate from the Game Manager handshake, GS_ManagerBus.h declares a small pair of buses that let non-manager components react to their gem’s manager without participating in the startup count.

ManagerBaseNotifications:

EventMeaning
HandleStartupThis gem’s manager has finished starting up. Components that need the manager ready before they wire themselves in listen here.
HandleShutdownThis gem’s manager is shutting down.
EnterStandby / ExitStandbyStandby propagated to dependent components.

ManagerBaseRequests:

MethodReturnsDescription
ManagerExists()boolWhether this gem’s manager is present. Lets a component degrade gracefully instead of assuming.

Usage Examples

C++ — Checking if the Game Manager Is Ready

#include <GS_Core/GS_CoreBus.h>

bool isStarted = false;
GS_Core::GameManagerRequestBus::BroadcastResult(
    isStarted,
    &GS_Core::GameManagerRequestBus::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. Extension is done in C++.

Header (.h)

#pragma once
#include <GS_Core/GS_Manager/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 OnSetupManagers() 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()
    {
        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::OnSetupManagers()
    {
        // Access other managers here — they are all initialized by now

        // IMPORTANT: Call base last — it reports OnRegisterManagerSetup()
        GS_ManagerComponent::OnSetupManagers();
    }

    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

For conceptual overviews and usage guides:

For component references:

For related resources:


Get GS_Core

GS_Core — Explore this gem on the product page and add it to your project.