Scripts and their lifecycle
Game code is C#. A script is a class derived from EntityScript, attached to an entity; the engine calls its methods as the entity is created, updated and destroyed.
One assembly for the whole game
A project's scripts are one C# project (Content/Scripts/<Name>Scripts.csproj), built by the editor with the engine's pinned .NET SDK. The same assembly runs in the editor's Play mode, in the packaged game and on the dedicated server. Edit with any code editor: Tools › Scripting › Open Project or Open Solution.
The editor rebuilds the scripts when you save (with Auto Build On Save) or on Build Scripts (Ctrl+Shift+B), and reloads them without restarting. Errors show in the Console with a link to the line.
The lifecycle
| Method | When |
|---|---|
OnCreate | The instance exists and its saved fields are set. |
OnEnable / OnDisable | The script attachment is switched on or off (Enabled). |
OnStart | Once, before the first update. |
OnFixedUpdate(fixedDeltaTime) | Every fixed step (60 per second by default), before physics. |
OnUpdate(deltaTime) | Every frame. |
OnLateUpdate(deltaTime) | Every frame, after every OnUpdate: cameras and followers. |
OnDestroy | The entity is destroyed or the session ends. |
Event callbacks arrive as things happen: OnCollisionEnter, OnCollisionStay, OnCollisionExit, OnTriggerEnter, OnTriggerExit, OnAnimationEvent and OnNetworkMessage.
LifecycleLogger.cs
public sealed class LifecycleLogger : EntityScript { protected override void OnCreate() => Debug.Log("created: the instance exists, fields are loaded"); protected override void OnEnable() => Debug.Log("enabled"); protected override void OnStart() => Debug.Log("start: once, before the first update"); protected override void OnFixedUpdate(float fixedDeltaTime) { // Every fixed step (60 per second by default), before physics. } protected override void OnUpdate(float deltaTime) { // Every frame. } protected override void OnLateUpdate(float deltaTime) { // Every frame, after all OnUpdate calls: cameras and followers. } protected override void OnDisable() => Debug.Log("disabled"); protected override void OnDestroy() => Debug.Log("destroyed: the entity or the play session ended"); }
Fields in the Inspector
Public fields of supported types, and private fields marked [SerializeField], appear in the Inspector under the script and are saved with the scene. [Range] makes a slider, [ReadOnly] shows without editing, [HideInInspector] hides a public field. Entity handles are run-time values and are never saved: look entities up in OnStart.
Turret.cs
public sealed class Turret : EntityScript { [Range(0.0f, 360.0f)] public float TurnSpeed = 90.0f; // a slider from 0 to 360 in the Inspector public string BarrelName = "Barrel"; [SerializeField] private int ammo = 30; // private, but saved and shown [ReadOnly] public int ShotsFired; // shown, not editable [HideInInspector] public float Heat; // public, but not shown private Entity barrel; // runtime handles are never saved protected override void OnStart() { barrel = Entity.FindChild(BarrelName, recursive: true); Debug.Log($"{Entity.Name}: {ammo} rounds, barrel {(barrel.IsValid ? "found" : "missing")}"); } }
Coroutines
StartCoroutine runs a method that can wait: yield return new WaitForSeconds(2), WaitForFixedUpdate, WaitForLateUpdate, or null for the next frame. Coroutines belong to the script and stop with it, or with StopCoroutine and StopAllCoroutines. The Coroutines page covers every wait, stopping and common mistakes.
TrafficLight.cs
public sealed class TrafficLight : EntityScript { public float GreenSeconds = 8.0f; public float RedSeconds = 6.0f; protected override void OnStart() => StartCoroutine(Cycle()); private IEnumerator Cycle() { while (true) { PointLight.SetColor(Entity, new Vector3(0.1f, 1.0f, 0.2f)); yield return new WaitForSeconds(GreenSeconds); PointLight.SetColor(Entity, new Vector3(1.0f, 0.1f, 0.1f)); yield return new WaitForSeconds(RedSeconds); } } }
Talking to the engine
KroysEngine 0.1.0 documentation, built from the engine repository by Tools/BuildDocsSite.py.