CSE434Semester 7 • 4 CreditsElective Subjects

Game Development In 3D

This course covers 3D game development in Unity, including 3D modelling, procedural environments, player controls, AI-driven behaviours, advanced scripting, lighting, user interfaces, immersive experiences, storytelling, multiplayer development and optimization.

6 Units • Notes • Model Answers • MCQs
Select Course Unit (6 Units)
Active: Unit 1
Unit 1:Unit 1: Working in Unity 3D
7 min read•High-Yield Unit Guide

Unit 1: Working in Unity 3D

Unit 1 Exam Focus & Core Takeaways

Review the core definitions, standard architectures, and syntax patterns below. Pay special attention to sections highlighted with bullet points for your mid-term and end-term subjective questions.

Unit 1: Working in Unity 3D

1. The Unity 3D Engine Architecture and Workspace

Unity is an industry-standard, multi-platform real-time 3D game engine and development ecosystem. At its core, Unity employs a component-based software architecture built on top of a high-performance native C++ engine core, with user code executing via a managed C# scripting runtime (Mono or CoreCLR/.NET).

1.1 The Entity-Component Model: GameObjects and Components

In traditional object-oriented game development, complex inheritance hierarchies (e.g., Pawn -> Character -> PlayerCharacter -> Warrior) lead to fragile, tightly coupled architectures. Unity replaces deep inheritance with the Entity-Component Model:

  • GameObject (Entity): The fundamental container object in a Unity scene. A GameObject has no inherent visual representation or behavior of its own; it acts purely as an empty entity holding components.
  • Component: Modular behavior or data attached to a GameObject. Every single characteristic—rendering a 3D mesh, simulating physical gravity, producing audio, or executing game logic—is encapsulated inside a distinct component.
  • The Invariant Rule: Every GameObject in Unity must possess exactly one Transform component. The Transform cannot be removed or destroyed because it establishes the entity's position, rotation, and scale in 3D space.
CODE
       +--------------------------------------------------------------+
       |                         GameObject                           |
       |  (e.g., "MainCharacter" - Active, Tag: "Player", Layer: 8)   |
       +--------------------------------------------------------------+
                                      |
         +----------------------------+----------------------------+
         |                            |                            |
         v                            v                            v
+------------------+         +------------------+         +------------------+
|    Transform     |         |    Rigidbody     |         | PlayerController |
| - Position       |         | - Mass: 75.0 kg  |         | (MonoBehaviour)  |
| - Rotation       |         | - Use Gravity    |         | - Move Speed     |
| - Scale          |         | - Interpolation  |         | - Jump Height    |
+------------------+         +------------------+         +------------------+

1.2 Core Editor Workspace Panels

The Unity Editor interface organizes game production into five primary synchronized views:

  1. Scene View: The interactive 3D authoring environment where level designers place, manipulate, and visually inspect scene geometry, lights, cameras, and collision volumes.
  2. Game View: The real-time preview showing what the rendered active cameras see during gameplay. Controlled by Play, Pause, and Step buttons.
  3. Hierarchy Window: A real-time tree structure displaying every active and disabled GameObject in the current scene. Visualizes parent-child relationships.
  4. Inspector Window: Displays all properties, materials, and serialized fields of the currently selected GameObject or asset, allowing real-time parameter tweaking.
  5. Project Window: The visual browser representing the physical Assets/ directory on disk. Contains textures, 3D meshes, audio clips, prefabs, and C# scripts.

2. Spatial Mathematics and Coordinate Systems in 3D Space

Understanding 3D mathematics—coordinate axes, matrix transformations, and rotation systems—is essential for positioning characters, aiming weapons, and orienting cameras.

2.1 The Left-Handed Coordinate System

Unity utilizes a Left-Handed Coordinate System with YY as the vertical axis:

  • +X+X Axis (Red): Points horizontally to the Right.
  • +Y+Y Axis (Green): Points vertically Up.
  • +Z+Z Axis (Blue): Points horizontally Forward (into the screen).
CODE
         +Y (Up)
          |
          |   +Z (Forward / Depth)
          |  /
          | /
          +------------ +X (Right)

The Left-Hand Rule: Extend your left hand. Point your index finger forward (+Z+Z), point your thumb upward (+Y+Y), and point your middle finger to the right (+X+X).

2.2 Coordinate Spaces and Transformations

