# Instructor Answer Key — Corrupted Scripts

Do not distribute this file to students. Each entry: what was changed, why it breaks the
game, and what a student needs to understand to catch and fix it. Difficulty is rated
🟢 easy (visible immediately in Play mode) / 🟡 medium (needs reading the logic) /
🔴 hard (subtle, requires understanding *why* the original line was written that way).

---

## 01_cam_orientation_CORRUPTED.cs — Mouse look (4 bugs)

1. 🟢 **`Cursor.lockState = CursorLockMode.Locked;` deleted from `Start()`.**
   The cursor stays visible and free — mouse look feels unresponsive because moving the
   mouse just moves the OS cursor off the Game window instead of rotating the camera.
   Fix: restore the line. Tests: understanding that `Cursor.lockState`/`Cursor.visible`
   are two separate settings that both matter for FPS controls.

2. 🟡 **`mouseX`/`mouseY` read the wrong axis of `mouseDelta`.**
   Original: `mouseX` comes from `mouseDelta.y` (vertical mouse movement → pitch),
   `mouseY` comes from `mouseDelta.x` (horizontal mouse movement → yaw).
   Corrupted version swaps them, so moving the mouse left/right tilts the camera up/down,
   and moving it up/down turns the camera left/right. Fix: swap `.x`/`.y` back.
   Tests: understanding that screen axes (X = horizontal, Y = vertical) don't
   automatically map to the "obvious" rotation axis — vertical mouse motion drives pitch
   (rotation around X), horizontal motion drives yaw (rotation around Y).

3. 🟡 **Clamp changed from `(-90f, 90f)` to `(-190f, 190f)`.**
   The camera can now rotate almost all the way around vertically — looking "up" eventually
   flips you upside-down looking backwards. Fix: restore `±90f`. Tests: understanding
   *why* the clamp exists (a first-person camera should never be able to loop past straight
   up/down).

4. 🔴 **`BodyOrientation.rotation` now applies `xRotation` (pitch) as well as `yRotation`.**
   Original only applies yaw (`Quaternion.Euler(0, yRotation, 0)`) to the body, keeping
   movement direction flat regardless of where the camera is looking. The corrupted
   version tilts the body with the camera's pitch, so looking up/down changes the
   direction WASD moves the player — e.g. looking straight up makes "forward" push the
   player backward or into the ground. Fix: set the first parameter back to `0`.
   Tests: the concept of separating *look direction* (camera, full 3 axes) from
   *movement direction* (body/orientation, yaw only) — this is the single most
   conceptually important bug in this file.

---

## 02_camera_follow_offset_CORRUPTED.cs — Camera merge/offset (2 bugs)

1. 🟢 **Code moved from `Update()` into `Start()`.**
   The camera snaps to the correct offset once when the scene starts, then never follows
   the player again — as soon as the Dabouza moves, the camera stays behind. Fix: move
   the assignment back into `Update()`. Tests: `Start()` runs once, `Update()` runs every
   frame — a "should this run continuously" question.

2. 🟡 **Offset sign flipped: `BodyTrans.position - new Vector3(xa, y, z)` instead of `+`.**
   The camera ends up mirrored on the opposite side of the intended offset (e.g. below/in
   front instead of above/behind), so the view looks wrong or is inside the player model.
   Fix: change `-` back to `+`. Tests: reading a vector-offset formula and predicting
   where it places an object relative to a target.

---

## 03_movement_basic_CORRUPTED.cs — Basic WASD movement (5 bugs)

1. 🟢 **`rb.freezeRotation = true;` changed to `false`.**
   The capsule can now be knocked over / tumble when colliding with things, since physics
   is allowed to rotate the Rigidbody freely. Fix: `true`. Tests: knowing this flag exists
   specifically to keep a character controller upright.

2. 🟡 **`MovePlayer()` moved into `Update()`; `FixedUpdate()` removed.**
   Physics forces are now applied on a variable-framerate callback instead of the fixed
   physics timestep, making movement speed inconsistent across different framerates/machines.
   Fix: restore `FixedUpdate()` calling `MovePlayer()`, remove the call from `Update()`.
   Tests: the core "why does FixedUpdate exist" concept from the walkthrough.

3. 🟡 **`Key.A` sets `horizontalInput = 1f` (should be `-1f`).**
   A and D both push the player the same direction — pressing A does nothing distinguishable
   from D. Fix: `-1f`. Tests: careful reading rather than assuming symmetric code is correct;
   also reinforces the sign convention (negative = left/negative axis).

4. 🔴 **`orientation.right * verticalInput + orientation.forward * horizontalInput`**
   (forward/right swapped relative to vertical/horizontal). W/S now strafe sideways and
   A/D move forward/backward. Fix: `orientation.forward * verticalInput + orientation.right
   * horizontalInput`. Tests: understanding which input axis is supposed to drive which
   world direction — a "does the code do what the variable names imply" check.

5. 🔴 **`moveDirection` no longer `.normalized` before applying force.**
   Diagonal movement (e.g. W+D together) produces a vector with magnitude ~1.41 instead
   of 1, so diagonal movement is faster than straight movement — hard to notice without
   comparing speeds directly. Fix: add `.normalized` back. Tests: vector magnitude
   intuition — a classic real-world game-dev bug, good to flag explicitly even if students
   don't catch it by eye (may need a hint: "try moving diagonally vs straight — same
   speed?").

