unity-game-agent / unity_agent /knowledge /entries /unity_fundamentals.md
ryzerrr's picture
Upload folder using huggingface_hub
40c0886 verified
|
Raw
History Blame Contribute Delete
3.94 kB

Unity Fundamentals

MonoBehaviour Lifecycle

Every gameplay script in Unity derives from MonoBehaviour. The engine calls a fixed sequence of messages; understanding the order is the difference between code that runs smoothly and code that stutters or races.

Awake()        -> OnEnable() -> Start()
                                |
                                v
FixedUpdate()  (physics, fixed timestep) --+
                                           |
Update()        (input, gameplay)        --+
                                           |
LateUpdate()    (camera follow, post-sim) --+
                                           |
OnDisable() -> OnDestroy()
Message When it runs Typical use
Awake Once, when the script instance is loaded Cache components, set up singletons
OnEnable Every time the component is enabled Subscribe to events
Start Once, before the first Update Cross-object references (other objects' Awake have run)
FixedUpdate Fixed timestep (default 0.02 s = 50 Hz) Rigidbody physics forces
Update Every rendered frame Input polling, gameplay logic
LateUpdate After all Update calls have finished Camera follow, post-processing
OnDisable When the component is disabled Unsubscribe from events
OnDestroy When the GameObject is destroyed Cleanup native resources

Gotcha: Camera.main searches the scene by tag every call in 2022.3. Cache it in Awake if you call it more than once per frame.

GameObjects and Components

A GameObject is an empty container. Behaviour is added through components:

var go = new GameObject("Enemy");
go.transform.position = Vector3.zero;
go.AddComponent<Rigidbody>();
go.AddComponent<CapsuleCollider>();
var ai = go.AddComponent<NPCController>();
ai.player = GameObject.FindGameObjectWithTag("Player").transform;
  • Every GameObject always has a Transform (or RectTransform under a Canvas). You cannot remove it.
  • Use GetComponent<T>() / GetComponentInChildren<T>() / GetComponentInParent<T>() to reach related components. Cache the result in Awake instead of calling it every frame.
  • RequireComponent(typeof(T)) makes Unity auto-add the required component when the script is added in the inspector.

The Component pattern in practice

[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
    private Rigidbody _rb;

    private void Awake()
    {
        _rb = GetComponent<Rigidbody>(); // safe: RequireComponent guarantees it
    }
}

Coroutines

Coroutines let you write asynchronous-looking code that runs across multiple frames:

private IEnumerator RespawnAfterDelay(float seconds)
{
    yield return new WaitForSeconds(seconds);
    transform.position = _spawnPoint;
}

private void OnDeath() => StartCoroutine(RespawnAfterDelay(2f));
  • yield return null; waits one frame.
  • yield return new WaitForFixedUpdate(); waits until the next physics step.
  • yield return new WaitUntil(() => _isGrounded); waits for a condition.

Inspector best practices

  • Use [Header], [Tooltip], [Range], [SerializeField] to keep the inspector tidy.
  • Prefer private + [SerializeField] over public for fields that should not be touched by other scripts but should be editable in the inspector.
  • Use [System.Serializable] on structs/classes to make them expandable in the inspector.