In a 3D game engine, spatial positions exist relative to different reference frames:

  1. World Space: The global coordinate frame where the center of the virtual universe is at (0,0,0)(0, 0, 0).
  2. Local Space (Object Space): Coordinates relative to an object's direct parent. If a car is at world position (10,0,0)(10, 0, 0) and its steering wheel has local position (0,1,0.5)(0, 1, 0.5), the steering wheel's world position is (10,1,0.5)(10, 1, 0.5).
  3. View Space (Camera Space): Coordinates evaluated relative to the active camera lens, where +Z+Z points down the camera's optical view frustum.
  4. Screen Space: Two-dimensional pixel coordinates (X,YX, Y) measured from the bottom-left corner of the display (0,0)(0, 0) to the top-right (W,H)(W, H).

Transform Coordinate Conversion Methods

The Transform component exposes methods to transform vectors between spaces:

csharp
// Convert a point from Local Space to World Space
Vector3 worldPos = transform.TransformPoint(new Vector3(0, 0, 2)); // 2 units in front

// Convert a point from World Space to Local Space
Vector3 localPos = transform.InverseTransformPoint(targetEnemy.position);

// Transform directions (ignores position translation, applies only rotation and scale)
Vector3 worldDirection = transform.TransformDirection(Vector3.forward);
Vector3 localDirection = transform.InverseTransformDirection(windVelocity);

2.3 Rotations: Euler Angles vs. Quaternions

Rotations in 3D can be expressed in two primary mathematical representations:

Dimensional TraitEuler AnglesQuaternions (UnityEngine.Quaternion)
Mathematical StructureThree scalar angles (Pitch X,Yaw Y,Roll Z)(\text{Pitch } X, \text{Yaw } Y, \text{Roll } Z) in degrees.Four-dimensional hyper-complex number (x,y,z,w)(x, y, z, w) representing an axis and an angle.
Human ReadabilityIntuitive; visible and editable directly in the Inspector.Non-intuitive; numbers have no obvious direct physical meaning.
Gimbal Lock HazardSevere Hazard: Occurs when two rotational axes align, losing one degree of rotational freedom.Immune: Quaternions never suffer from Gimbal Lock.
Interpolation QualityUneven; linear interpolation produces jerky, non-spherical arcs.Smooth: Supports Spherical Linear Interpolation (Quaternion.Slerp).

Development Rule: Never modify transform.rotation.x, y, z directly! Always use Quaternion.Euler(pitch, yaw, roll), Quaternion.LookRotation(forward), or Quaternion.Slerp() to manipulate orientations safely.


3. The MonoBehaviour Lifecycle and Execution Pipeline

Every custom C# game script attached to a GameObject must inherit from UnityEngine.MonoBehaviour. Unity executes lifecycle callback methods in a strict, deterministic sequence:

CODE
                  [ Script Instantiated / Scene Loaded ]
                                    |
                                    v
                                Awake()
                                    |
                                    v
                               OnEnable()
                                    |
                                    v
                                 Start()
                                    |
        +---------------------------+---------------------------+
        |                                                       |
        v (Fixed Timestep: 50Hz)                                v (Per-Frame Render Loop)
   FixedUpdate()                                             Update()
        |                                                       |
   Internal Physics Simulation                                  v
        |                                                  LateUpdate()
   OnCollision / OnTrigger Callbacks                            |
        |                                                       v
        +---------------------------+---------------------------+
                                    |
                                    v
                           [ OnApplicationQuit / Destroyed ]
                                    |
                               OnDisable()
                                    |
                               OnDestroy()

3.1 Initialization Phase

  • Awake(): Invoked immediately when the GameObject is instantiated, even if the script component is disabled (enabled = false). Used to establish self-contained references (GetComponent<Rigidbody>()).
  • OnEnable(): Invoked whenever the component or its host GameObject transitions from inactive to active. Ideal for subscribing to C# events or delegates.
  • Start(): Invoked on the frame before any Update() method runs, provided the script component is enabled. Used to establish dependencies across other external objects.