---

## 04_movement_jump_CORRUPTED.cs — Jump & ground raycast (5 bugs)

1. 🔴 **`Physics.Raycast(...)` call is missing the `whatIsGround` layer mask argument.**
   Without it, the ray can hit *anything* — including the player's own collider geometry
   or unrelated objects — giving false "grounded" reads in the air or false negatives on
   the ground. Fix: add `whatIsGround` back as the 4th argument. Tests: understanding why
   the layer mask parameter exists at all (this is also **Q1** from the original PDF guide
   — good one to cross-reference).

2. 🔴 **`MyInput();` is called *before* the grounded raycast in `Update()` (order swapped).**
   Jump input is checked against last frame's `grounded` value, not the current frame's —
   a one-frame-stale read. Usually not visible to the eye, but worth discussing:
   "does line order matter here, and why?" Fix: compute `grounded` first, then call
   `MyInput()`.

3. 🟢 **`grounded` removed from the jump condition** (`readyToJump` alone gates the jump).
   Player can jump infinitely while airborne. Fix: add `&& grounded` back to the `if`.
   Tests: the most visible/obvious bug in this file — good "quick win" for students who
   are stuck elsewhere.

4. 🟢 **`Invoke(jumpCooldown, nameof(ResetJump));` — argument order reversed.**
   `Invoke` signature is `Invoke(string methodName, float time)`. This is a genuine
   **compile error** (float where string expected). Fix: swap to
   `Invoke(nameof(ResetJump), jumpCooldown);`. Tests: reading a compiler error message
   and matching it to a method signature — not a "guess and check" fix.

5. 🟡 **`Jump()` uses `ForceMode.Force` instead of `ForceMode.Impulse`.**
   `Force` applies a small continuous push scaled by the physics timestep — a single call
   in one frame barely moves the Rigidbody, so the jump is extremely weak or invisible.
   Fix: `ForceMode.Impulse`. Tests: the Force vs Impulse distinction (continuous vs
   instantaneous) covered in the walkthrough.

---

## 05_movement_final_drag_CORRUPTED.cs — Final version with drag (4 bugs)

1. 🟢 **`rb.freezeRotation = true;` deleted from `Start()`.**
   Same as bug 03-1 — capsule can tumble. Fix: add the line back.

2. 🔴 **Ternary flipped: `rb.linearDamping = grounded ? 0f : groundDrag;`**
   Drag now applies *in the air* and turns *off* on the ground — backwards. On the ground
   the player will feel like they're sliding on ice (no friction to stop them); in the air
   they'll feel unnaturally "sticky"/slowed. Fix: `grounded ? groundDrag : 0f;`. Tests:
   understanding what each branch of the ternary is *for*, not just its syntax.

3. 🟡 **W/S swapped: `Key.W` sets `verticalInput = -1f`, `Key.S` sets `verticalInput = 1f`.**
   Forward and backward are inverted. Fix: swap the values back. Tests: careful reading —
   same category as bug 03-3, reinforced here.

4. 🔴 **`Jump()` zeroes X/Z velocity and keeps Y, instead of the reverse.**
   `rb.linearVelocity = new Vector3(0f, rb.linearVelocity.y, 0f);` kills all horizontal
   momentum on every jump (player stops dead in the air whenever they jump) instead of
   resetting vertical velocity for a consistent jump height. Fix:
   `new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z);`. Tests: understanding
   *why* the original reset exists (consistent jump height regardless of fall/rise speed)
   — students need to explain the purpose before they can fix the axes correctly.

---

## 06_score_inc_CORRUPTED.cs — Scoring (3 bugs)

1. 🟢 **`OnTriggerEnter` changed to `OnTriggerStay`.**
   Fires every physics frame the colliders remain overlapping, not just once on entry —
   score shoots up by dozens/hundreds while standing on a plane instead of +1 per crossing.
   Fix: `OnTriggerEnter`. Tests: the difference between "enter" (once) and "stay"
   (every frame) trigger events — very visible in Play mode, good diagnostic starting point.

2. 🔴 **Condition flipped: `if (other.gameObject == prevPlane)`.**
   Score now increments only when re-entering the *same* plane you were already on,
   instead of when crossing to a *different* one — the opposite of the intended
   "scores when moving from one plane to another" rule. Fix: `!=`. Tests: reading the
   condition against the stated game rule, not just the code in isolation.

3. 🟡 **`scoreText.text = score.ToString();` placed before `score++;` (order swapped).**
   The displayed score always lags one crossing behind the internal `score` value (e.g.
   shows "0" on the first crossing instead of "1"). Fix: increment before updating the
   text. Tests: that statement order matters even when each line looks individually
   correct.

---

## Suggested class flow

- Bugs marked 🟢 are good "did you even read the code" checks — most students should
  catch these from Play-mode symptoms alone.
- 🟡 bugs need the student to trace logic, not just observe symptoms.
- 🔴 bugs are the ones worth pausing the whole class on if multiple groups get stuck —
  they map directly to the *concepts* from the walkthrough (FixedUpdate vs Update,
  Force vs Impulse, yaw-only body orientation, why velocity gets reset on jump), so a
  student who fixes them by trial-and-error without being able to explain *why* hasn't
  actually cleared the 70% bar — worth asking them to explain the fix out loud before
  moving on.
