194 lines
8.1 KiB
Markdown
194 lines
8.1 KiB
Markdown
# BPC_PlaystyleClassifier — Playstyle Classifier
|
||
|
||
## Blueprint Spec — UE 5.5–5.7
|
||
|
||
---
|
||
|
||
### Parent Class
|
||
`ActorComponent`
|
||
|
||
### Dependencies
|
||
- **Requires:** [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) — Reads behaviour metrics (AggressionScore, CautionScore, ExplorationScore)
|
||
- **Required By:** [`BPC_AdaptiveEnvironmentDirector`](BPC_AdaptiveEnvironmentDirector.md) — Subscribes to `OnPlaystyleChanged`
|
||
- **Required By:** [`BPC_BehaviourVariantSelector`](../09-ai/BPC_BehaviourVariantSelector.md) — AI adapts to playstyle
|
||
- **Required By:** [`BPC_PacingDirector`](BPC_PacingDirector.md) — Pacing adjusted by playstyle
|
||
- **Engine/Plugin Requirements:** GameplayTags (for playstyle tag output), Timer system
|
||
|
||
### Purpose
|
||
Analyses player behaviour metrics from BPC_PlayerMetricsTracker and classifies their playstyle in real time. Outputs a dominant playstyle tag (Aggressive, Cautious, Explorer, etc.) that feeds into the adaptive environment director and AI behaviour systems.
|
||
|
||
---
|
||
|
||
## 1. Enums
|
||
|
||
```text
|
||
Enum Name: E_PlaystyleTag
|
||
(DisplayName = "Playstyle Tag")
|
||
|
||
Values:
|
||
Aggressive = 0 // Player favours combat and direct confrontation
|
||
Cautious = 1 // Player favours hiding, sneaking, and avoidance
|
||
Explorer = 2 // Player favours off-path exploration and discovery
|
||
Passive = 3 // Player avoids interaction, moves slowly
|
||
Speedrunner = 4 // Player rushes through content, skips optional
|
||
Completionist = 5 // Player exhaustively searches every area
|
||
```
|
||
|
||
*Values are used as Gameplay Tags for routing to adaptive systems.*
|
||
|
||
---
|
||
|
||
## 2. Structs
|
||
|
||
*No new structs defined. Reads from [`S_BehaviourEvent`](../02-player/15_BPC_PlayerMetricsTracker.md).*
|
||
|
||
---
|
||
|
||
## 3. Variables
|
||
|
||
### Configuration (Instance Editable, Expose On Spawn)
|
||
|
||
| Variable | Type | Default | Category | Description |
|
||
|----------|------|---------|----------|-------------|
|
||
| `ClassificationInterval` | Float | 15.0 | Classifier | Seconds between playstyle re-evaluations |
|
||
| `AggressionWeight` | Float | 1.0 | Classifier | Multiplier for aggression score influence |
|
||
| `CautionWeight` | Float | 1.0 | Classifier | Multiplier for caution score influence |
|
||
| `ExplorationWeight` | Float | 1.0 | Classifier | Multiplier for exploration score influence |
|
||
| `ClassificationThreshold` | Float | 10.0 | Classifier | Minimum score delta required to change classification |
|
||
|
||
### Internal (Private / Protected, No Expose)
|
||
|
||
| Variable | Type | Default | Category | Description |
|
||
|----------|------|---------|----------|-------------|
|
||
| `CurrentPlaystyleTag` | GameplayTag | Explorer | Internal | Dominant playstyle classification |
|
||
| `PreviousPlaystyleTag` | GameplayTag | None | Internal | Last classification for change detection |
|
||
| `ClassificationTimer` | TimerHandle | — | Internal | Timer for periodic re-evaluation |
|
||
|
||
---
|
||
|
||
## 4. Functions
|
||
|
||
### Public Functions
|
||
|
||
#### `ClassifyPlaystyle` → `GameplayTag`
|
||
- **Description:** Reads metrics from BPC_PlayerMetricsTracker, calculates weighted scores, and determines the dominant playstyle tag.
|
||
- **Parameters:** None
|
||
- **Blueprint Authority:** Any
|
||
- **Flow:** Get metrics → multiply by weights → find highest scoring playstyle → return tag
|
||
|
||
#### `GetCurrentPlaystyle` → `GameplayTag`
|
||
- **Description:** Returns the current dominant playstyle tag.
|
||
- **Parameters:** None
|
||
- **Blueprint Authority:** Any
|
||
|
||
#### `GetPlaystyleScores` → `Map (GameplayTag → Float)`
|
||
- **Description:** Returns the weighted score for each playstyle tag for external analysis.
|
||
- **Parameters:** None
|
||
- **Blueprint Authority:** Any
|
||
|
||
#### `ForcePlaystyle` → `void`
|
||
- **Description:** Overrides the playstyle classification for debugging or scripted sequences.
|
||
- **Parameters:**
|
||
| Param | Type | Description |
|
||
|-------|------|-------------|
|
||
| `PlaystyleTag` | GameplayTag | Forced classification |
|
||
- **Blueprint Authority:** Any
|
||
|
||
---
|
||
|
||
## 5. Event Dispatchers
|
||
|
||
| Dispatcher | Parameters | Bind Access | Description |
|
||
|------------|-----------|-------------|-------------|
|
||
| `OnPlaystyleChanged` | OldTag: GameplayTag, NewTag: GameplayTag | Public | Playstyle classification changed |
|
||
| `OnPlaystyleReclassified` | NewTag: GameplayTag | Public | Periodic re-evaluation completed (fires even if unchanged) |
|
||
|
||
---
|
||
|
||
## 6. Overridden Events / Custom Events
|
||
|
||
### Event: `BeginPlay`
|
||
- **Description:** Starts the classification timer. Subscribes to metrics changes if needed.
|
||
- **Flow:**
|
||
1. Start ClassificationTimer with ClassificationInterval repeating
|
||
2. Perform initial classification immediately
|
||
|
||
### Event: `TickClassification`
|
||
- **Description:** Timer callback. Runs ClassifyPlaystyle and broadcasts if changed.
|
||
- **Flow:**
|
||
1. Read BPC_PlayerMetricsTracker.AggressionScore, CautionScore, ExplorationScore
|
||
2. Calculate weighted scores
|
||
3. Determine dominant playstyle tag (highest weighted score)
|
||
4. If new tag differs from CurrentPlaystyleTag AND score delta exceeds ClassificationThreshold:
|
||
- Set PreviousPlaystyleTag = CurrentPlaystyleTag
|
||
- Set CurrentPlaystyleTag = new tag
|
||
- Broadcast OnPlaystyleChanged
|
||
5. Always broadcast OnPlaystyleReclassified
|
||
|
||
---
|
||
|
||
## 7. Blueprint Graph Logic Flow
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[BeginPlay] --> B[Start ClassificationTimer]
|
||
B --> C[Timer fires each ClassificationInterval seconds]
|
||
C --> D[Read AggressionScore from BPC_PlayerMetricsTracker]
|
||
D --> E[Read CautionScore from BPC_PlayerMetricsTracker]
|
||
E --> F[Read ExplorationScore from BPC_PlayerMetricsTracker]
|
||
F --> G[Calculate weighted scores:]
|
||
G --> H[AggressionScore * AggressionWeight]
|
||
H --> I[CautionScore * CautionWeight]
|
||
I --> J[ExplorationScore * ExplorationWeight]
|
||
J --> K[Also factor in: DeathCount, HideCount, ItemsCollected, DistanceTravelled]
|
||
K --> L[Map derived values to playstyle tags]
|
||
L --> M[Find highest weighted playstyle tag]
|
||
M --> N{New tag != CurrentPlaystyleTag?}
|
||
N -->|Yes| O{Score delta >= ClassificationThreshold?}
|
||
O -->|Yes| P[Broadcast OnPlaystyleChanged]
|
||
O -->|No| Q[Keep current classification]
|
||
N -->|No| Q
|
||
P --> R[Update CurrentPlaystyleTag]
|
||
R --> S[Broadcast OnPlaystyleReclassified]
|
||
Q --> S
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Communication Matrix
|
||
|
||
| Who Talks | How | What Is Sent |
|
||
|-----------|-----|-------------|
|
||
| [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) | Direct read | `AggressionScore`, `CautionScore`, `ExplorationScore`, `DeathCount`, `HideCount` |
|
||
| [`BPC_AdaptiveEnvironmentDirector`](BPC_AdaptiveEnvironmentDirector.md) | Dispatcher (`OnPlaystyleChanged`) | `OldTag: GameplayTag`, `NewTag: GameplayTag` |
|
||
| [`BPC_BehaviourVariantSelector`](../09-ai/BPC_BehaviourVariantSelector.md) | Dispatcher (`OnPlaystyleChanged`) | `NewTag: GameplayTag` |
|
||
| [`BPC_PacingDirector`](BPC_PacingDirector.md) | Direct read | `CurrentPlaystyleTag` |
|
||
| [`SS_AchievementSystem`](../11-meta/SS_AchievementSystem.md) | Dispatcher (`OnPlaystyleChanged`) | Playstyle tag for adaptive achievement checks |
|
||
|
||
---
|
||
|
||
## 9. Validation / Testing Checklist
|
||
|
||
- [ ] E_PlaystyleTag enum has all 6 values defined
|
||
- [ ] ClassificationInterval timer fires at correct interval
|
||
- [ ] Weighted scores correctly calculated from metrics
|
||
- [ ] OnPlaystyleChanged fires when classification changes significantly
|
||
- [ ] ClassificationThreshold prevents minor fluctuations from causing changes
|
||
- [ ] ForcePlaystyle overrides the automatic classification
|
||
- [ ] Edge case: BPC_PlayerMetricsTracker not present → default to Explorer
|
||
- [ ] Edge case: all scores zero → classify as Passive
|
||
- [ ] Edge case: timer fires during cutscene → classification still runs, adapt on exit
|
||
|
||
---
|
||
|
||
## 10. Reuse Notes
|
||
|
||
- AggressionWeight, CautionWeight, ExplorationWeight can be tuned per project to prioritise different behaviours
|
||
- ClassificationThreshold prevents "flickering" between similar playstyles on minor metric changes
|
||
- For non-adaptive games, set ClassificationInterval to 0 to disable or force a fixed playstyle
|
||
- Extend with new E_PlaystyleTag values per project (e.g., "Pacifist", "Collector")
|
||
- Classification is player-pawn-relative — in co-op, each player has their own classifier
|
||
|
||
---
|
||
|
||
*Specification based on Master Section 9.1, line 2699.* |