add blueprints
This commit is contained in:
335
docs/blueprints/02-player/15_BPC_PlayerMetricsTracker.md
Normal file
335
docs/blueprints/02-player/15_BPC_PlayerMetricsTracker.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# 15 — Player Metrics Tracker (`BPC_PlayerMetricsTracker`)
|
||||
|
||||
## Purpose
|
||||
Records and exposes player behaviour telemetry throughout a play session. Metrics feed into the adaptive difficulty system, achievement tracking, end-game stats screen, and narrative pacing adjustments. This is a pure data-gathering component — it never blocks or modifies gameplay directly.
|
||||
|
||||
## Dependencies
|
||||
- **Requires:** `GI_GameFramework` (session start/end events), `DA_GameDifficulty` (adaptive thresholds)
|
||||
- **Required By:** `BPC_AdaptiveDifficulty` (consumes metrics), `WBP_StatsScreen` (end-game display), `GM_CoreGameMode` (death/checkpoint metrics)
|
||||
- **Engine/Plugin Requirements:** `FTimerHandle` for play time tracking
|
||||
|
||||
## Class Info
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Parent Class** | `ActorComponent` |
|
||||
| **Class Type** | Blueprint Component |
|
||||
| **Asset Path** | `Content/Framework/Player/BPC_PlayerMetricsTracker` |
|
||||
| **Implements Interfaces** | `I_Persistable` (save/load metrics) |
|
||||
|
||||
---
|
||||
|
||||
## 1. Enums
|
||||
|
||||
### `E_MetricEventType`
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `Death = 0` | Player died |
|
||||
| `HideStarted = 1` | Player entered a hide spot |
|
||||
| `HideEnded = 2` | Player exited a hide spot |
|
||||
| `ItemCollected = 3` | Player picked up an item |
|
||||
| `EnemyEncountered = 4` | Player entered an enemy detection range |
|
||||
| `EnemyEvaded = 5` | Player escaped enemy detection |
|
||||
| `StressPeakReached = 6` | Stress tier exceeded Distressed |
|
||||
| `AreaExplored = 7` | Player entered a new area/chapter |
|
||||
| `NarrativeEvent = 8` | Story beat triggered |
|
||||
| `CombatEvent = 9` | Player used a weapon or was attacked |
|
||||
|
||||
---
|
||||
|
||||
## 2. Structs
|
||||
|
||||
### `S_BehaviourEvent`
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `Timestamp` | `Float` | Game time in seconds when event occurred |
|
||||
| `EventType` | `E_MetricEventType` | What happened |
|
||||
| `ContextTag` | `GameplayTag` | Tagged context for filtering |
|
||||
| `FloatValue` | `Float` | Numeric payload (e.g. distance, intensity) |
|
||||
| `StringValue` | `String` | Text payload (e.g. location name, enemy ID) |
|
||||
| `Location` | `Vector` | World location where event occurred |
|
||||
|
||||
### `S_MetricsSnapshot`
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `TotalPlayTime` | `Float` | Total seconds played |
|
||||
| `TimesDied` | `Integer` | Total deaths this session |
|
||||
| `TimesHid` | `Integer` | Total hide events |
|
||||
| `TotalDistanceWalked` | `Float` | Cumulative walking distance (cm) |
|
||||
| `TotalDistanceSprinted` | `Float` | Cumulative sprinting distance (cm) |
|
||||
| `ItemsCollected` | `Integer` | Total items picked up |
|
||||
| `UniqueItemsCollected` | `Set<FName>` | Names of unique items collected |
|
||||
| `EnemiesEncountered` | `Integer` | Total unique enemy encounters |
|
||||
| `EnemiesEvaded` | `Integer` | Total successful evasions |
|
||||
| `PeakStressTier` | `Float` | Highest stress tier reached (as float) |
|
||||
| `StressPeakCount` | `Integer` | Times stress exceeded Distressed threshold |
|
||||
| `NarrativeBeatsCompleted` | `Integer` | Story triggers activated |
|
||||
| `AreasExplored` | `Set<FName>` | Unique areas/chapters visited |
|
||||
| `CombatEvents` | `Integer` | Total combat interactions |
|
||||
|
||||
### `S_TimeSegment`
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `SegmentLabel` | `String` | Label for this time segment |
|
||||
| `StartTime` | `Float` | Game time at segment start |
|
||||
| `EndTime` | `Float` | Game time at segment end |
|
||||
| `StressAverage` | `Float` | Average stress during segment |
|
||||
| `MetricsDelta` | `S_MetricsSnapshot` | Metrics accumulated in this segment |
|
||||
|
||||
---
|
||||
|
||||
## 3. Variables
|
||||
|
||||
### Configuration (Instance Editable, Expose On Spawn)
|
||||
|
||||
| Variable | Type | Default | Category | Description |
|
||||
|----------|------|---------|----------|-------------|
|
||||
| `bTrackDetailedEvents` | `Boolean` | `true` | `Metrics Config` | If true, store full `S_BehaviourEvent` history |
|
||||
| `MaxEventHistory` | `Integer` | `5000` | `Metrics Config` | Cap on stored events to prevent memory bloat |
|
||||
| `bAutoSaveMetrics` | `Boolean` | `true` | `Metrics Config` | Auto-save metrics via I_Persistable on quit |
|
||||
| `SnapshotInterval` | `Float` | `60.0` | `Metrics Config` | Seconds between automatic snapshot writes |
|
||||
|
||||
### Computed / Runtime (Public, Blueprint Read Only)
|
||||
|
||||
| Variable | Type | Default | Category | Description |
|
||||
|----------|------|---------|----------|-------------|
|
||||
| `CurrentSnapshot` | `S_MetricsSnapshot` | `-` | `Runtime Metrics` | Live accumulated metrics this session |
|
||||
| `bIsTrackingSession` | `Boolean` | `false` | `Runtime Metrics` | Whether currently in a tracked session |
|
||||
| `TimeSegmentHistory` | `Array<S_TimeSegment>` | `[]` | `Runtime Metrics` | Historical segments for analysis |
|
||||
| `CurrentSegment` | `S_TimeSegment` | `-` | `Runtime Metrics` | Active time segment |
|
||||
|
||||
### Internal (Private / Protected, No Expose)
|
||||
|
||||
| Variable | Type | Default | Category | Description |
|
||||
|----------|------|---------|----------|-------------|
|
||||
| `SessionStartTime` | `Float` | `0.0` | `Timing` | Game time when session started |
|
||||
| `CachedEventHistory` | `Array<S_BehaviourEvent>` | `[]` | `History` | Full event history (if tracking enabled) |
|
||||
| `LastPosition` | `Vector` | `(0,0,0)` | `Tracking` | Previous frame position for distance calc |
|
||||
| `SnapshotTimerHandle` | `FTimerHandle` | `-` | `Timing` | Timer for automatic snapshots |
|
||||
|
||||
### Replicated (if multiplayer)
|
||||
|
||||
| Variable | Type | Condition | Description |
|
||||
|----------|------|-----------|-------------|
|
||||
| `CurrentSnapshot` | `S_MetricsSnapshot` | `Replicated` | Synced session metrics |
|
||||
|
||||
---
|
||||
|
||||
## 4. Functions
|
||||
|
||||
### Public Functions
|
||||
|
||||
#### `StartSession` → `void`
|
||||
- **Description:** Begins a new metrics tracking session. Resets snapshot.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `SegmentLabel` | `String` | Label for initial segment (e.g. "Chapter 1") |
|
||||
- **Blueprint Authority:** Any
|
||||
- **Flow:**
|
||||
1. Set SessionStartTime = Current Game Time
|
||||
2. Reset CurrentSnapshot to zero values
|
||||
3. Initialize CurrentSegment with label and start time
|
||||
4. Set bIsTrackingSession = true
|
||||
5. Start snapshot timer
|
||||
6. Fire OnSessionStarted
|
||||
|
||||
#### `EndSession` → `S_MetricsSnapshot`
|
||||
- **Description:** Ends the tracking session and returns final snapshot.
|
||||
- **Parameters:** None
|
||||
- **Flow:**
|
||||
1. Finalize CurrentSegment end time
|
||||
2. Finalize CurrentSnapshot.PlayTime
|
||||
3. Add CurrentSegment to TimeSegmentHistory
|
||||
4. Set bIsTrackingSession = false
|
||||
5. Clear snapshot timer
|
||||
6. Fire OnSessionEnded
|
||||
7. Return CurrentSnapshot
|
||||
|
||||
#### `RecordEvent` → `void`
|
||||
- **Description:** Records a single behaviour event into history and updates snapshot.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `EventType` | `E_MetricEventType` | Type of event |
|
||||
| `ContextTag` | `GameplayTag` | Filtering tag |
|
||||
| `FloatValue` | `Float` | Numeric payload |
|
||||
| `StringValue` | `String` | Text payload |
|
||||
- **Flow:**
|
||||
1. If not bIsTrackingSession: return
|
||||
2. Create S_BehaviourEvent with current timestamp
|
||||
3. If bTrackDetailedEvents and event count < MaxEventHistory:
|
||||
- Add event to CachedEventHistory
|
||||
4. Update CurrentSnapshot based on EventType:
|
||||
- Death: TimesDied++
|
||||
- HideStarted: TimesHid++
|
||||
- ItemCollected: ItemsCollected++, add name to UniqueItemsCollected
|
||||
- EnemyEncountered: EnemiesEncountered++
|
||||
- EnemyEvaded: EnemiesEvaded++
|
||||
- StressPeakReached: StressPeakCount++, update PeakStressTier if higher
|
||||
- NarrativeEvent: NarrativeBeatsCompleted++
|
||||
- AreaExplored: add to AreasExplored
|
||||
- CombatEvent: CombatEvents++
|
||||
5. Fire OnEventRecorded
|
||||
|
||||
#### `RecordDistanceTraveled` → `void`
|
||||
- **Description:** Called by movement system or tick to log distance.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `DistanceDelta` | `Float` | Distance moved this frame |
|
||||
| `bWasSprinting` | `Boolean` | Whether player was sprinting |
|
||||
- **Flow:**
|
||||
1. If bWasSprinting: CurrentSnapshot.TotalDistanceSprinted += Delta
|
||||
2. Else: CurrentSnapshot.TotalDistanceWalked += Delta
|
||||
|
||||
#### `GetMetricsSnapshot` → `S_MetricsSnapshot`
|
||||
- **Description:** Returns a copy of the current live snapshot.
|
||||
- **Parameters:** None
|
||||
- **Flow:** Return CurrentSnapshot
|
||||
|
||||
#### `GetEventHistory` → `Array<S_BehaviourEvent>`
|
||||
- **Description:** Returns recorded event history.
|
||||
- **Parameters:**
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `bFiltered` | `Boolean` | If true, apply filter |
|
||||
| `FilterType` | `E_MetricEventType` | Only return events of this type |
|
||||
- **Flow:**
|
||||
1. If bFiltered: return filtered CachedEventHistory by FilterType
|
||||
2. Else: return CachedEventHistory
|
||||
|
||||
#### `GetPlayTimeFormatted` → `String`
|
||||
- **Description:** Returns total play time as formatted string (HH:MM:SS).
|
||||
- **Parameters:** None
|
||||
- **Flow:** Format SecondsToTimeString(SessionDuration)
|
||||
|
||||
### Protected / Private Functions
|
||||
|
||||
#### `OnTickDistance (Tick)` → `void`
|
||||
- **Description:** Each tick, compute distance from LastPosition. Only active when tracking.
|
||||
- **Flow:**
|
||||
1. If not bIsTrackingSession: return
|
||||
2. Get current actor location
|
||||
3. Delta = Distance(LastPosition, CurrentLocation)
|
||||
4. If Delta < 10.0: skip (noise threshold)
|
||||
5. Check movement mode from BPC_MovementStateSystem
|
||||
6. Call RecordDistanceTraveled(Delta, bWasSprinting)
|
||||
7. Update LastPosition = CurrentLocation
|
||||
|
||||
#### `CreateSnapshot` → `void`
|
||||
- **Description:** Timer callback to create periodic snapshots.
|
||||
- **Flow:**
|
||||
1. Copy CurrentSnapshot as snapshot
|
||||
2. Finalize CurrentSegment end time
|
||||
3. Store snapshot delta in CurrentSegment.MetricsDelta
|
||||
4. Add CurrentSegment to TimeSegmentHistory
|
||||
5. Start new CurrentSegment
|
||||
|
||||
---
|
||||
|
||||
## 5. Event Dispatchers
|
||||
|
||||
| Dispatcher | Parameters | Bind Access | Description |
|
||||
|------------|-----------|-------------|-------------|
|
||||
| `OnSessionStarted` | `String SegmentLabel` | `Public` | Fired when a tracking session begins |
|
||||
| `OnSessionEnded` | `S_MetricsSnapshot FinalSnapshot` | `Public` | Fired when a tracking session ends |
|
||||
| `OnEventRecorded` | `S_BehaviourEvent Event` | `Public` | Fired each time an event is recorded |
|
||||
| `OnMetricsThresholdReached` | `E_MetricEventType EventType`, `int CurrentValue`, `int Threshold` | `Public` | Fired when a metric crosses a threshold |
|
||||
| `OnSnapshotCreated` | `S_TimeSegment Segment` | `Public` | Fired when a periodic snapshot is written |
|
||||
|
||||
---
|
||||
|
||||
## 6. Overridden Events / Custom Events
|
||||
|
||||
### Event: `BeginPlay`
|
||||
- **Description:** Cache owner reference, start session if game has started.
|
||||
- **Flow:**
|
||||
1. Get owning pawn
|
||||
2. Bind to GI_GameFramework.OnGamePhaseChanged
|
||||
3. If game phase is Playing: StartSession("Initial")
|
||||
|
||||
### Custom Event: `OnGamePhaseChangedHandler`
|
||||
- **Description:** Auto-start/stop session based on game phase.
|
||||
- **Flow:**
|
||||
1. If NewPhase == Playing: StartSession("Resume")
|
||||
2. If NewPhase == Menu / Transition / Ended: EndSession()
|
||||
|
||||
---
|
||||
|
||||
## 7. Blueprint Graph Logic Flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[RecordEvent called] --> B{Is session active?}
|
||||
B -->|No| C[Return]
|
||||
B -->|Yes| D[Build S_BehaviourEvent]
|
||||
D --> E{Has history space?}
|
||||
E -->|Yes| F[Append to CachedEventHistory]
|
||||
E -->|No| G[Skip history store]
|
||||
G --> H[Update CurrentSnapshot counters]
|
||||
F --> H
|
||||
H --> I{Threshold crossed?}
|
||||
I -->|Yes| J[Fire OnMetricsThresholdReached]
|
||||
I -->|No| K[Fire OnEventRecorded]
|
||||
J --> K
|
||||
|
||||
L[Tick: RecordDistanceTraveled] --> M[Get current location]
|
||||
M --> N[Distance from LastPosition]
|
||||
N --> O{Delta > 10 cm?}
|
||||
O -->|No| P[Skip]
|
||||
O -->|Yes| Q[Update snapshot distance]
|
||||
Q --> R[Set LastPosition]
|
||||
|
||||
S[SnapshotTimer fires] --> T[Copy CurrentSnapshot]
|
||||
T --> U[Write segment to history]
|
||||
U --> V[Fire OnSnapshotCreated]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Communication Matrix
|
||||
|
||||
| Who Talks | How | What Is Sent |
|
||||
|-----------|-----|-------------|
|
||||
| `BPC_PlayerMetricsTracker` | `Direct call (other)` | `GetMetricsSnapshot` -> `BPC_AdaptiveDifficulty` |
|
||||
| `BPC_PlayerMetricsTracker` | `Dispatcher` | `OnEventRecorded` -> `BPC_AdaptiveDifficulty` (real-time feed) |
|
||||
| `BPC_PlayerMetricsTracker` | `Dispatcher` | `OnSessionEnded` -> `WBP_StatsScreen` (end-game display) |
|
||||
| `BPC_PlayerMetricsTracker` | `Dispatcher` | `OnMetricsThresholdReached` -> `GM_CoreGameMode` (achievements) |
|
||||
| `BPC_HealthSystem` | `Direct` | Calls `RecordEvent(Death)` on player death |
|
||||
| `BPC_HidingSystem` | `Direct` | Calls `RecordEvent(HideStarted/HideEnded)` |
|
||||
| `BPC_StressSystem` | `Direct` | Calls `RecordEvent(StressPeakReached)` |
|
||||
| `GI_GameFramework` | `Dispatcher` | `OnGamePhaseChanged` -> `BPC_PlayerMetricsTracker` (start/stop) |
|
||||
| `BPC_NarrativeManager` | `Direct` | Calls `RecordEvent(NarrativeEvent)` on story beat |
|
||||
| `BPC_WeaponSystem` | `Direct` | Calls `RecordEvent(CombatEvent)` on attack |
|
||||
| `BPC_InventoryManager` | `Direct` | Calls `RecordEvent(ItemCollected)` on pickup |
|
||||
|
||||
---
|
||||
|
||||
## 9. Validation / Testing Checklist
|
||||
|
||||
- [ ] StartSession resets all counters to zero
|
||||
- [ ] Distance tracking correctly accumulates walking vs sprinting
|
||||
- [ ] Events are capped at MaxEventHistory and do not overflow
|
||||
- [ ] Snapshot timer creates periodic snapshots at correct interval
|
||||
- [ ] EndSession returns accurate final snapshot
|
||||
- [ ] Filtered event history returns only matching event types
|
||||
- [ ] Threshold dispatchers fire at correct metric values
|
||||
- [ ] I_Persistable save/load preserves snapshot across sessions
|
||||
- [ ] Edge case: Rapid events within one frame all recorded
|
||||
- [ ] Edge case: Session ends with zero events — snapshot is valid zero-state
|
||||
- [ ] Edge case: Distance tracking ignores teleportation (delta > 5000 cm)
|
||||
- [ ] Formatted play time displays correctly for all durations
|
||||
|
||||
---
|
||||
|
||||
## 10. Reuse Notes
|
||||
|
||||
- The metrics snapshot is designed to be consumed by `BPC_AdaptiveDifficulty` for dynamic difficulty adjustment.
|
||||
- Event history can be serialised to disk via `I_Persistable` for cross-session analytics.
|
||||
- The same `BPC_PlayerMetricsTracker` can be attached to any character blueprint — not just the player.
|
||||
- Threshold values are not defined here; they live in `DA_GameDifficulty` or `DA_AdaptiveSettings`.
|
||||
- For debugging, call `GetEventHistory(false)` to dump all events to log.
|
||||
|
||||
---
|
||||
|
||||
*Blueprint Spec: Player Metrics Tracker. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||
Reference in New Issue
Block a user