Skip to content

Commit 9ccfafd

Browse files
committed
updated better again
1 parent be49743 commit 9ccfafd

File tree

5 files changed

+656
-276
lines changed

5 files changed

+656
-276
lines changed
Lines changed: 149 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,164 @@
1-
# Breathing Dots
1+
# 🫁 Breathing Dot Indicators
22

3-
The "Breathing Dots" are a key visual feedback mechanism within the Shock System, providing immediate and intuitive information about physiological states or interactive points on the anatomical model. (All the elements used for it can be found inside Unreal Engine a folder called ButtonsForShocks)
3+
## Overview
44

5-
## Concept and Purpose
5+
Breathing Dots are dynamic UI widgets that visually mark **diagnosis-specific anatomical points** (e.g., a faint wrist pulse during Cardiogenic Shock). They:
6+
- Animate with a subtle "breathing" pulse.
7+
- Follow a 3D socket on the anatomical model.
8+
- Display an **info panel** on hover or click with title, description, and optional image.
9+
- Are **spawned dynamically** based on diagnosis configuration, and removed on diagnosis change.
610

7-
These dots are dynamic UI elements that visually change (e.g., color, size, opacity, animation) to signify:
8-
* **Vital Signs:** Representing a pulse, respiration rate, or other continuous physiological data in a visually engaging manner.
9-
* **Interactive Hotspots:** Guiding the user to specific areas that can be interacted with to trigger events or apply medical interventions.
10-
* **Status Indicators:** Showing the current "health" or state of a particular organ or system within a specific diagnosis.
11+
They are implemented via:
12+
- A C++ data class: `UShockIndicator`
13+
- A Blueprint widget: `UBreathingDotWidget` (WBP_BreathingDot)
14+
- Spawned/managed by: `UCPP_SimulationManager`
1115

12-
## Implementation Details
16+
---
17+
18+
## 🧬 System Architecture
19+
20+
### Data Source: `UShockIndicator`
21+
22+
Each indicator is described by:
23+
24+
| Field | Description |
25+
|--------------|----------------------------------------------------------|
26+
| `SocketName` | Bone/socket name the indicator should follow (e.g. `"Wrist"`) |
27+
| `Title` | Title text for the info panel |
28+
| `Description`| Tooltip-style detailed description |
29+
| `Image` | Optional image/texture shown in the info panel |
30+
31+
Each `UCPP_Diagnosis` has an array:
32+
```cpp
33+
TArray<UShockIndicator*> Indicators;
34+
```
35+
36+
These are registered during diagnosis building using:
37+
38+
```cpp
39+
.AddIndicator("Wrist", "Pulse", "Faint pulse detected", IconTexture)
40+
```
41+
42+
---
43+
44+
### Runtime Spawning Logic
45+
46+
Inside `UCPP_SimulationManager::ChangeDiagnosis()`:
47+
48+
1. All previously spawned breathing dots are destroyed.
49+
2. The `Indicators` array from the selected diagnosis is retrieved.
50+
3. For each `UShockIndicator`, a `UBreathingDotWidget` is:
51+
- Created dynamically
52+
- Anchored to the corresponding bone/socket
53+
- Initialized via `SetDescription()` or `InitializeFromIndicator()` using `ProcessEvent`
54+
- Tracked in a local `TArray<UUserWidget*> SpawnedWidgets` for cleanup
55+
56+
---
57+
58+
### Widget: `UBreathingDotWidget`
59+
60+
This UMG Blueprint handles:
61+
- **3D following**: Converts world position of the socket to screen position.
62+
- **Visual pulsing**: Looping animation to draw user attention.
63+
- **Info Panel logic**:
64+
- Tooltip-style display on hover or click
65+
- Receives title, description, and image from the indicator
66+
67+
Functions called from C++:
68+
- `InitializeFromIndicator()` or `SetDescription(FString Title, FString Desc, UTexture2D* Image)`
69+
- Toggle visibility or appearance based on current diagnosis
70+
71+
---
72+
73+
## 🔁 Example Builder Usage
1374

