4. Your First Script

The one pattern that unlocks the whole framework, plus copy-paste snippets for the five services you will use first.

4.1 The quick start

Create a new C# script, paste this in, drop it on any GameObject in any scene, and press Play:

using CommonGameSystem.Core;
using UnityEngine;

public class HelloCgs : MonoBehaviour
{
    private void Start()
    {
        var scheduler = ServiceLocator.Resolve<IScheduler>();
        var tween     = ServiceLocator.Resolve<ITweenService>();

        scheduler.After(1f, () => Debug.Log("One second after Play."));

        tween.To(1f, 2f, 0.5f,
            scale => transform.localScale = Vector3.one * scale,
            EaseType.OutBack);
    }
}

The object pops to double size with a springy overshoot, and a message appears one second later. Notice what is missing: no manager prefab, no initialization scene, no setup component. The bootstrap registered all 23 services before your Start ran.

4.2 How resolving services works

Every CGS service is reached the same way: ask the service locator for the interface you need.

var save = ServiceLocator.Resolve<ISaveService>();

Three rules keep this fast and safe:

  • Resolve early, cache the result. Services register before the first scene loads, so Awake and Start are both safe places to resolve. Store the reference in a field and reuse it — never call Resolve inside Update.
  • Missing services throw. Resolve<T>() throws a clear exception if nothing is registered for that interface. For a soft check, use ServiceLocator.TryResolve(out T service) or ServiceLocator.IsRegistered<T>().
  • Anything can be swapped. ServiceLocator.Replace<T>(instance) substitutes your own implementation, or a do-nothing null version, at any time (chapter 1, section 1.4).

The typical consumer looks like this:

public class GameHud : MonoBehaviour
{
    private ISaveService _save;   // cached once, used many times

    private void Awake()
    {
        _save = ServiceLocator.Resolve<ISaveService>();
    }
}

4.3 The five services you will use first

Each snippet below runs inside a MonoBehaviour, after the usings from section 4.1.

Saving and loading

Any serializable class can be a save file. Writes are atomic — a crash mid-save never corrupts the previous file. Full reference: Save/Load.

[System.Serializable]
public class PlayerData { public int Level; public float Health; }

var save = ServiceLocator.Resolve<ISaveService>();
save.Save("slot0", new PlayerData { Level = 3, Health = 75f });

var result = save.Load<PlayerData>("slot0");
if (result.Status == SaveStatus.Ok)
    Debug.Log("Loaded level " + result.Value.Level);

Events

Any class can be an event. Publishers and subscribers never reference each other — only the event type. Full reference: Event Bus.

public sealed class CoinCollected { public int Amount; }

var events = ServiceLocator.Resolve<IEventBus>();
System.IDisposable ticket = events.Subscribe<CoinCollected>(
    e => Debug.Log("Coins gained: " + e.Amount));

events.Publish(new CoinCollected { Amount = 5 });
ticket.Dispose();   // stop listening (do this in OnDestroy)

Timers

Timers fire in the order they were scheduled. One-shot timers clean up after themselves; repeating timers run until you dispose them. Full reference: Scheduler.

var scheduler = ServiceLocator.Resolve<IScheduler>();

scheduler.After(2f, () => Debug.Log("Two seconds later, exactly once."));

System.IDisposable heartbeat =
    scheduler.Every(0.5f, () => Debug.Log("Every half second."));
heartbeat.Dispose();   // stop the repeating timer when done

Tweening

A tween animates a value from one number to another and hands you each step. By default tweens run on the gameplay clock, so they pause when your game pauses. Full reference: Tween.

var tween = ServiceLocator.Resolve<ITweenService>();
CanvasGroup group = GetComponent<CanvasGroup>();

tween.To(1f, 0f, 0.75f,
    alpha => group.alpha = alpha,
    EaseType.OutQuad,
    onComplete: () => Debug.Log("Fade finished."));

Input

Input is read through named action maps from your input actions asset. Replace "Gameplay" and "Jump" with names from your own asset. Add using UnityEngine.InputSystem; for the InputAction type. Full reference: Input.

private IInputService _input;
private InputAction _jump;

private void Start()
{
    _input = ServiceLocator.Resolve<IInputService>();
    _jump  = _input.GetAction("Gameplay", "Jump");
}

private void Update()
{
    if (_jump != null && _input.WasPressedThisFrame(_jump))
        Debug.Log("Jump!");
}

4.4 Where to go next

You now know the one pattern that unlocks the entire framework: resolve an interface, cache it, call it. From here:

  • Service reference. Every service has its own page — start at the documentation index, or in your project under Assets/CommonGameSystem.Core/Documentation/Modules/ (for example SaveLoad.md or Tween.md). Each page covers the full API, common patterns, and the caveats that matter.
  • Samples. The RPG Starter Sample (chapter 3) shows several of the services above cooperating in one small game slice.
  • The rest of this manual. Chapter 5 explains the five core concepts; chapter 6 connects the framework to your own assets.
  • Changelog. The Welcome window's quick links (chapter 3) open the documentation and the changelog for your installed version.

Next: 5. Core Concepts