add blueprints
This commit is contained in:
328
docs/blueprints/02-player/12_BPC_HidingSystem.md
Normal file
328
docs/blueprints/02-player/12_BPC_HidingSystem.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# 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.*
|
||||
Reference in New Issue
Block a user