14-
Breathing Dots are managed by the `UCPP_SimulationManager` and defined within each `UCPP_Diagnosis` profile.
75+
Here’s how a diagnosis defines its breathing dots:
1576

16-
### Core Classes: `UShockIndicator` and `UBreathingDotWidget`
77+
```cpp
78+
DiagnosisBuilder(this)
79+
.SetName("Cardiogenic Shock")
80+
.SetDescription("Heart failure causing reduced blood circulation")
81+
.AddIndicator("Wrist", "Weak Pulse", "Pulse is faint and irregular", WristIcon)
82+
.AddIndicator("Chest", "Chest Discoloration", "Bluish skin near heart", ChestIcon)
83+
.Build();
84+
```
1785
18-
1. **`UShockIndicator` (Base Class)**:
19-
* This is the base C++ class (`Simulation/Diagnosis/Indicators/ShockIndicator.h`) that conceptually represents a "shock indicator." It holds common data like the `BoneName` it should attach to, and potentially the `Title`, `Description`, and `Image` used for a tooltip or pop-up associated with the dot.
20-
* It is a `UObject` and managed by the `UCPP_Diagnosis` to define the *properties* of an indicator.
21-
* It includes a `GetScreenPosition` method to project the 3D bone location to a 2D screen position.
22-
* It also contains a private `DotWidgetRef` which is set when the widget is spawned by the `SimulationManager`.
86+
---
2387
24-
2. **`UBreathingDotWidget` (UMG Widget)**:
25-
* Defined in `BreathingDotWidget.h`. This is a `UUserWidget` subclass, meaning it's a Blueprint-implementable UI widget.
26-
* It contains a `UPROPERTY(meta = (BindWidgetAnim), Transient) UWidgetAnimation* ImageBreathing;` which is a crucial part. This property is designed to be linked to an animation created in the Unreal Engine UMG editor (e.g., in `WBP_BreathingDotWidget`). This animation is what gives the "breathing" pulse effect.
27-
* The `bIsRuntimeSpawned` flag helps distinguish widgets spawned by the simulation from those placed manually in the editor.
28-
* Its `NativeConstruct()` method is where you would typically start the `ImageBreathing` animation or hide editor-only debugging UI.
88+
## 🎯 Integration Map
2989
30-
### Integration with `UCPP_Diagnosis` and `UCPP_SimulationManager`
90+
| Class | Responsibility |
91+
|---------------------------|------------------------------------------------|
92+
| `UShockIndicator` | Stores metadata for one indicator |
93+
| `UCPP_Diagnosis` | Holds multiple indicators |
94+
| `UCPP_SimulationManager` | Spawns/removes widgets based on diagnosis |
95+
| `UBreathingDotWidget` | UMG widget shown on-screen for each dot |
96+
97+
---
3198
32-
* **Defining in `UCPP_Diagnosis`**: Each `UCPP_Diagnosis` object holds an array of `UShockIndicator*` (`UPROPERTY() TArray<UShockIndicator*> Indicators;`). This array is populated using the `AddIndicator()` method of the `DiagnosisBuilder`.
33-
```cpp
34-
// Example from DiagnosisBuilder (conceptual)
35-
// AddIndicator(FName BoneName, const FText& Title, const FText& Description, UTexture2D* Image);
36-
DiagnosisBuilder(this)
37-
.SetName("Cardiogenic Shock")
38-
// ... other settings
39-
.AddIndicator(FName("HeartSocket"), FText::FromString("Cardiac Pulse"), FText::FromString("Monitors heart rate and rhythm."), HeartIconTexture)
40-
.Build();
41-
```
42-
* **Spawning by `UCPP_SimulationManager`**: When a diagnosis is changed or started, the `UCPP_SimulationManager` calls `SpawnIndicators(const UCPP_Diagnosis& Diagnosis)`. This function is responsible for:
43-
1. Iterating through the `Indicators` array of the active `UCPP_Diagnosis`.
44-
2. For each `UShockIndicator`, it creates an instance of `UBreathingDotWidgetClass` (a `TSubclassOf<UBreathingDotWidget>` exposed in `UCPP_SimulationManager`'s properties).
45-
3. It then attaches these `UBreathingDotWidget` instances to the `TargetSkeletalMesh` (e.g., `BP_Bones`) at the specified `BoneName` (socket) from the `UShockIndicator`, likely converting the 3D socket location to a 2D screen position for the UMG widget.
46-
4. The spawned widgets are added to the `ActiveIndicators` array in `UCPP_SimulationManager` for later management (e.g., `ClearActiveIndicators()`).
99+
## 🧪 Debugging Breathing Dots
47100
48-
## Customization and Best Practices
101+
### If dots don’t appear:
102+
- ✅ Check that `SocketName` exists on the skeletal mesh:
103+
```cpp
104+
SkeletalMesh->DoesSocketExist(FName("Wrist"))
105+
```
106+
- ✅ Log dot creation in C++:
107+
```cpp
108+
UE_LOG(LogTemp, Warning, TEXT("Spawning indicator for socket %s"), *SocketName.ToString());
109+
```
110+
- ✅ Use `DrawDebugSphere()` to confirm socket world location is valid.
49111
50-
* **Blueprint Customization**: `UBreathingDotWidget` is designed to be extended in Blueprint. You can customize its visual appearance, add more complex animations, and implement interactive logic directly within the Blueprint subclass (`WBP_BreathingDotWidget`).
51-
* **Material Parameters**: The "breathing" effect itself is often driven by a Material Instance. You can expose parameters in your material (e.g., `EmissiveColor`, `PulseSpeed`) that can be dynamically updated by C++ or Blueprint logic to change the dot's visual behavior based on real-time simulation data.
52-
* **Performance**: Be mindful of the number of active breathing dots, especially if they involve complex animations or many materials, to maintain good performance.
112+
### If info panel doesn’t show:
113+
- ✅ Confirm that `SetDescription()` or `InitializeFromIndicator()` is called.
114+
- ✅ Use breakpoints inside the Blueprint widget.
115+
- ✅ Ensure image is valid or pass `nullptr`.
53116
54117
---
55118
56-
For how these indicators fit into the broader system, see [[How Everything Connects at Runtime]].
119+
## 🔥 Advanced Features & Customization
120+
121+
### Add Conditional Visibility
122+
Inside `UBreathingDotWidget`, use Blueprint logic:
123+
- Only show info panel when hovered.
124+
- Auto-hide based on diagnosis-specific rules.
125+
- Support alternate states (e.g., blinking, fading).
126+
127+
### Override Appearance Per Dot
128+
Each indicator could eventually have:
129+
- A custom **color theme**
130+
- A **different pulse rate** or animation
131+
- Even **contextual behavior** depending on vital stats (BPM, blood volume)
132+
133+
Just extend `UShockIndicator` with new properties like:
134+
135+
```cpp
136+
FLinearColor DotColor;
137+
float PulseRateOverride;
138+
```
139+
140+
---
141+
142+
## ✅ Best Practices
143+
144+
- Keep socket names consistent with the skeletal mesh you're targeting.
145+
- Group related indicators close together in the builder for easy debugging.
146+
- Don’t hardcode dot spawn logic — always use `Indicators` array.
147+
- Use `nullptr` in `AddIndicator()` for optional image to avoid crashing.
148+
- Remove or destroy all dot widgets when diagnosis changes.
149+
150+
---
151+
152+
## 🧱 Example Use Case: Cardiogenic Shock
153+
154+
```cpp
155+
// In DiagnosisRegistery.cpp
156+
DiagnosisBuilder(this)
157+
.SetName("Cardiogenic Shock")
158+
.AddIndicator("Wrist", "Pulse", "Faint wrist pulse detected", WristIcon)
159+
.AddIndicator("Face", "Pale Skin", "Skin tone loss near eyes", nullptr)
160+
.SetBehavior(NewObject<UCardiogenicShockBehavior>(this))
161+
.Build();
162+
```
163+
164+
---

0 commit comments

Comments
 (0)