- Updated references from GI_GameTagRegistry to DA_GameTagRegistry in architecture overview and implementation patterns documentation. - Added new Blueprint specification for GI_StarterGameInstance, detailing its purpose, configuration, and integration pattern. - Introduced DA_GameTagRegistry Blueprint specification, centralizing GameplayTag management and providing functions for tag validation and logging. - Created documentation for the Starter GameInstance, outlining its role in the project setup and how other systems can integrate with it.
352 lines
15 KiB
Markdown
352 lines
15 KiB
Markdown
# 12 — Hiding System (`BPC_HidingSystem`)
|
|
|
|
## Purpose
|
|
Manages the player's ability to hide inside, behind, or under environmental objects. Handles entering/exiting hiding spots, line-of-sight checks against enemies, peeking, and stress reduction while concealed. The central component for stealth gameplay.
|
|
|
|
## Dependencies
|
|
- **Requires:** `FL_GameUtilities` (interface casts), `I_HidingSpot` (interface on world actors)
|
|
- **Required By:** `AI_EnemyController` (query hiding state for awareness), `BPC_StressSystem` (stress decay while hidden), `BPC_PlayerController` (movement/input restriction)
|
|
- **Engine/Plugin Requirements:** GameplayTags, LineTraceByChannel (LOS checks)
|
|
|
|
## Class Info
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| **Parent Class** | `ActorComponent` |
|
|
| **Class Type** | Blueprint Component |
|
|
| **Asset Path** | `Content/Framework/Player/BPC_HidingSystem` |
|
|
| **Implements Interfaces** | None |
|
|
|
|
---
|
|
|
|
## 1. Enums
|
|
|
|
### `E_HideState`
|
|
| Value | Description |
|
|
|-------|-------------|
|
|
| `Exposed = 0` | Not hiding, fully visible |
|
|
| `Entering = 1` | Transient — animation playing to enter hide spot |
|
|
| `Hidden = 2` | Inside hide spot, concealed |
|
|
| `Peeking = 3` | Partially visible, can look around |
|
|
| `Exiting = 4` | Transient — animation playing to exit hide spot |
|
|
|
|
### `E_HideType`
|
|
| Value | Description |
|
|
|-------|-------------|
|
|
| `Locker = 0` | Fully enclosed (locker, wardrobe) |
|
|
| `BehindCover = 1` | Behind low wall, crate, counter |
|
|
| `Under = 2` | Under bed, table, porch |
|
|
| `InShadow = 3` | Standing in a shadow volume |
|
|
| `TallGrass = 4` | Crouch-moving through vegetation |
|
|
|
|
### `E_PeekDirection`
|
|
| Value | Description |
|
|
|-------|-------------|
|
|
| `Left = 0` | Peek left from behind cover |
|
|
| `Right = 1` | Peek right from behind cover |
|
|
| `Over = 2` | Peek over low cover |
|
|
|
|
---
|
|
|
|
## 2. Structs
|
|
|
|
### `S_HideSpotInfo`
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `HidingActor` | `AActor` | The hide spot actor |
|
|
| `HideType` | `E_HideType` | Type of hiding |
|
|
| `SlotCount` | `Integer` | How many can hide here (-1 = infinite) |
|
|
| `bHasPeekAbility` | `Boolean` | Can player peek from this spot? |
|
|
| `PeekSocketLeft` | `FName` | Socket name for left peek camera |
|
|
| `PeekSocketRight` | `FName` | Socket name for right peek camera |
|
|
| `PeekSocketOver` | `FName` | Socket name for over peek camera |
|
|
| `ExitLocation` | `Vector` | World location to place player on exit |
|
|
| `ExitRotation` | `Rotator` | Rotation to face on exit |
|
|
| `bBlocksStress` | `Boolean` | Whether this spot reduces stress gain |
|
|
| `HideTags` | `GameplayTagContainer` | Tags for filtering |
|
|
| `LOSTraceChannel` | `TEnumAsByte<ETraceTypeQuery>` | Trace channel for LOS checks from this spot |
|
|
|
|
---
|
|
|
|
## 3. Variables
|
|
|
|
### Configuration (Instance Editable, Expose On Spawn)
|
|
|
|
| Variable | Type | Default | Category | Description |
|
|
|----------|------|---------|----------|-------------|
|
|
| `bCanHide` | `Boolean` | `true` | `Hiding Config` | Can this player hide at all? |
|
|
| `StressDecayWhileHidden` | `Float` | `5.0` | `Hiding Config` | Stress lost per second while hidden |
|
|
| `bEnemyCanFindHidingSpot` | `Boolean` | `true` | `Hiding Config` | Can enemies discover this spot |
|
|
| `MaxPeekDuration` | `Float` | `3.0` | `Hiding Config` | Max seconds player can peek before forced back |
|
|
| `PeekCooldown` | `Float` | `1.5` | `Hiding Config` | Seconds before player can peek again |
|
|
| `CapsuleCheckRadius` | `Float` | `50.0` | `Hiding Config` | Radius for capsule trace when finding hide spots |
|
|
|
|
### Internal (Private / Protected, No Expose)
|
|
|
|
| Variable | Type | Default | Category | Description |
|
|
|----------|------|---------|----------|-------------|
|
|
| `CurrentHideState` | `E_HideState` | `Exposed` | `Hiding State` | Current hiding state |
|
|
| `PreviousHideState` | `E_HideState` | `Exposed` | `Hiding State` | Previous state for transition detection |
|
|
| `CurrentHideSpot` | `S_HideSpotInfo` | `-` | `Hiding State` | Active hide spot data |
|
|
| `PendingHideSpot` | `AActor` | `None` | `Hiding State` | Hide spot being entered |
|
|
| `PeekTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for max peek duration |
|
|
| `PeekCooldownTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for peek cooldown |
|
|
| `bCanPeek` | `Boolean` | `true` | `Hiding State` | Whether peeking is allowed |
|
|
| `LastLOSCheckTime` | `Float` | `0.0` | `Hiding State` | World time of last LOS check |
|
|
| `LOSTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for periodic LOS checks |
|
|
| `BreathHeldTimer` | `FTimerHandle` | `-` | `Hiding State` | Timer for breath-hold mechanic |
|
|
| `bIsHoldingBreath` | `Boolean` | `false` | `Hiding State` | Whether player is holding breath |
|
|
|
|
### Replicated (if multiplayer)
|
|
|
|
| Variable | Type | Condition | Description |
|
|
|----------|------|-----------|-------------|
|
|
| `CurrentHideState` | `E_HideState` | `RepNotify` | Replicated hide state |
|
|
| `CurrentHideSpot` | `S_HideSpotInfo` | `Replicated` | Replicated spot reference |
|
|
|
|
---
|
|
|
|
## 4. Functions
|
|
|
|
### Public Functions
|
|
|
|
#### `EnterHideSpot` → `Boolean`
|
|
- **Description:** Attempts to enter a hiding spot. Returns true if successful.
|
|
- **Parameters:**
|
|
| Param | Type | Description |
|
|
|-------|------|-------------|
|
|
| `HideSpotActor` | `AActor` | The hide spot to enter |
|
|
- **Blueprint Authority:** Server (if MP), Any (single-player)
|
|
- **Flow:**
|
|
1. Early-out if CurrentHideState != Exposed or !bCanHide
|
|
2. Validate that HideSpotActor implements I_HidingSpot
|
|
3. Check slot availability (I_HidingSpot.Execute_HasAvailableSlots)
|
|
4. If available:
|
|
- Set CurrentHideState = Entering
|
|
- Store HideSpotActor info in CurrentHideSpot
|
|
- Disable player movement input
|
|
- Play entering animation (via ABP_GASP)
|
|
- On animation complete: Set CurrentHideState = Hidden
|
|
- Fire OnHideStateChanged
|
|
- Start stress decay timer
|
|
- Start periodic LOS check timer
|
|
- Return true
|
|
5. If full: return false
|
|
|
|
#### `ExitHideSpot` → `void`
|
|
- **Description:** Exits the current hiding spot.
|
|
- **Parameters:**
|
|
| Param | Type | Description |
|
|
|-------|------|-------------|
|
|
| `bForceExit` | `Boolean` | If true, skip exit animation |
|
|
- **Flow:**
|
|
1. If CurrentHideState != Hidden and CurrentHideState != Peeking: return
|
|
2. Set CurrentHideState = Exiting
|
|
3. If bForceExit: skip animation, immediately set Exposed
|
|
4. Else: play exiting animation
|
|
5. On animation complete or bForceExit:
|
|
- Teleport player to CurrentHideSpot.ExitLocation with ExitRotation
|
|
- Re-enable movement input
|
|
- Set CurrentHideState = Exposed
|
|
- Clear CurrentHideSpot
|
|
- Stop stress decay and LOS timers
|
|
- Fire OnHideStateChanged
|
|
- Fire OnExitedHidingSpot
|
|
|
|
#### `StartPeek` → `void`
|
|
- **Description:** Begins peeking from behind cover.
|
|
- **Parameters:**
|
|
| Param | Type | Description |
|
|
|-------|------|-------------|
|
|
| `Direction` | `E_PeekDirection` | Which direction to peek |
|
|
- **Flow:**
|
|
1. Early-out if CurrentHideState != Hidden or !bCanPeek or !CurrentHideSpot.bHasPeekAbility
|
|
2. Set CurrentHideState = Peeking
|
|
3. Move camera to peek socket location
|
|
4. Start MaxPeekDuration timer
|
|
5. Start PeekCooldown timer (bCanPeek = false during cooldown)
|
|
6. Fire OnPeekStarted
|
|
|
|
#### `StopPeek` → `void`
|
|
- **Description:** Ends the peek and returns to Hidden state.
|
|
- **Flow:**
|
|
1. If CurrentHideState != Peeking: return
|
|
2. Set CurrentHideState = Hidden
|
|
3. Return camera to default hide position
|
|
4. Fire OnPeekEnded
|
|
|
|
#### `IsPlayerDetectable` → `Boolean`
|
|
- **Description:** Checks if the player is detectable from a given enemy location, considering hide type and edges.
|
|
- **Parameters:**
|
|
| Param | Type | Description |
|
|
|-------|------|-------------|
|
|
| `EnemyLocation` | `Vector` | Location of the enemy |
|
|
| `DetectionRange` | `Float` | Max range for detection check |
|
|
- **Flow:**
|
|
1. If CurrentHideState != Hidden: return true (fully detectable)
|
|
2. If EnemyLocation distance > DetectionRange: return false
|
|
3. Perform line trace from enemy to player's approximate position
|
|
4. If trace hits CurrentHideSpot.HidingActor before player: return false (blocked by cover)
|
|
5. If trace hits player directly: return true (cover not fully effective)
|
|
6. Edge case: peek state increases detection radius
|
|
|
|
#### `TryBreathHold` → `void`
|
|
- **Description:** Player holds breath to reduce noise/detectability for a short time.
|
|
- **Flow:**
|
|
1. bIsHoldingBreath = true
|
|
2. Start breath-hold timer (e.g., 8 seconds)
|
|
3. On timer end: forced exhale, brief noise event, bIsHoldingBreath = false
|
|
4. Fire OnBreathHoldChanged
|
|
|
|
### Protected / Private Functions
|
|
|
|
#### `PerformLOSCheck` → `void`
|
|
- **Description:** Timer callback — checks if any enemy has line of sight to this hide spot.
|
|
- **Flow:**
|
|
1. Get all AI_EnemyController instances within detection range
|
|
2. For each enemy: call IsPlayerDetectable(EnemyLocation)
|
|
3. If any enemy has LOS:
|
|
- Fire OnHideSpotCompromised
|
|
- If CurrentHideSpot.bEnemyCanFindHidingSpot: fire OnForcedExitWarning
|
|
|
|
#### `OnAnimationHideEnterComplete` → `void`
|
|
- **Description:** Animation notify callback when enter animation finishes.
|
|
- **Flow:**
|
|
1. Set CurrentHideState = Hidden
|
|
2. Attach player to hide spot socket if applicable
|
|
3. Fire OnHideStateChanged
|
|
4. Start stress decay loop
|
|
|
|
#### `OnAnimationHideExitComplete` → `void`
|
|
- **Description:** Animation notify callback when exit animation finishes.
|
|
- **Flow:**
|
|
1. Perform final exit steps already queued in ExitHideSpot
|
|
|
|
---
|
|
|
|
## 5. Event Dispatchers
|
|
|
|
| Dispatcher | Parameters | Bind Access | Description |
|
|
|------------|-----------|-------------|-------------|
|
|
| `OnHideStateChanged` | `E_HideState OldState`, `E_HideState NewState` | `Public` | Fired on any hide state change |
|
|
| `OnEnteredHidingSpot` | `AActor HideSpot`, `E_HideType HideType` | `Public` | Fired when player successfully hides |
|
|
| `OnExitedHidingSpot` | `AActor HideSpot` | `Public` | Fired when player leaves hiding |
|
|
| `OnPeekStarted` | `E_PeekDirection Direction` | `Public` | Fired when player starts peeking |
|
|
| `OnPeekEnded` | `none` | `Public` | Fired when player stops peeking |
|
|
| `OnHideSpotCompromised` | `AActor EnemyActor` | `Public` | Fired when an enemy sees the player's spot |
|
|
| `OnForcedExitWarning` | `float TimeUntilDiscovered` | `Public` | Fired when enemy is approaching known spot |
|
|
| `OnBreathHoldChanged` | `bool bIsHoldingBreath` | `Public` | Fired when breath hold toggles |
|
|
|
|
---
|
|
|
|
## 6. Overridden Events / Custom Events
|
|
|
|
### Event: `BeginPlay`
|
|
- **Description:** Initialises state, binds to input actions.
|
|
- **Flow:**
|
|
1. Set CurrentHideState = Exposed
|
|
2. Register with DA_GameTagRegistry if needed
|
|
3. Cache reference to CharacterMovementComponent for disabling during hide
|
|
|
|
### Custom Event: `ForceKickFromHide`
|
|
- **Description:** Called externally (e.g., by enemy discovery) to force player out of hiding.
|
|
- **Flow:**
|
|
1. Call ExitHideSpot(bForceExit = true)
|
|
2. Apply brief stun or camera shake
|
|
3. Fire OnForcedExitWarning with 0 seconds
|
|
|
|
### Custom Event: `OnDamageWhileHiding`
|
|
- **Description:** Listener for when damage is taken while hidden.
|
|
- **Flow:**
|
|
1. If damage source penetrates cover: force exit
|
|
2. Else: reduce stress instead of health (environmental damage)
|
|
|
|
---
|
|
|
|
## 7. Blueprint Graph Logic Flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[EnterHideSpot called] --> B{Is Exposed?}
|
|
B -->|No| C[Return false]
|
|
B -->|Yes| D[Validate I_HidingSpot]
|
|
D --> E{Has available slots?}
|
|
E -->|No| F[Return false]
|
|
E -->|Yes| G[Set Entering state]
|
|
G --> H[Disable movement input]
|
|
H --> I[Play Enter animation]
|
|
I --> J[Animation notify: OnComplete]
|
|
J --> K[Set Hidden state]
|
|
K --> L[Start stress decay loop]
|
|
L --> M[Start periodic LOS check]
|
|
M --> N[Fire OnEnteredHidingSpot]
|
|
N --> O[Return true]
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Communication Matrix
|
|
|
|
| Who Talks | How | What Is Sent |
|
|
|-----------|-----|-------------|
|
|
| `BPC_HidingSystem` | `Dispatcher` | `OnHideStateChanged` -> `BPC_PlayerController` (input), `ABP_GASP` (anim), `BPC_StressSystem` (decay), `AI_EnemyController` (awareness reset) |
|
|
| `BPC_HidingSystem` | `Dispatcher` | `OnHideSpotCompromised` -> `BPC_StressSystem` (stress spike), `BP_AudioManager` (tension music) |
|
|
| `BPC_HidingSystem` | `Dispatcher` | `OnForcedExitWarning` -> `WBP_HUD` (hide icon flash), `BP_AudioManager` (heartbeat) |
|
|
| `External (AI)` | `Query` | `IsPlayerDetectable` called by `AI_EnemyController` for LOS checks |
|
|
| `External (World)` | `Interface` | `I_HidingSpot` on `BP_Locker`, `BP_Wardrobe`, `BP_Cover` |
|
|
| `BPC_HidingSystem` | `Dispatcher` | `OnPeekStarted/Ended` -> `BPC_CameraStateLayer` (FOV/position) |
|
|
|
|
---
|
|
|
|
## 9. Validation / Testing Checklist
|
|
|
|
- [ ] EnterHideSpot correctly transitions through Entering -> Hidden
|
|
- [ ] ExitHideSpot correctly transitions through Exiting -> Exposed
|
|
- [ ] Forced exit with bForceExit = true skips animation, immediately exits
|
|
- [ ] Peek timer forces player back to Hidden after MaxPeekDuration
|
|
- [ ] PeekCooldown prevents rapid peek toggling
|
|
- [ ] IsPlayerDetectable returns correct values for all hide types and peek states
|
|
- [ ] Multiple hide spots with limited slots: full slots reject entry
|
|
- [ ] Stress decays while hidden at configured rate
|
|
- [ ] LOS check detects enemies within range and fires compromise dispatcher
|
|
- [ ] Edge case: EnterHideSpot called while already hiding returns false
|
|
- [ ] Edge case: ExitHideSpot called while Exposed does nothing
|
|
- [ ] Edge case: Destroying hide spot while inside force-exits player
|
|
|
|
---
|
|
|
|
## 10. Reuse Notes
|
|
|
|
- Enemy AI characters can use a simplified version to find and occupy hide spots.
|
|
- Hide spots can be pre-placed (lockers, beds) or dynamic (shadow volumes, tall grass).
|
|
- The LOS trace channel should be custom (e.g., DetectionChannel) to ignore small props.
|
|
- Breath-hold mechanic adds depth to stealth gameplay near enemies.
|
|
- For multiplayer: only the hiding player knows their state; other players see a generic "occupado" on the spot.
|
|
|
|
---
|
|
|
|
## 11. Multiplayer Networking (Expanded)
|
|
|
|
### Server RPCs
|
|
| RPC | Direction | Description |
|
|
|-----|-----------|-------------|
|
|
| `Server_EnterHideSpot` | Client→Server | Client requests entering hide spot. Server validates slot availability, proximity. |
|
|
| `Server_ExitHideSpot` | Client→Server | Client requests exiting. Server validates, teleports player, replicates. |
|
|
| `Server_StartPeek` | Client→Server | Client starts peeking. Server validates peek ability, starts duration timer. |
|
|
| `Server_StopPeek` | Client→Server | Client stops peeking. Server returns to Hidden state. |
|
|
| `Server_TryBreathHold` | Client→Server | Client holds breath. Server validates, applies noise reduction on server. |
|
|
|
|
### Authority
|
|
- `EnterHideSpot`, `ExitHideSpot` are server-authoritative.
|
|
- `IsPlayerDetectable` is server-only — AI runs on server.
|
|
- `PerformLOSCheck` runs on server; results multicast to clients for UI feedback.
|
|
- `ForceKickFromHide` is server-only (triggered by enemy discovery / damage).
|
|
|
|
### Client Prediction
|
|
- Enter/exit animations: client predicts; server confirms via replicated `CurrentHideState`.
|
|
- Peek: camera offset is local; state replicated.
|
|
- Hide spot occupation: `I_HidingSpot.OccupantPawn` is replicated — other clients see "Occupied".
|
|
- Hide spots with bBlocksStress = true can double as narrative safe rooms.
|
|
|
|
---
|
|
|
|
*Blueprint Spec: Hiding System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.* |