Use this file to discover all available pages before exploring further.
All gameplay logic in Prowl is written in C# (.NET 9) by extending MonoBehaviour. You attach a script class to a GameObject and Prowl calls the appropriate lifecycle methods at the right times — no configuration files, no registration boilerplate. If you have written scripts for Unity, the model will feel immediately familiar; Prowl shares the same lifecycle names and most of the same API surface.
In the editor, right-click in the Assets panel and choose Create → C# Script. Prowl creates a .cs file, a companion .meta file (handled by MonoScriptImporter), and opens the file in your configured editor.
2
Extend MonoBehaviour
Replace the generated stub with your logic. The class name must match the file name.
using Prowl.Runtime;public class PlayerController : MonoBehaviour{ public double moveSpeed = 5.0; public override void Update() { double h = (Input.GetKey(Key.D) ? 1 : 0) - (Input.GetKey(Key.A) ? 1 : 0); double v = (Input.GetKey(Key.W) ? 1 : 0) - (Input.GetKey(Key.S) ? 1 : 0); Transform.position += new Vector3(h, 0, v) * moveSpeed * Time.deltaTime; }}
3
Attach to a GameObject
Select a GameObject in the hierarchy, click Add Component in the Inspector, and find your script. Prowl calls AddComponent<PlayerController>() internally.
MonoBehaviour lifecycle methods are virtual — override only the ones you need. They are called by SceneManager.Update(), SceneManager.PhysicsUpdate(), and the render pipeline.
Initialisation
Per-frame update
Disable & destroy
Rendering callbacks
Method
When called
Awake()
Once when the component is first created (even if disabled). Runs before Start.
OnEnable()
Each time EnabledInHierarchy transitions from false → true.
Start()
Once, just before the first Update(), only if the component is enabled.
public override void Awake(){ // One-time setup — safe to run while disabled _rb = GetComponent<Rigidbody>();}public override void Start(){ // Runs after all Awake() calls — safe to reference other components _rb.Mass = 10f;}
Method
When called
FixedUpdate()
Fixed timestep (physics tick) — use for forces and physics queries.
Update()
Once per rendered frame.
LateUpdate()
After all Update() calls — good for camera following.
public override void FixedUpdate(){ _rb.AddForce(Vector3.up * 10f);}public override void LateUpdate(){ // Camera lag/smooth-follow logic here}
Method
When called
OnDisable()
Each time EnabledInHierarchy transitions from true → false.
OnDestroy()
Once when the component is destroyed, only if Start() was previously called.
From inside any MonoBehaviour you can reach the owning GameObject and all of its components:
// The attached GameObject and its TransformGameObject go = GameObject;Transform t = Transform; // shorthand for GameObject.Transform// Get another component on the same objectRigidbody rb = GetComponent<Rigidbody>();// Pattern-match style — no exception if missingif (TryGetComponent<AudioSource>(out var audio)) audio.Play();// Walk up the hierarchyCamera cam = GetComponentInParent<Camera>();// Walk down into childrenLight light = GetComponentInChildren<Light>(includeSelf: false);// Add a component at runtimevar emitter = AddComponent<ParticleEmitter>();// Remove this component from its GameObjectRemoveSelf();
All these methods are simply forwarded to the underlying GameObject. You can also call them on GameObject directly: GameObject.GetComponent<T>() returns the same result.
ScriptableObject is an EngineObject that lives purely as an asset on disk. It has no lifecycle beyond OnEnable() (called after deserialisation), making it ideal for configuration tables, item databases, and shared settings.
using Prowl.Runtime;public class EnemyData : ScriptableObject{ public string enemyName = "Goblin"; public int maxHealth = 100; public float moveSpeed = 3.5f; public AssetRef<Prefab> deathEffect; public override void OnEnable() { // Called once after the asset is loaded from disk Debug.Log($"EnemyData loaded: {enemyName}"); }}
Reference it from a MonoBehaviour just like any other asset:
public class EnemyController : MonoBehaviour{ public AssetRef<EnemyData> data; public override void Start() { EnemyData stats = data.Res; _health = stats.maxHealth; }}
Coroutines let you spread work across multiple frames using C# iterators.
public class Door : MonoBehaviour{ public override void Start() { StartCoroutine(nameof(OpenSequence)); } private IEnumerator OpenSequence() { // Yield for 2 seconds yield return new WaitForSeconds(2f); Debug.Log("Door opening..."); // Yield until end of frame yield return new WaitForEndOfFrame(); Debug.Log("Door fully open."); }}
Controls the order in which Update(), FixedUpdate(), and LateUpdate() are called relative to other components. Lower numbers execute first. Default is 0.
[ExecutionOrder(-50)] // runs before default-order scriptspublic class InputManager : MonoBehaviour { }[ExecutionOrder(100)] // runs after default-order scriptspublic class UISyncSystem : MonoBehaviour { }
Makes lifecycle methods run in edit mode (not only during play mode). Use together with null-guards — the full scene infrastructure may not be initialised in edit mode.
[ExecuteAlways]public class GridVisualiser : MonoBehaviour{ public override void DrawGizmos() { // Draws even while editing the scene Gizmos.DrawWireCube(Transform.position, Vector3.one); }}
When you edit a C# source file and save it, Prowl’s AssemblyManager recompiles and reloads the scripting assemblies without restarting the editor. The reload sequence is:
[OnAssemblyUnload] callbacks are invoked (clears component caches, reflection caches, etc.).
Assemblies are swapped.
AssemblyMethodAttributeBase.FindAll() scans all loaded assemblies for new attribute callbacks.
[OnAssemblyLoad] callbacks are invoked (re-populates lookup tables, re-discovers importers, etc.).
Static fields are reset on assembly reload. If you cache runtime data in a static field (e.g. a singleton instance), ensure you rebuild it inside an [OnAssemblyLoad] method or use ScriptableSingleton<T> which persists across reloads.
MonoBehaviour and GameObject both expose SendMessage and BroadcastMessage for loose coupling between components:
// Calls "TakeDamage" on every MonoBehaviour on this GameObjectGameObject.SendMessage("TakeDamage", 25);// Calls "TakeDamage" on this object AND all of its childrenBroadcastMessage("TakeDamage", 25);
Methods are resolved by name at runtime using reflection, so they work across assembly boundaries without shared interfaces.