Unit 1: Working in Unity 3D

CSE434 — Game Development In 3D 11 min read

I. Orientation: The Unity 3D Environment

Unity (first released 2005, Unity Technologies) is a component-based real-time engine in which a game is a Scene containing GameObjects, each of which is an empty container given behaviour and appearance by attached Components. Nothing in Unity is inherently a "player" or a "wall" — a GameObject becomes one by composition. Unity's 3D pipeline choices (Built-in, URP, HDRP) determine which lighting and shader tools are available; Shader Graph and most modern lighting workflow require URP or HDRP.

Defining conventions the rest of the unit relies on:

  • Left-handed coordinate system: +X right, +Y up, +Z forward. Unity's Vector3.forward is (0,0,1).
  • Units: 1 Unity unit = 1 metre by convention; physics gravity defaults to -9.81 m/s² on Y.
  • Transform component: every GameObject has exactly one — position, rotation (stored as a Quaternion, displayed as Euler degrees), and scale.
  • Hierarchy = parenting: a child's Transform values are local, expressed relative to its parent.
  • Component composition over inheritance: behaviour is added by attaching MonoBehaviour scripts, not by subclassing a game-object class.
  • Prefabs: serialised GameObject templates; editing the prefab asset propagates to all instances.
  • Frame loop: Awake → OnEnable → Start → (Update → LateUpdate) per frame → FixedUpdate on the physics tick (default 0.02 s).

II. Working in 3D Space

Transforms, spaces and navigation

A. Coordinate spaces and the Transform

The Transform is the only mandatory component because every renderable, collider and light must be positioned in space.

  • World vs local space: transform.position is absolute; transform.localPosition is relative to the parent. A child at local (0,1,0) under a parent at world (5,0,0) sits at world (5,1,0).
  • Conversion methods: TransformPoint() (local→world, applies scale and rotation), InverseTransformPoint() (world→local), TransformDirection() (ignores translation).
  • Rotation as Quaternion: stored as (x,y,z,w) to avoid gimbal lock. Never set .x directly; use Quaternion.Euler(0,90,0) or Quaternion.LookRotation(target - transform.position).
  • Scale caution: non-uniform scale on a parent (e.g. (1,3,1)) shears rotated children and breaks sphere colliders, which use only the largest axis.
CSHARP
// Move 5 m/s along the object's own forward axis, frame-rate independent
transform.Translate(Vector3.forward * 5f * Time.deltaTime, Space.Self);
// Smoothly face a target
Quaternion look = Quaternion.LookRotation(target.position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, look, 2f * Time.deltaTime);

B. Scene view navigation and object placement

  • Navigation: Right-mouse + WASD = flythrough; Alt+LMB = orbit; F = frame selected object.
  • Gizmo tools: W move, E rotate, R scale, T rect, Y transform; the Pivot/Center and Local/Global toggles decide which origin and axis set the handle uses.
  • Snapping: Ctrl-drag snaps to the grid increment set in Grid Settings; V activates vertex snap for seamless modular level pieces.
  • Meshes and colliders: the visual MeshRenderer and the physical Collider are independent — a 5 000-triangle rock should use a cheap Box or Mesh (convex) collider, not the render mesh.

C. Analytical dimension: performance of the spatial setup

  • Draw calls: each unique material/mesh pair is a draw call; static batching (tick Static on non-moving geometry) merges them.
  • Deep hierarchies cost: every transform.position write dirties children's matrices; keep moving objects shallow.

III. Adding Behaviors

Scripting components and physics

A. The MonoBehaviour lifecycle

A behaviour is a C# class inheriting MonoBehaviour, attached to a GameObject, whose callbacks Unity invokes automatically.

  • Awake(): once, on load, even if disabled — cache references here.
  • Start(): once, before the first Update, only if enabled — use for logic depending on other objects' Awake.
  • Update(): once per rendered frame; use Time.deltaTime (seconds since last frame) for all rate-based maths.
  • FixedUpdate(): on the fixed physics tick (default 50 Hz); all Rigidbody force application belongs here.
  • LateUpdate(): after all Update calls — the classic place for camera follow so it reads final player positions.
  • Serialization: public or [SerializeField] private fields appear in the Inspector and their Inspector value overrides the code initialiser.

