Files
UE5-Modular-Game-Framework/docs/blueprints/02-player/12_BPC_HidingSystem.md
Lefteris Notas 411edea8ce add blueprints
2026-05-19 13:22:27 +03:00

328 lines
14 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 GI_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.
- 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.*