Everything you need to wire RCE into your game — from input mapping to custom storage paths and DI configuration.
SetActiveState(LoadoutState) is the primary runtime API for controlling which reticle category is displayed. You call it from your game's input system whenever the player's combat posture changes. RCE handles all transition animations, shape blending, and GPU upload automatically.
| Enum Value | Constant | Typical Trigger |
|---|---|---|
| HUD | LoadoutState.HUD |
Always-on elements — set once at startup, rarely changed at runtime |
| Situational Awareness | LoadoutState.SituationalAwareness |
Default idle state — no weapon input active |
| Hip Fire | LoadoutState.HipFire |
Fire button pressed without aiming |
| Aim Down Sight | LoadoutState.AimDownSight |
ADS / right-click / left trigger held |
Only call SetActiveState() when the state actually changes. Calling it every frame with the same value is safe but wasteful — RCE performs an equality check internally, but the call overhead is unnecessary. Guard with a simple if (newState != currentState). See the Performance page for more guidance.
using UnityEngine; using UnityEngine.InputSystem; using RCE.Runtime.Data; using RCE.Runtime.Bootstrap; public class GamepadStateDriver : MonoBehaviour { private ILoadoutManager _loadout; private LoadoutState _current = LoadoutState.SituationalAwareness; void Start() { _loadout = RCEServices.Get<ILoadoutManager>(); } void Update() { var gp = Gamepad.current; if (gp == null) return; LoadoutState next; if (gp.leftTrigger.IsPressed()) next = LoadoutState.AimDownSight; else if (gp.rightTrigger.IsPressed()) next = LoadoutState.HipFire; else next = LoadoutState.SituationalAwareness; if (next != _current) { _loadout.SetActiveState(next); _current = next; } } }
using UnityEngine; using RCE.Runtime.Data; using RCE.Runtime.Bootstrap; public class KeyboardStateDriver : MonoBehaviour { private ILoadoutManager _loadout; private LoadoutState _current = LoadoutState.SituationalAwareness; void Start() { _loadout = RCEServices.Get<ILoadoutManager>(); } void Update() { LoadoutState next; if (Input.GetMouseButton(1)) // Right-click = ADS next = LoadoutState.AimDownSight; else if (Input.GetMouseButton(0)) // Left-click = Hip Fire next = LoadoutState.HipFire; else next = LoadoutState.SituationalAwareness; if (next != _current) { _loadout.SetActiveState(next); _current = next; } } }
ILoadoutManager is the primary interface for controlling the reticle system at runtime. It manages the active combat state, provides access to the current loadout data, and bridges your game logic to the rendering pipeline.
| Member | Description |
|---|---|
SetActiveState(LoadoutState) |
Switches the active combat category. Triggers transition animation if configured for this state pair. |
SetEditState(LoadoutState) |
Sets the category being edited in the visual editor. Does not trigger transitions — used by editor UI only. |
GetCurrentState() |
Returns the currently active LoadoutState. |
LoadoutData ActiveLoadout |
The currently loaded loadout data. Read-only at runtime; modifications go through the editor or serialization layer. |
using RCE.Runtime.Data; using RCE.Runtime.Bootstrap; public class WeaponController : MonoBehaviour { private ILoadoutManager _loadout; void Awake() { _loadout = RCEServices.Get<ILoadoutManager>(); } public void OnAimStart() { _loadout.SetActiveState(LoadoutState.AimDownSight); } public void OnAimEnd() { _loadout.SetActiveState(LoadoutState.SituationalAwareness); } public void OnFireStart() { if (_loadout.GetCurrentState() != LoadoutState.AimDownSight) _loadout.SetActiveState(LoadoutState.HipFire); } public void OnFireEnd() { if (_loadout.GetCurrentState() == LoadoutState.HipFire) _loadout.SetActiveState(LoadoutState.SituationalAwareness); } }
IDataPathProvider is the interface RCE uses to resolve file system paths for all persistent data — loadouts, tracers, and color palettes. The default implementation uses Application.persistentDataPath, but you can provide your own to redirect storage anywhere: a custom user directory, a cloud-sync folder, or a game-specific save location.
| Member | Description |
|---|---|
string UserDataPath |
Root directory for all RCE user data. |
string LoadoutsPath |
Directory where loadout JSON files are stored. |
string TracersPath |
Directory for tracer configuration files. |
string PalettesPath |
Directory for color palette files. |
EnsureDirectoriesExist() |
Creates all required directories if they don't already exist. Called automatically on startup. |
using UnityEngine; using System.IO; public class DefaultDataPathProvider : IDataPathProvider { private readonly string _root; public DefaultDataPathProvider() { _root = Path.Combine(Application.persistentDataPath, "RCE"); } public string UserDataPath => _root; public string LoadoutsPath => Path.Combine(_root, "Loadouts"); public string TracersPath => Path.Combine(_root, "Tracers"); public string PalettesPath => Path.Combine(_root, "Palettes"); public void EnsureDirectoriesExist() { Directory.CreateDirectory(LoadoutsPath); Directory.CreateDirectory(TracersPath); Directory.CreateDirectory(PalettesPath); } }
using System.IO; public class CustomDataPathProvider : IDataPathProvider { private readonly string _root; public CustomDataPathProvider(string basePath) { _root = Path.Combine(basePath, "RCE"); } public string UserDataPath => _root; public string LoadoutsPath => Path.Combine(_root, "Loadouts"); public string TracersPath => Path.Combine(_root, "Tracers"); public string PalettesPath => Path.Combine(_root, "Palettes"); public void EnsureDirectoriesExist() { Directory.CreateDirectory(LoadoutsPath); Directory.CreateDirectory(TracersPath); Directory.CreateDirectory(PalettesPath); } }
Register your custom IDataPathProvider before RCE initializes. If you're using the built-in RCEInstaller, override its path provider binding. If you have your own DI container, bind the interface there instead.
RCEServices is a lightweight static service locator that provides access to all RCE engine interfaces at runtime. Services are registered automatically by RCEInstaller during startup — no manual configuration needed.
| Member | Description |
|---|---|
T Get<T>() |
Resolves and returns the registered implementation of interface T. Throws if not registered. |
bool Has<T>() |
Returns true if an implementation of T is registered. Use to check optional services before resolving. |
GlobalFXBridge GlobalFX |
Convenience property for quick access to the GlobalFX system. Equivalent to Get<GlobalFXBridge>(). |
using RCE.Runtime.Bootstrap; using RCE.Runtime.Data; // Resolve the loadout manager var loadout = RCEServices.Get<ILoadoutManager>(); loadout.SetActiveState(LoadoutState.HipFire); // Access GlobalFX for hit markers var globalFX = RCEServices.GlobalFX; globalFX?.PlayLayerByName("HitMarker", LayerPlayMode.FireAndForget); // Check if a service is available before using it if (RCEServices.Has<IColorPaletteStorage>()) { var palettes = RCEServices.Get<IColorPaletteStorage>(); }
RCEInstaller is the bootstrap component that registers all RCE engine services on startup. It runs automatically when the RCE prefab is loaded — you don't need to call anything manually.
| Interface | Purpose |
|---|---|
ILoadoutManager | Combat state management, active loadout access |
ILoadoutStorage | Loadout file persistence (JSON read/write) |
ITracerStorage | Tracer configuration persistence |
IColorPaletteStorage | Color palette persistence |
IThemeProvider | Editor UI theme resolution |
IDataPathProvider | File path configuration (overridable) |
If your project already uses a dependency injection framework like Zenject or VContainer, you can bypass RCEInstaller and bind RCE's interfaces directly in your container. This gives you full control over lifetimes, scoping, and constructor injection.
using Zenject; using RCE.Runtime.Bootstrap; using RCE.Runtime.Loadout; using RCE.Runtime.Palettes; public class GameInstaller : MonoInstaller { public override void InstallBindings() { // Override path provider with your own Container.Bind<IDataPathProvider>() .To<CustomDataPathProvider>() .AsSingle(); // Bind RCE engine services Container.Bind<ILoadoutManager>() .To<LoadoutManager>() .AsSingle(); Container.Bind<ILoadoutStorage>() .To<LoadoutStorage>() .AsSingle(); Container.Bind<IColorPaletteStorage>() .To<ColorPaletteStorage>() .AsSingle(); } }
using VContainer; using VContainer.Unity; using RCE.Runtime.Bootstrap; using RCE.Runtime.Loadout; public class GameLifetimeScope : LifetimeScope { protected override void Configure(IContainerBuilder builder) { builder.Register<IDataPathProvider, CustomDataPathProvider>(Lifetime.Singleton); builder.Register<ILoadoutManager, LoadoutManager>(Lifetime.Singleton); builder.Register<ILoadoutStorage, LoadoutStorage>(Lifetime.Singleton); } }
Loadouts are stored as plain JSON files with a versioned schema (currently v8). The LoadoutSerializer handles reading, writing, and automatic migration between schema versions — older loadout files are transparently upgraded when loaded.
ILoadoutStorage abstracts the file I/O layer. The default implementation writes JSON to the path provided by IDataPathProvider, but you can replace it to persist loadouts anywhere — a database, a REST API, or a cloud storage backend.
var storage = RCEServices.Get<ILoadoutStorage>(); // List all saved loadouts var names = storage.GetLoadoutNames(); // Load a specific loadout LoadoutData loadout = storage.Load("MyReticle"); // Save the current loadout storage.Save(loadout);
| Version | Changes |
|---|---|
| v1 | Initial schema — basic shapes, colors, transforms |
| v2 | Added transition settings (TransitionSettingsMap) |
| v5 | Custom shape points (customPoints, customPathClosed) |
| v6 | Boolean groups, palettes, full loadout serialization |
| v8 | Animation sequence system, unique entry IDs |
Because loadouts are plain JSON, they're inherently modding-friendly. Players and community tools can create, share, and import loadout files directly. RCE's automatic version migration ensures that loadouts created with older versions of the engine always load correctly in newer releases — no manual conversion needed.
Understanding the loadout data structure helps when building tools, writing importers, or debugging serialization issues. Here's the high-level hierarchy:
LoadoutData ├── name: string // Display name ├── version: int // Schema version (currently 8) ├── categories: CategoryData[5] // HUD, SA, HF, ADS, Global │ └── CategoryData │ └── layers: List<LayerData> │ └── LayerData │ ├── id: string // Unique layer GUID │ ├── name: string // Display name │ ├── shapeType: ShapeType // Circle, Ring, Polygon, etc. │ ├── position: Vector2 // UV-space position │ ├── rotation: float // Degrees │ ├── scale: Vector2 // X/Y scale │ ├── color: Color // Fill color + alpha │ ├── outlineColor: Color // Outline color + alpha │ ├── glowIntensity: float │ ├── glowSize: float │ ├── feather: float │ ├── animations: List<ShapeAnimationEntry> │ │ └── ShapeAnimationEntry │ │ ├── id: string // Unique entry GUID │ │ ├── property: AnimatableProperty │ │ ├── mode: AnimationMode │ │ └── ... speed, amplitude, frequency, etc. │ ├── customPoints: List<CustomControlPoint> │ └── customPathClosed: bool └── transitions: TransitionSettingsMap ├── hfToSa: TransitionPairSettings ├── saToHf: TransitionPairSettings ├── saToAds: TransitionPairSettings ├── adsToSa: TransitionPairSettings ├── hfToAds: TransitionPairSettings └── adsToHf: TransitionPairSettings ├── transitionType: TransitionType ├── duration: float ├── easingType: EasingType ├── enabled: bool ├── slideDirection: SlideDirection └── flashColor: Color
Each LayerData maps to one ShapeData struct (144 bytes) that gets uploaded to the GPU structured buffer. The C# struct and HLSL struct must match exactly — see the Architecture page for details on the binary layout.