16 KiB
OGG — Dev Notes & Systems Guide
Boomer shooter. Cruelty Squad vibes. Jank is a feature. This doc covers all current systems.
Scene Hierarchy
Player [CharacterController, FirstPersonController, SimpleCrosshair]
│ [Player, PlayerHUD, Inventory, WeaponManager, BootsEffect]
└── Camera [Camera, AudioListener, CameraShake, WeaponBob]
Pickups are standalone GameObjects placed anywhere in the scene — not children of the player.
Scripts Overview
| Script | Lives On | What it does |
|---|---|---|
FirstPersonController |
Player | WASD move, mouse look, jump, sprint, cursor lock |
Player |
Player | Health + stamina stats, drain/regen logic, sprint gating |
PlayerHUD |
Player | Health bar, stamina bar, speedometer (IMGUI) |
SimpleGun |
Weapon prefabs | Shooting, ammo, recoil, muzzle flash, bullet decals |
CameraShake |
Camera | Singleton shake impulses, called by SimpleGun on fire |
WeaponBob |
Camera | Procedural weapon sway while moving |
WeaponViewmodel |
Weapon prefabs | Per-weapon position/rotation/scale in the player's hands |
SimpleCrosshair |
Player | Draws the crosshair via OnGUI |
EnemyHealth |
Enemy objects | Takes damage from SimpleGun raycasts, flashes red, dies |
EnemySpawner |
Any object | Spawns enemies at runtime |
HumanoidEnemy |
Enemy objects | Enemy AI / behaviour |
ItemDefinition |
ScriptableObject asset | Data definition for any item: name, icon, type, weapon prefab, equippable flag |
PickupItem |
Pickup GameObjects | Spin/bob animation, proximity prompt, E-to-collect |
Inventory |
Player | Item list, [I] HUD, right-click context menu, equip logic |
WeaponManager |
Player | Instantiates weapon prefabs, handles slot switching |
BootsEffect |
Player | Watches inventory for Bunny Hop Boots equipped state, applies stamina boost |
StaminaBoostPickup |
Pickup GameObjects | Legacy — superseded by BootsEffect. Left in for reference. |
RadarHUD |
Player | Mini-map radar disc (IMGUI), top-left corner — shows enemies, NPCs, pickups |
DialogueLine |
(data class) | Serializable speaker + pages of text, populated in Inspector on DialogueNPC |
DialogueNPC |
Any interactable object | World prompt + E-to-talk trigger, references DialogueLine array |
DialogueManager |
Any persistent GameObject | Singleton — renders the dialogue box, handles input and cursor lock |
1. Player Stats — Player + PlayerHUD
Player.cs is the data container for all player stats. PlayerHUD.cs reads from it and draws the HUD.
Health fields:
maxHealth/health— enemies subtract fromhealthdirectly, no auto-regen
Stamina fields:
maxStamina/stamina— depletes while sprintingstaminaDrain— units lost per second while sprinting (default 22)staminaRegen— units gained per second when resting (default 12)regenDelay— seconds of non-sprinting before regen starts (default 1.2)- When stamina hits 0 the player is exhausted — sprint locked out until stamina fully refills
HUD Layout:
[bottom-left corner]
SPEED
42.3
units / sec
HEALTH 85 / 100
[████████████░░░]
STAMINA 62 / 100
[██████░░░░░░░░░]
Speedometer:
- Sits above the health/stamina bars, bottom-left
- Speed calculated from actual position delta per frame (horizontal only)
- Colour shifts: green → yellow → red as speed increases
- Thresholds configurable in Inspector (
speedoFastThreshold,speedoCritThreshold) - Can be toggled off via
showSpeedometerbool
2. Movement — FirstPersonController
Standard FPS controller.
Inspector fields:
walkSpeed/runSpeed— intentionally high (50/80) due to CharacterController collision tuningmouseSensitivity— linear, no accelerationplayerCamera— auto-found if empty, best to assign explicitly
Controls:
| Key | Action |
|---|---|
| W A S D / Arrows | Move |
| Left Shift | Sprint (drains stamina) |
| Space | Jump |
| Mouse | Look |
| Escape | Unlock cursor |
| Left Click (unlocked) | Re-lock cursor |
3. Shooting — SimpleGun
Raycast-based hitscan. No projectiles.
Key fields:
damage— per shotrange— raycast distance in unitsfireRate— shots per secondisAutomatic— hold vs clickrecoilKickback/recoilKickUp— feel tuningfpsCam— auto-found if null
Internals:
- Muzzle flash: child Quad + Point Light, shown ~50ms per shot
- Bullet decals: pooled (30 max), oldest reused when full
- Spark particles: spawned and self-destroyed on impact
- Calls
CameraShake.Instance.Shake()on every shot - Shooting blocked when inventory is open
- Position driven by
WeaponManager.weaponPositionOffseteach frame so recoil applies on top correctly
Ammo: currentAmmo / maxAmmo. Press R to reload (instant).
4. Item Definitions — ItemDefinition (ScriptableObject)
Create: Right-click in Project → Create → OGG → Item Definition
| Field | Notes |
|---|---|
itemName |
Display name everywhere — must be unique |
icon |
Sprite shown in inventory slots |
type |
Misc, Consumable, or Weapon |
isEquippable |
Tick for non-weapon items that can be equipped (e.g. boots) — shows equip button in inventory |
weaponPrefab |
Weapon type only — prefab spawned in weapon holder when equipped |
description |
Shown in inventory detail panel |
5. Pickups — PickupItem
Attach to any GameObject to make it a spinning, bobbing pickup.
Setup:
- Place a GameObject in the scene with any mesh
- Add
PickupItemcomponent - Drag an
ItemDefinitioninto theDefinitionslot
Inspector fields:
| Field | Notes |
|---|---|
definition |
The ItemDefinition asset |
spinSpeed |
Degrees/sec Y rotation (default 120) |
bobHeight |
Vertical travel in units (default 0.18) |
bobSpeed |
Bob cycles per second (default 2.2) |
pickupRadius |
Proximity trigger distance |
pickupKey |
Default E |
pickupParticlesPrefab |
Optional — spawned on collect |
pickupSound |
Optional — played on collect |
On pickup: Calls Inventory.AddItem(definition, autoEquip: true) — picking up a weapon automatically equips it.
6. Inventory — Inventory
Lives on the Player. Press I to open/close. Cursor unlocks when open.
HUD Layout:
┌──────────────────────────────┐
│ [ INVENTORY ] │ ← header, shows active weapon top-right
├──────────────────────────────┤
│ [ ] [ ] [ ] [ ] │
│ [ ] [ ] [ ] [ ] │ ← item grid (4×5 by default)
├──────────────────────────────┤
│ SELECTED: Item Name │ ← detail panel
│ Description │
│ ▶ EQUIPPED [ F ] UNEQUIP│ ← shown for weapons AND equippable items
└──────────────────────────────┘
Right-click context menu on any slot:
- Equip / Unequip — shown for weapons and items with
isEquippable = true - Inspect — selects the item in the detail panel
- Drop — removes from inventory
Controls:
| Input | Action |
|---|---|
| I | Toggle inventory open/close |
| Click slot | Select item |
| Double-click weapon slot | Equip it |
| Right-click slot | Open context menu |
| F | Equip selected item |
| Scroll Wheel (closed) | Cycle weapons |
| 1–9 (closed) | Equip weapon by slot number |
Slot colours:
- Dark grey = empty
- Dark green = has item
- Bright green = selected
- Orange = equipped
Useful methods:
inventory.HasItem("Medkit") // bool
inventory.CountOf("Grenade") // int
inventory.RemoveItem("Medkit", 1) // bool
inventory.AddItem(definition) // add by ItemDefinition
inventory.AddItem("Raw Name", sprite) // add by name
inventory.IsOpen // bool — checked by gun + WeaponManager
7. Weapon Manager — WeaponManager
Lives on the Player. Manages weapon instances and which is active.
Setup:
- Add
WeaponManagerto Player - Set
Weapon Holderto the Camera transform - Any
SimpleGunchild of the holder is auto-registered but starts inactive — player must pick up and equip from inventory
Weapon Position fields:
weaponPositionOffset/weaponRotationOffset/weaponScale— global fallback if the weapon has noWeaponViewmodelcomponent- Per-weapon positioning is preferred — add
WeaponViewmodelto the weapon prefab instead
Live positioning workflow (in Editor):
- Hit Play, equip the gun
- On WeaponManager Inspector → click "Select Active Weapon in Scene"
- Use gizmos in Scene view to position it
- Click "Sync Transform → Offset Fields" — saves to the
WeaponViewmodelon the prefab automatically - Stop Play — values persist
Useful methods:
weaponManager.EquipByIndex(0)
weaponManager.EquipByName("Gun Splat")
weaponManager.CycleWeapon(1) // next
weaponManager.CycleWeapon(-1) // previous
weaponManager.ActiveWeaponName // string
weaponManager.Unequip() // holster all
8. WeaponViewmodel — WeaponViewmodel
Add to a weapon prefab to define how it sits in the player's hands. WeaponManager reads from it on equip instead of using the global offset.
Fields:
positionOffset— local position relative to weapon holderrotationOffset— euler anglesscale— local scale
Each weapon prefab gets its own values, so Gun Splat can sit differently from any future weapon.
9. Radar HUD — RadarHUD
Attach to the Player alongside PlayerHUD. Draws a circular mini-map in the top-left corner. Rotates so "up" always faces the player's forward direction.
Blip legend:
| Blip | Colour | Shape | Source component |
|---|---|---|---|
| Player | White | Round | (always centre) |
| Enemy | Red (pulsing) | Round | HumanoidEnemy |
| NPC | Green | Round | DialogueNPC |
| Pickup | Yellow | Square | PickupItem |
Points beyond worldRange are clamped to the disc edge so off-screen threats always show a direction.
Key Inspector fields:
| Field | Notes |
|---|---|
radarRadius |
Visual size of the disc in pixels (default 60) |
worldRange |
World-space units covered (default 40) |
scanInterval |
How often the scene is re-scanned for objects (default 0.25s) |
No tags or layers required — blips are found by component type.
10. Dialogue — DialogueNPC + DialogueManager
Setup
- Add
DialogueManagerto any persistent GameObject in the scene (e.g. a "Managers" empty). One instance required per scene. - Add
DialogueNPCto any world object you want to be talkable. - In the Inspector on
DialogueNPC, expand theLinesarray and fill in speaker names and pages.
DialogueNPC Inspector fields
| Field | Notes |
|---|---|
lines[] |
Array of DialogueLine entries — each has a speaker name + pages of text |
interactRange |
Max distance for the E prompt to appear (default 3.5 units) |
interactAngle |
Max degrees off-centre the player can be looking (default 45°) |
occlusionMask |
Layer mask for the line-of-sight raycast |
promptText |
Label shown in the world-space prompt bubble (default [E] Talk) |
Dialogue box controls
| Key | Action |
|---|---|
| E / Space / Enter | Advance page (or skip typewriter) |
| Escape | Close immediately |
DialogueManager Inspector fields
| Field | Notes |
|---|---|
useTypewriter |
Enable/disable letter-by-letter reveal (default on) |
charsPerSecond |
Typewriter speed (default 40) |
boxHeightFraction |
Box height as fraction of screen height (default 0.22) |
Rich text tags (<b>, <i>, <color=red>) work inside page text — good for glitchy/stylised dialogue.
While the dialogue box is open, mouse look and movement are frozen and the cursor is unlocked automatically.
NPCs on the radar
Any GameObject with a DialogueNPC component automatically appears as a green dot on the RadarHUD. No extra tagging needed.
11. Equippable Items — BootsEffect
Add BootsEffect to the Player for the Bunny Hop Boots to work.
Lives on the Player. Watches Inventory.items each frame for the boots' isEquipped state and adjusts Player.maxStamina accordingly.
| State | Effect |
|---|---|
| Boots equipped | maxStamina → 200, stamina topped up |
| Boots unequipped | maxStamina → 100, stamina clamped back down |
The stamina bar stretches/shrinks live to reflect the new max.
Inspector fields:
bootsItemName— must match the ItemDefinitionitemNameexactly (default"Bunny Hop Boots")boostedMaxStamina— default 200
Setup Scripts (Editor Menu: OGG → Setup)
Create Gun Splat Weapon
Creates:
Assets/Prefabs/Weapons/GunSplat.prefab— held weapon withSimpleGunAssets/Prefabs/Pickups/GunSplat_Pickup.prefab— world pickup with spin/bobAssets/Items/GunSplat_Item.asset—ItemDefinition(type = Weapon)
Create Bunny Hop Boots
Creates:
Assets/Items/BhopBoots_Item.asset—ItemDefinition(type = Misc, isEquippable = true)Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab— world pickup
After running either script: drag the pickup prefab into your scene.
Adding a New Weapon — Step by Step
- Create an
ItemDefinitionasset — settype = Weapon, assign your gun prefab toweaponPrefab - Add
SimpleGunto the gun prefab root - Add
WeaponViewmodelto the gun prefab — set position/rotation/scale or dial it in with the live Editor workflow - Create a pickup: any mesh +
PickupItemcomponent + drag in the ItemDefinition - Place the pickup in the scene
Adding a New Equippable Item
- Create an
ItemDefinition— settype = Misc, tickisEquippable = true - Write a component that lives on the Player, watches
inventory.itemsforentry.isEquipped, and applies/removes the effect - Place a pickup in the scene referencing the definition
Adding a Consumable
Inventory inv = GetComponent<Inventory>();
if (inv.HasItem("Medkit")) {
health += 50f;
inv.RemoveItem("Medkit");
}
Known Rough Edges
- No weapon switch animation — instant swap. Hook into
WeaponManager.EquipByIndex()when ready. - No save system — inventory is runtime only, nothing persists between sessions.
- Starting gun — any
SimpleGunthat's a child of the Camera at Start gets auto-registered but starts inactive. Player needs to find it as a pickup in the scene. - Inventory is IMGUI — not Canvas. Performance fine at this scale. All data logic is decoupled from drawing so a Canvas swap later would be straightforward.
SimpleGunassumes it's always active — non-SimpleGun weapons will slot-switch fine but need their own input handling to shoot.- Dialogue LOS raycast — uses
occlusionMaskdefaulting to~0(all layers). If geometry is blocking the prompt unexpectedly, trim the mask on theDialogueNPCcomponent.
Controls Reference
| Key | Action |
|---|---|
| W A S D | Move |
| Left Shift | Sprint |
| Space | Jump |
| Left Click | Shoot |
| Right Click | Context menu (inventory open) |
| R | Reload |
| E | Pick up nearby item / advance dialogue |
| I | Open / close inventory |
| F | Equip selected item |
| Scroll Wheel | Cycle weapons |
| 1 – 9 | Equip weapon by slot |
| Escape | Unlock cursor |