3.2 The Game and Physics Loops

  • FixedUpdate(): Executed on a rigid, deterministic physical time step (defaulting to 0.02 seconds / 50 Hz via Time.fixedDeltaTime), completely decoupled from the rendering frame rate. All physics calculations and Rigidbody.AddForce() calls must reside here.
  • Update(): Executed once per rendered frame. Frame time varies depending on GPU/CPU load (Time.deltaTime). Used for reading player inputs, moving non-physical objects, and timer countdowns.
  • LateUpdate(): Executed once per frame immediately after all Update() methods across all scene scripts have completed. Ideal for camera-following scripts to guarantee that target character movement for the current frame has finished.

4. Physics-Driven Behaviors: Rigidbodies and Colliders

Physical interactions in Unity are simulated using the PhysX 3D engine.

4.1 The Rigidbody Component

Attaching a Rigidbody transfers dynamic control of a GameObject to the physics engine:

  • mass: Inertial mass in arbitrary units (typically kilograms).
  • drag & angularDrag: Air resistance damping linear and rotational velocities.
  • useGravity: Whether the global gravity vector (−9.81 m/s2-9.81\text{ m/s}^2 on YY) acts upon the body.
  • isKinematic: When true, the object is exempt from forces, gravity, and physical collisions; it can only be moved explicitly via script transform or animations.
  • collisionDetection:
    • Discrete (Default): Evaluates collisions at fixed intervals; high-speed objects may tunnel through thin walls (Physics Tunneling).
    • Continuous / ContinuousDynamic: Uses predictive swept-volume collision detection to prevent fast-moving projectiles from tunneling.

Applying Physical Forces: ForceMode Taxonomy

Forces are applied inside FixedUpdate() via rb.AddForce(direction, ForceMode):

csharp
// 1. Force: Continuous force respecting mass (e.g., rocket thruster)
rb.AddForce(transform.forward * 50f, ForceMode.Force);

// 2. Impulse: Instantaneous force respecting mass (e.g., explosive blast, jump)
rb.AddForce(Vector3.up * 8f, ForceMode.Impulse);

// 3. VelocityChange: Instantaneous velocity modification ignoring mass
rb.AddForce(dashDirection * 15f, ForceMode.VelocityChange);

// 4. Acceleration: Continuous acceleration ignoring mass (e.g., gravity booster)
rb.AddForce(Vector3.up * 9.81f, ForceMode.Acceleration);

4.2 Colliders and Triggers

Colliders define the physical boundary geometry of a GameObject for collision detection:

  • Primitive Colliders: BoxCollider, SphereCollider, CapsuleCollider. Computationally fast and memory efficient.
  • MeshCollider: Wraps the exact polygonal surface of a 3D model. To collide with dynamic moving objects, a MeshCollider must be flagged as Convex (limited to 255 triangles).

Hard Physical Collisions vs. Non-Physical Triggers

  • Physical Collision (isTrigger = false): The physics solver computes bounce forces, friction, and penetration depenetration. At least one object must have a non-kinematic Rigidbody.
    • Callbacks: OnCollisionEnter(Collision col), OnCollisionStay(Collision col), OnCollisionExit(Collision col).
    • Exposes contact points (col.contacts[0].point) and collision impulse.
  • Trigger Volume (isTrigger = true): The collider ignores physical deflection; objects pass through freely while generating spatial intersection events.
    • Callbacks: OnTriggerEnter(Collider other), OnTriggerStay(Collider other), OnTriggerExit(Collider other).
    • Ideal for checkpoint zones, collectible coins, proximity triggers, and damage fields.

5. Visual Effects and Particle Systems (Shuriken)

The Shuriken Particle System simulates complex visual phenomena (fire, smoke, explosions, rain, magical spells) by generating, moving, and rendering thousands of small 2D/3D sprites or meshes.

5.1 Modular Architecture of the Particle System

A Particle System is configured via modular feature blocks:

  1. Main Module: Controls foundational properties:
    • Duration: Lifespan of the emitter cycle.
    • Looping: Whether the system restarts automatically upon finishing.
    • Start Lifetime, Start Speed, Start Size, Start Color.
    • Gravity Modifier: Scales the influence of world gravity on particles.
    • Simulation Space: Local (particles move with the parent emitter) vs. World (particles remain in world space once emitted, creating realistic smoke trails).
  2. Emission Module: Governs the rate of particle generation over time (Rate over Time, Rate over Distance) and configures instantaneous bursts (Bursts).
  3. Shape Module: Defines the geometric volume from which particles spawn (Cone, Sphere, Box, Mesh).
  4. Velocity / Force over Lifetime: Modifies velocity curves over the lifespan of individual particles.
  5. Color / Size over Lifetime: Gradually fades alpha or scales particles as they age.
  6. Renderer Module: Controls how particles are drawn (Billboard facing the camera, Stretched Billboard, Mesh, or Trail).