B. Physics-driven behaviour

  • Rigidbody: grants mass (kg), drag and gravity; an object with a Rigidbody must be moved by AddForce or MovePosition, not by writing transform.position.
  • Force modes: ForceMode.Force (continuous, mass-dependent), ForceMode.Impulse (instantaneous, e.g. a jump).
  • Collision vs trigger: OnCollisionEnter(Collision c) fires for solid contact and returns contact points; OnTriggerEnter(Collider c) fires for an Is Trigger collider that passes through — used for checkpoints and pickups.
  • Requirement: at least one of the two colliding bodies needs a non-kinematic Rigidbody or no message is sent.
CSHARP
[RequireComponent(typeof(Rigidbody))]
public class Jumper : MonoBehaviour {
    [SerializeField] float jumpImpulse = 6f;   // newton-seconds
    Rigidbody rb;
    void Awake()  => rb = GetComponent<Rigidbody>();
    void Update() { if (Input.GetButtonDown("Jump")) rb.AddForce(Vector3.up * jumpImpulse, ForceMode.Impulse); }
}

C. Communication between behaviours

  • Direct references: GetComponent<Health>() — fast, but cache it; calling it per frame allocates lookup cost.
  • Events: UnityEvent fields or C# event Action decouple sender from receiver (a door listens to a switch without knowing about it).
  • Coroutines: StartCoroutine(Fade()) with yield return new WaitForSeconds(2f) spreads behaviour over time without blocking the frame.

IV. Working in Unity Particles

Visual effects and their supporting scene systems

A. The Particle System (Shuriken)

Particles are camera-facing quads spawned, animated and killed by a single component, used for fire, smoke, sparks and dust.

  • Main module: Duration, Looping, Start Lifetime (s), Start Speed (units/s), Start Size, Simulation Space — set to World so a torch's smoke trails behind a moving player instead of sticking to it.
  • Emission: Rate over Time (particles/s) plus Bursts (e.g. 50 particles at t = 0 for an explosion).
  • Shape: Cone (with angle and radius), Sphere, Box, Mesh — determines initial position and velocity direction.
  • Over-lifetime modules: Color over Lifetime (gradient with alpha to 0 for fade-out), Size over Lifetime (curve), Velocity/Force over Lifetime (a +0.5 Y force lifts smoke).
  • Renderer module: Render Mode Billboard / Stretched Billboard (sparks) / Mesh (debris); Sorting Fudge resolves depth-sort fighting.

B. Materials

  • Definition: a Material is an instance of a Shader with concrete property values; the particle Renderer requires one.
  • Particle-appropriate settings: URP Particles Unlit shader with Additive blending for fire and energy (colours sum, never darkens), Alpha blending for smoke.
  • Key maps: Base Map (RGBA texture, alpha = particle silhouette), Emission colour with HDR intensity, e.g. intensity 2.0 to trigger bloom.
  • Soft Particles: fades the quad where it intersects opaque geometry, removing the hard cut line; requires a depth texture in the pipeline asset.

C. Lightening

  • Light types: Directional (sun; only rotation matters, infinite range), Point (bulb, range in metres, inverse-square falloff), Spot (cone with spot angle), Area (baked only).
  • Real-time vs baked: Realtime recomputes per frame and casts dynamic shadows; Baked burns light into lightmap textures at zero runtime cost but is static; Mixed gives baked static light plus real-time shadows for moving objects.
  • Global Illumination: enable Lightmapping in the Lighting window; Light Probes feed baked bounce light to dynamic objects, and a Reflection Probe supplies local cubemap reflections.
  • Particles and light: particles are unlit by default; add a Lights module to spawn actual point lights on a fraction (e.g. Ratio 0.1) of particles so an explosion illuminates the wall.
  • Environment lighting: the Skybox material acts as ambient source; Intensity Multiplier scales it.

D. Audio

  • Two halves: exactly one Audio Listener (normally on the Main Camera) receives, and many Audio Sources emit.
  • Spatial Blend: 0 = 2D (music, UI, constant volume); 1 = 3D (positional, attenuates with distance) — the single most important particle-effect audio setting.
  • Rolloff: Logarithmic (physically plausible) or Custom curve, bounded by Min Distance (full volume inside) and Max Distance (silent beyond).
  • One-shots: AudioSource.PlayClipAtPoint(clip, transform.position) spawns a temporary source — ideal for an explosion whose emitter is destroyed.
  • Audio Mixer: route sources to groups (Music/SFX) for grouped volume in dB and effects such as reverb or low-pass.

