This repository contains the Unreal Pulse Physiology Engine plugin. You can simply copy the PulsePhysiologyEngine folder from here and drop it in the Plugins directory in your application. Along with the Plugin, there is an example project in each of the `e` folders, where the number is the unreal version the project is for. The PulsePhysiologyEngine plugin works with any of these versions. The example project provides a vitals monitor. In order for it to draw waveform data on our example vitals monitor, we are using the Kantan Charts plugin. It's not avaiable for any of the unreal 5 versions. You can grap it [here](https://data.kitware.com/api/v1/item/657caab48c54f378b99229ff/download), and also drop it into the Plugins folder in any of the example projects provided here. Simply run the push_plugin_to_examples.bat to push a copy of the PulsePhysiologyEngine folder into each of the examples. Then you can open and run any of the `PulseExample.uproject` files. If the project is not opening, you can build it via command line to address any issues using: ```bash "C:/Program Files/Epic Games/UE_5.3/Engine/Build/BatchFiles/Build.bat" -projectfiles -project="C:/Programming/Pulse/unreal/e5.3/PulseExample.uproject" -game -rocket -progress ``` ### Pulse The actual Pulse Engine is provided in the [Plugin](/Plugins/PulsePhysiologyEngine/Source/ThirdParty) as headers and libraries. Currently we are only providing Win64 libraries, but creating linux/macos/android library versions would not be hard to add in. To create the libraries, just build Pulse per the instructions in our [engine repository](https://gitlab.kitware.com/physiology/engine) with the extra step of checking/enabling the `Pulse_AS_SHARED` option. Then you can copy the `Pulse.dll` from the `install/bin`, the `Pulse.lib` from the `install/lib` and the `pulse` folder from the `install/include` and replace the contents in the PulseSDK folder. Please familiarize yourself with the [Pulse API](https://pulse.kitware.com/physeng.html) to use this Plugin ### Blueprint Design Our plugin provides a Blueprint component with the basic functionality needed to use Pulse - Initialization methods and Events - Simulation Step Event - Patient Death Event - Simulation Reset Event The intended use of this component, is for you to extend it and add events and methods for using Pluse in your own application. Our plugin provides an example extention `ExamplePulseComponent` that demonstrates - Using Pulse data to drive a vitals monitor - This shows how to create, populate, and expose an Blueprint data structure with Pulse data - Exposing a simple method that simulates an IED explosion near our patient - This shows how to call actions to affect the simulated patient - Checks for patient death due to wounds recieved from the explosion - How to examine physiologic data from pulse to determine when to stop simulating due to death ### Initialization The Pulse component can be initialized in the `BeginPlay` Event. Supported initialization schemes are: - Load a state file (json or binary) from disk or from your game resources - Load a patient file (json or binary) from disk or from your game resources - Note this will require the engine to stabilize (~1-2m of time) in the BeginPlay Event It is highly recommended to load states. You can generate and test all your states offline using our [Pulse Exporer](https://gitlab.kitware.com/physiology/explorer/-/wikis/home). The Pulse component will broadcast an `On Simulation Ready` event once the engine is initialized. You can use this event to setup any logic in your Blueprint Once initialized, connect the component to the Activate method to complete initialization. ### Simulation Step The Pulse component uses the `TickComponent` method to advance time. Pulse simulates in steps of 0.02 seconds. This is generally slower than the game frame rate. Pulse will simulate a 0.02 seconds time step when the `TickComponent` delta time is greater than 0.02s. Only during those frames will Pulse calculate. During `TickComponent`, Pulse will: - Simulate physiology in increments of 0.02 seconds - Call the `IsPatientDead` method - The base `IsPatientDead` method, always returns false, its up to you to override this and implement your own death check - If true, will broadcast the `OnPatientDeath` Event, then the `OnSimulationReset` Event - Broadcast the `OnSimulationStep` Event (only if the patient is still alive) - In your game Blueprint, listen for the `OnSimulationStep` event and pefrom any logic using the Pulse API. #### Providing Actions to Pulse Simple action structures are used to interact with the patient simulated by Pulse. Actions are the building blocks used to construct various game specific insults and interventions that occur to your patient. Each actions has 1 or more fields that describe its impacts to the simulation. Usually, in simulating a realisitic event, one or more actions are combined to properly transate a real world event to Pulse. In our experiance, constructing these events using individual action Blueprints can be difficult and complicated. We recommend creating `UFUNCTION(BlueprintPure)` methods on your derived Pulse component to encapsulate Pulse action logic using the Pulse API. You can then easily call these methods in your Blueprint. See the UExamplePulseComponent::IED_Explosion() method for an example. Using this approach, you can also design your own encapsulation logic specific to your game. For example, you may want to add a distance parameter to your IED_Explosion method that scales the severities of injury based on how far your player is from the explosion. The scaling logic can be implemented in the method rather than the Blueprint. Of course you could also expose a `UFUNCTION(BlueprintPure)` for each action you intend on using and implement that logic in your Blueprint. Please join our [discoure](https://discourse.kitware.com/c/pulse-physiology-engine/5) to discuss this topic further #### Pulling Data from Pulse Pulse computes thousands of physiologic data each time step. It is unrealistic to expose all of these data properties individually though a Blueprint. The best way to expose data via a Blueprint in your game is to declare one or more structs as `USTRUCT(BlueprintType)`. These structures will be available for use in your Blueprint, and they are much easier to populate in a custom method using the Pulse API. For each USTRUCT you create, simply add an accompanying ` UFUNCTION(BlueprintPure)` method that uses the Pulse API to populate the USTRUCT. Call this method in your Blueprint from the `OnSimulationStep` Event, you can then pass that struct to other Blueprint components. #### Death Checking Identifying patient death is highly contextual. Depending on what has happened to the patient, you will identify death differently. In our provided ExamplePulseComponent, we demonstrate several possible death checks for the 3 main causes of death on the battle field - Hemorrhage - Pneumothorax - Airway Obstruction Note this is an example, times/events may vary depending on scenario and your Subject Matter Expert ### Resetting the Engine At anytime, you can reset the engine component. Resetting can be various things, such as: - Stopping simulation altogether, i.e. no more patient - Loading up a different state and continuing with game logic You can execute any Blueprint logic via the `OnSimulationReset` Event