6. Lighting, Materials, and Physically Based Rendering (PBR)

Visual realism in modern 3D games is achieved through Physically Based Rendering (PBR), which simulates real-world optical behavior.

6.1 Light Sources in Unity

  1. Directional Light: Simulates an infinitely distant light source (the Sun). Rays are completely parallel across the entire scene; position in the scene is irrelevant, only rotation matters.
  2. Point Light: Emits light omnidirectionally in all directions from a single 3D point in space (like a lightbulb or campfire), attenuating over a defined Range.
  3. Spot Light: Emits light in a directional cone from a specific point, constrained by Range and Spot Angle (like a flashlight or car headlight).
  4. Area Light: Emits light from a rectangular or disc surface. Because calculating area lighting in real-time is computationally intense, Area Lights are Baked only (precalculated into lightmaps).

6.2 Standard PBR Material Channels

A Unity Material acts as the container binding a Shader to specific textures and optical properties:

  • Albedo: The base diffuse color or texture of the surface, free from any baked specular highlights or directional shadows.
  • Metallic: Governs whether the surface behaves as a dielectric insulator (0.00.0, e.g., wood, plastic, skin) or a reflective metal (1.01.0, e.g., gold, iron).
  • Smoothness / Roughness: Controls micro-surface irregularities. High smoothness produces sharp, mirror-like specular highlights; low smoothness scatters light diffusely.
  • Normal Map: A tangent-space vector texture encoding surface height details into RGB color vectors (R=X,G=Y,B=ZR = X, G = Y, B = Z), creating the illusion of intricate surface geometry on low-polygon models without increasing triangle counts.
  • Occlusion: A grayscale map darkening ambient light in cracks, crevices, and seams.
  • Emission: Radiates light color and intensity outward, participating in real-time bloom and baked global illumination.

7. Audio Systems in 3D Space

Realistic 3D audio requires three foundational elements:

  1. AudioClip: The audio asset file stored on disk (WAV, MP3, OGG).
  2. AudioSource: The audio emitter (speaker) placed on a GameObject.
  3. AudioListener: The microphone (ears) that receives sound waves in the 3D world. Every scene must have exactly one active AudioListener, conventionally attached to the Main Camera.

7.1 Spatial Audio and 3D Attenuation

The AudioSource component exposes a Spatial Blend slider:

  • 0.0 (2D Sound): Played identically across both headphone speakers without attenuation (ideal for background music and UI button clicks).
  • 1.0 (3D Spatial Sound): Sound volume and stereo panning are calculated dynamically based on the 3D distance and angle between the AudioSource and the AudioListener.
  • Rolloff Curves: Configurable logarithmic or linear volume decay over distance (Min Distance, Max Distance).

8. Procedural Camera Control with Cinemachine

Traditional camera positioning scripts using simple transform.position = target.position + offset suffer from jarring camera jumps, occluded targets, and visual stutter. Cinemachine provides a procedural camera suite that acts like a Hollywood film crew.

8.1 The Cinemachine Architecture

  1. CinemachineBrain: Attached to the primary Unity Camera. It monitors all virtual cameras in the scene, handles priority blending, and applies the calculated transform to the physical camera lens.
  2. CinemachineVirtualCamera (vcam): Lightweight GameObjects placed throughout the scene acting as camera directives. They do not render anything themselves; they feed position and rotation targets to the Brain.
  3. Priority and Blending: Each vcam has a Priority integer. When a vcam's priority exceeds others, the CinemachineBrain automatically executes a smooth interpolation blend (e.g., Ease In/Out over 2 seconds) between the old camera angle and the new one.

8.2 Body and Aim Algorithms

  • Body (How the camera positions itself relative to the target):
    • Transposer: Maintains a fixed directional offset relative to the target in world or local space.
    • Framing Transposer: Keeps the target framed within screen boundaries using configurable soft zones and dead zones (standard for 2D and 3rd-person games).
    • Orbital Transposer: Rotates around the target in response to player mouse/controller input.
    • Tracked Dolly: Constrains the camera to move along a pre-authored spline path.
  • Aim (How the camera rotates to look at the target):
    • Composer: Rotates the camera to keep the LookAt target within a defined screen area, applying damping to eliminate robotic motion.
    • Hard Look At: Points the camera directly at the target with zero damping.