E. Camera Positioning

  • Projection: Perspective with Field of View in degrees (60 typical; 90 for FPS) or Orthographic with Size = half the view height in units.
  • Clipping planes: Near 0.3, Far 1000; a very small near plane relative to far causes z-fighting through depth-buffer precision loss.
  • Placement patterns: third-person orbit (a pivot at shoulder height, camera offset (0, 2, -5)), first-person (camera childed to the head), fixed cinematic angle.
  • Clear Flags and Depth: a weapon camera with Clear Flags: Depth only and a higher Depth renders on top of the world camera without clipping into walls.
  • Smoothing: never hard-set the position in Update; use Vector3.SmoothDamp in LateUpdate.

V. Shader Graph-based Lighting

Node-based authoring of surface response

A. Purpose and structure

Shader Graph (URP/HDRP) replaces hand-written HLSL with a visual node graph compiled to a shader asset, so lighting behaviour becomes an editable data flow.

  • Master Stack: the graph terminates in two contexts — Vertex (Position, Normal, Tangent) and Fragment (Base Color, Metallic, Smoothness, Normal, Emission, Alpha).
  • Choice of target decides lighting: a Lit target feeds the PBR pipeline and receives scene lights, shadows and reflections automatically; an Unlit target ignores all lights and outputs colour directly.
  • Blackboard properties: exposed parameters (_BaseColor, _Speed) become Material Inspector fields, so one graph drives many materials.

B. Physically based lighting inputs

  • Metallic workflow: Metallic 0 = dielectric (wood, plastic), 1 = conductor (gold); intermediate values are only valid on transition texels.
  • Smoothness: 01 controls specular lobe width — 0.9 yields a mirror-like highlight; drives reflection probe mip selection.
  • Normal map: sample with Sample Texture 2D set to Normal type, feeding the Normal (Tangent Space) block; adds lighting detail without geometry.
  • Emission: an HDR colour ignored by shadowing; values above 1 bleed into Bloom post-processing and, with a Lit target, contribute to baked GI.

C. Custom lighting effects

  • Fresnel rim light: Fresnel Effect node (Power ≈ 3) → multiply by a rim colour → Emission gives an edge glow for shields and holograms.
  • Reading the light direction: on a Lit target, use the Main Light Direction / Baked GI nodes (or a Custom Function node in HLSL) to build stylised toon shading — a Dot Product(Normal, LightDir) fed through a Step(0.5) node yields hard cel bands.
  • Time-driven animation: Time → Sine → Lerp between two emission colours for a pulsing power core.
  • Cost awareness: every node becomes instructions per pixel; Sample Texture 2D in a loop or a high Power chain multiplies fragment cost across full-screen coverage.

VI. Cinemachine for Camera Control

Procedural, designer-driven cameras

A. Architecture

Cinemachine (Unity package) leaves a single Unity Camera in the scene and drives it procedurally from lightweight Virtual Cameras.

  • CinemachineBrain: the component added to the Main Camera; each frame it finds the enabled Virtual Camera with the highest Priority and copies its computed state into the real Camera.
  • Virtual Camera (vcam): holds no rendering — only a Follow target (position) and a Look At target (aim), plus body and aim algorithms.
  • Blending: switching Priority triggers the Brain's Default Blend (e.g. Ease In Out, 2 s); per-pair overrides live in the Custom Blends asset.

B. Body and Aim algorithms

  • Body – Transposer: maintains a fixed offset from Follow in a chosen binding mode (Lock To Target On Assign, World Space).
  • Body – Framing Transposer: 2D/2.5D; uses Dead Zone (no camera motion) and Soft Zone (damped correction) with independent X/Y/Z damping in seconds.
  • Body – Orbital Transposer / 3rd Person Follow: input-driven horizontal orbit; 3rd Person Follow adds Camera Collision Filter and shoulder offset.
  • Aim – Composer: keeps Look At inside a screen-space target region defined by Screen X/Y, Dead Zone Width/Height, Lookahead Time.
  • Damping: all values are approximate seconds to reach the target; 0 is instant and rigid, 2 is floaty.

C. Extensions and shot management

  • Collider extension: ray-casts from target to camera and pulls the camera in to avoid clipping through walls.
  • Confiner: restricts camera position to a bounding volume or 2D polygon.
  • Noise (Basic Multi Channel Perlin): applies a handheld profile such as 6D Shake, scaled by Amplitude Gain and Frequency Gain — the standard impact/explosion shake.
  • FreeLook camera: three stacked orbital rigs (top, middle, bottom) with a spline between them for third-person mouse-driven control.
  • Timeline integration: a Cinemachine Track cuts and blends between vcams on a timeline for cutscenes, keeping gameplay and cinematic cameras in one system.