9. Complete Architectural Script: Physics-Driven 3D Player Character

Below is a production-grade, modular C# script demonstrating the concepts covered in this unit:

  • Component-based architecture with [RequireComponent].
  • Input processing in Update().
  • Physics movement and forces in FixedUpdate().
  • Ground check via sphere cast / trigger checks.
  • Particle effect triggering and 3D spatial audio invocation.
csharp
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class PlayerCharacterController : MonoBehaviour
{
    [Header("Locomotion Parameters")]
    [SerializeField] private float moveSpeed = 8f;
    [SerializeField] private float rotationSpeed = 12f;
    [SerializeField] private float jumpImpulse = 7.5f;

    [Header("Ground Detection")]
    [SerializeField] private Transform groundCheckAnchor;
    [SerializeField] private float groundCheckRadius = 0.25f;
    [SerializeField] private LayerMask groundLayerMask;

    [Header("Sensory Feedback")]
    [SerializeField] private ParticleSystem jumpDustParticles;
    [SerializeField] private AudioSource audioSource;
    [SerializeField] private AudioClip jumpSfxClip;

    private Rigidbody rb;
    private Vector2 inputVector;
    private bool jumpRequested;
    private bool isGrounded;

    private void Awake()
    {
        // Cache component references immediately upon instantiation
        rb = GetComponent<Rigidbody>();
        
        // Configure Rigidbody constraints for a responsive character
        rb.freezeRotation = true; // Prevents physics forces from tipping the character over
        rb.interpolation = RigidbodyInterpolation.Interpolate; // Smooth rendering across frames
        rb.collisionDetectionMode = CollisionDetectionMode.Continuous; // Prevents floor tunneling
    }

    private void Update()
    {
        // 1. Gather player inputs on the render frame loop
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        inputVector = new Vector2(h, v).normalized;

        // 2. Capture jump trigger
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            jumpRequested = true;
        }

        // 3. Evaluate ground contact
        if (groundCheckAnchor != null)
        {
            isGrounded = Physics.CheckSphere(groundCheckAnchor.position, groundCheckRadius, groundLayerMask);
        }
    }

    private void FixedUpdate()
    {
        // Execute physical movement strictly inside FixedUpdate
        ApplyLocomotion();
        ApplyJumpPhysics();
    }

    private void ApplyLocomotion()
    {
        Vector3 moveDirection = new Vector3(inputVector.x, 0f, inputVector.y);

        if (moveDirection.sqrMagnitude > 0.001f)
        {
            // Calculate target world movement velocity while preserving vertical physics velocity
            Vector3 targetVelocity = moveDirection * moveSpeed;
            targetVelocity.y = rb.linearVelocity.y; // Unity 6 / modern Rigidbody API
            rb.linearVelocity = targetVelocity;

            // Rotate smoothly towards movement direction using Slerp
            Quaternion targetRotation = Quaternion.LookRotation(moveDirection, Vector3.up);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.fixedDeltaTime);
        }
        else
        {
            // Decelerate horizontal movement immediately when input ceases
            rb.linearVelocity = new Vector3(0f, rb.linearVelocity.y, 0f);
        }
    }

    private void ApplyJumpPhysics()
    {
        if (jumpRequested)
        {
            // Apply upward velocity impulse
            rb.AddForce(Vector3.up * jumpImpulse, ForceMode.Impulse);
            jumpRequested = false;

            // Trigger visual particle effect
            if (jumpDustParticles != null)
            {
                jumpDustParticles.Play();
            }

            // Play 3D spatial jump audio
            if (audioSource != null && jumpSfxClip != null)
            {
                audioSource.PlayOneShot(jumpSfxClip);
            }
        }
    }

    private void OnDrawGizmosSelected()
    {
        // Draw debug wire sphere in Scene view for visual tuning of ground detection
        if (groundCheckAnchor != null)
        {
            Gizmos.color = isGrounded ? Color.green : Color.red;
            Gizmos.DrawWireSphere(groundCheckAnchor.position, groundCheckRadius);
        }
    }
}