Skip to content
Tuxedo CatSTUDIO
Menu

Quickstart — Blueprint Usage

Create a named Stream once, keep using that Stream for the gameplay system that owns it, use Sequential calls for “next result” behavior or Keyed calls for stable semantic decisions, and centralize your game’s Stream IDs and Decision Keys in a small Blueprint Function Library.

This guide shows the simplest way to start using Probability Director in Blueprint and keep using it correctly as your game grows.

The main idea is:

Create a Stream once
|
v
Use that Stream for gameplay decisions
|
v
Keep using the same Stream while its owning GameInstance lives
|
v
Remove it only when that Stream's lifecycle is actually finished

Probability Director has no hidden default Stream.


For most Blueprint gameplay:

1. Create Stream
2. Check that creation succeeded
3. Use Sequential or Keyed operations
4. Check each operation's Status/Result
5. Keep using the same registered Stream
6. Remove Stream only when you genuinely no longer need it

You normally do not need to manually get a Probability Director subsystem Target.

Probability Director’s normal Blueprint nodes use the current World Context automatically.


Create long-lived gameplay Streams at a stable initialization point.

Good places include:

  • Your GameInstance-derived Blueprint initialization.
  • A persistent gameplay manager.
  • An authoritative system that clearly owns that Stream.

Avoid creating a new Stream every time you need one random result.

Game starts
|
+--> Create Stream: Combat.Damage
+--> Create Stream: Loot.DropTable
+--> Create Stream: World.Encounters

Then later:

Attack
-> use Combat.Damage
Open Chest
-> use Loot.DropTable
Encounter Check
-> use World.Encounters
Attack
-> Create Combat.Damage
-> Roll
-> Remove Combat.Damage
Attack
-> Create Combat.Damage
-> Roll
-> Remove Combat.Damage

A Stream is intended to have a meaningful gameplay lifetime.


3. Streams Survive Map Changes with the GameInstance

Section titled “3. Streams Survive Map Changes with the GameInstance”

Probability Director is GameInstance-scoped.

That means a registered Stream belongs to the current GameInstance rather than one individual map.

For a normal playthrough:

GameInstance
|
+--> Combat.Damage
+--> Loot.DropTable
+--> World.Encounters
Map A
-> uses those Streams
Map B
-> uses the same Streams

Do not recreate long-lived Streams simply because the player changed maps.


Use:

Create Stream

Example:

Create Stream
Stream ID:
Combat.Damage
Seed Source:
Secure
Decision Mode:
Sequential
Persistence Policy:
Ephemeral

Creation registers the Stream.

It does not roll or consume random output.


Setting Simple meaning
Stream ID What gameplay system owns this random source
Seed Source How the Stream begins
Decision Mode How random decisions are addressed
Persistence Policy What happens across save/load

Need Use
Normal fresh gameplay randomness Secure
Reproducible testing/debugging Fixed
Approved imported-state workflow Imported
Loot.DropTable
Seed Source: Secure

Use this for normal gameplay when you do not need a visible reproducible seed.

Test.Combat
Seed Source: Fixed
Fixed Seed: 12345

Use this when you want to reproduce the same compatible sequence.


The simplest rule is:

Your question Use
“Give me the next random result.” Sequential
“Give me the result for this exact gameplay decision.” Keyed

Create:

Stream:
Combat.Damage
Decision Mode:
Sequential

Then every damage roll uses that same Stream:

Attack 1
-> Resolve Sequential Numeric Dice Total
Attack 2
-> Resolve Sequential Numeric Dice Total
Attack 3
-> Resolve Sequential Numeric Dice Total

Sequential mode is sensitive to call order.

Adding another advancing random call can change later results.


Create:

Stream:
Loot.World
Decision Mode:
Keyed

For one chest:

Namespace:
Loot
Stable Subject ID:
Chest.Forest.0042
Decision:
Rarity
Occurrence:
0

Use that Decision Key with the Keyed operation.

The same compatible key intentionally gives the same deterministic result.


If one semantic subject needs several distinct deterministic results, change Occurrence.

Example:

Item pick Occurrence
First item 0
Second item 1
Third item 2

Do not expect Probability Director to increment a hidden Keyed counter for you.

There is no hidden Keyed occurrence cursor.


Good:

Namespace: Loot
Stable Subject ID: Chest.World01.Room12.Chest04
Decision: Rarity
Occurrence: 0

Avoid:

Stable Subject ID: BP_Chest_C_2147481250

or anything based on:

  • Pointer values.
  • Memory addresses.
  • Temporary Actor numbering.
  • Array positions that can change.

Use gameplay meaning, not process identity.


A simple Blueprint flow:

Create Stream
Stream ID: Combat.Damage
Seed Source: Secure
Decision Mode: Sequential
|
v
Make Standard Numeric Dice Pipeline
Standard Die: D20
Quantity: 1
|
v
Resolve Sequential Numeric Dice Total
Stream ID: Combat.Damage
Flat Modifier: +5
|
v
Check Status
|
+--> Success
|
+--> use final result

Do not use the output as if the operation succeeded until you check the returned status/result.


A typical Keyed flow:

Create Stream
Stream ID: Loot.World
Decision Mode: Keyed
|
v
Build Decision Key
Namespace: Loot
Stable Subject ID: Chest.Forest.0042
Decision: Rarity
Occurrence: 0
|
v
Call the Keyed operation
Stream ID: Loot.World
Decision Key: above
|
v
Check Status
|
+--> Success
|
+--> use result

Once:

Combat.Damage

is registered, keep using it.

You do not need to call Create Stream before every Roll.

A good ownership pattern is:

Initialization
-> Create Combat.Damage
Gameplay
-> use Combat.Damage hundreds/thousands of times
Shutdown / true lifecycle end
-> Remove Combat.Damage if needed

Use:

Has Stream

Example:

Has Stream
Stream ID: Combat.Damage
|
+--> True
| -> continue
|
+--> False
-> handle setup problem

Has Stream is useful for checking lifecycle problems.

Do not use it as a reason to silently create Streams everywhere in the project.

Prefer one clear owner.


Use:

Get Stream Configuration

This is useful when something behaves differently than expected.

For example:

I thought Loot.World was Keyed, but my Keyed calls are failing.

Check the actual registered configuration.

Look for:

  • Seed Source.
  • Decision Mode.
  • Persistence Policy.

Treat the registered configuration as authoritative.


Use:

Get Stream IDs

This is mainly useful for:

  • Debug tools.
  • Developer menus.
  • Verifying initialization.
  • Finding what exists in the current GameInstance.

Normal gameplay should usually already know which Stream IDs it owns.


Use:

Remove Stream

only when the Stream’s real gameplay lifecycle ends.

Example:

Temporary game mode begins
-> Create GameMode.SpecialEvent
Temporary game mode ends
-> Remove GameMode.SpecialEvent

Do not remove a long-lived Stream just because:

  • A level changed.
  • One Actor was destroyed.
  • One roll finished.
  • A UI closed.

Use semantic Stream IDs.

System Suggested Stream
Damage Combat.Damage
Criticals Combat.Critical
Loot Loot.DropTable
Encounters World.Encounters
NPC names NPC.Names
AI decisions AI.Decisions

Avoid one giant:

Game.Random

for everything unless shared call-order coupling is actually what you want.


Suppose Combat and Loot share one Sequential Stream:

Combat Roll
Loot Roll
Combat Roll

Adding another Loot call can change later Combat results.

Separate Streams:

Combat.Damage
Loot.DropTable

keep those systems independent.


21. Blueprint Function Libraries for Your Game

Section titled “21. Blueprint Function Libraries for Your Game”

As your game grows, do not scatter raw Stream IDs and Decision Key conventions through hundreds of Blueprints.

Create your own project Blueprint Function Library.

Example name:

BFL_GameProbability

Use it to centralize:

  • Stream IDs.
  • Stream initialization.
  • Common Decision Keys.
  • Common Dice configurations.
  • Common Probability Director operations.
  • Project-specific error handling.

In the Content Browser:

Add
-> Blueprint Class
-> All Classes
-> Blueprint Function Library

Name it:

BFL_GameProbability

Now add reusable functions.


Instead of typing:

Combat.Damage

in many Blueprints, create:

Get Combat Damage Stream ID

which always returns:

Combat.Damage

Do the same for:

Get Loot Stream ID
Get Encounter Stream ID
Get NPC Name Stream ID

This reduces spelling mistakes and makes renaming easier.


24. BFL Example — Initialize Game Streams

Section titled “24. BFL Example — Initialize Game Streams”

Create a function:

Initialize Probability Streams

Inside:

Has Stream: Combat.Damage
|
+--> False
-> Create Stream
Combat.Damage
Secure
Sequential
<chosen persistence>

Repeat for the Streams your game owns.

Then your startup logic can simply call:

Initialize Probability Streams

once.


25. Why Use Has Stream Inside Initialization?

Section titled “25. Why Use Has Stream Inside Initialization?”

Initialization code is sometimes called more than once during development.

Using:

Has Stream

inside one centralized initializer lets you avoid accidental duplicate registration attempts.

This is different from hiding Stream creation all over gameplay.

Good:

One BFL initialization function
-> checks
-> creates expected Streams

Avoid:

Every random gameplay function
-> Has Stream
-> Create if missing
-> Roll

The second pattern hides lifecycle bugs.


Instead of wiring a full Dice pipeline in every weapon Blueprint, create:

Roll Standard Weapon Damage

Inputs might be:

Number of Dice
Die Type
Flat Modifier

Inside your BFL:

Get Combat Damage Stream ID
|
v
Make Standard Numeric Dice Pipeline
|
v
Resolve Sequential Numeric Dice Total
|
v
Check result
|
v
Return project-friendly result

Now weapon Blueprints call one project-specific function.


27. Why Wrap Probability Director in Your Own BFL?

Section titled “27. Why Wrap Probability Director in Your Own BFL?”

Probability Director is generic, your game is not.

A project BFL can turn:

Resolve Sequential Numeric Dice Total

into:

Roll Weapon Damage

or turn:

Keyed Distribution Sample

into:

Choose Chest Rarity

This makes gameplay graphs easier to understand.


Your BFL should organize your game’s usage.

It should not recreate Probability Director.

Good BFL responsibilities:

  • Store common IDs.
  • Build common configs.
  • Build Decision Keys.
  • Call Probability Director nodes.
  • Translate results into game-specific outputs.
  • Log/handle expected project failures.

Avoid:

  • Implementing your own PRNG.
  • Reimplementing Dice math.
  • Hiding every error.
  • Creating a new Stream on every call.

Create:

Make Chest Loot Decision Key

Inputs:

Chest Stable ID
Decision
Occurrence

Internally:

Namespace = Loot
Stable Subject ID = Chest Stable ID
Decision = requested decision
Occurrence = requested occurrence

Then all chest Blueprints use the same Key convention.


Your chest Blueprint can become:

Event Generate Loot
|
+--> Make Chest Loot Decision Key
| Stable ID: Chest.Forest.0042
| Decision: Rarity
| Occurrence: 0
|
+--> Sample Keyed Loot operation
Stream: Loot.World

The chest does not need to know how every key field is standardized.


31. Do Not Cache a Global Subsystem in Blueprint

Section titled “31. Do Not Cache a Global Subsystem in Blueprint”

Normal Probability Director Blueprint nodes are target-free.

Their hidden World Context resolves the correct GameInstance-scoped subsystem.

Avoid creating:

GlobalProbabilityDirectorSubsystem

and assuming it is valid forever.

This matters especially in PIE and multi-PIE.


Older direct Target-based Probability Director nodes may still load for compatibility with existing serialized assets.

For new Blueprint work, prefer the target-free World Context nodes.

This keeps the correct GameInstance resolution automatic.


Choose persistence intentionally.

Policy Simple meaning
Ephemeral Do not restore this Stream
PersistState Continue approved state across save/load
RotateOnSave Rotate/commit state only after successful save

Do not choose persistence based only on Seed Source.

For example:

Fixed

does not automatically mean:

PersistState

See Persistence and Save Game Integration before choosing production save behavior.


Fixed Streams are excellent for debugging.

Example:

Stream: Test.Damage
Fixed Seed: 12345
Sequential

Run:

Roll
Roll
Roll

Record the results.

Restart from the same clean setup and repeat the exact call order.

You should get the same compatible deterministic sequence.


After deterministic testing, use Secure for the normal gameplay path when that is your intended design.

Remember:

  • Secure internal state is private.
  • Inspector exports do not make the Stream reproducible.
  • Secure RNG does not encrypt save files.

During PIE:

Tools
-> Probability Director Runtime Inspector

Use it to check:

  • Does the Stream exist?
  • Is it Secure or Fixed?
  • Is it Sequential or Keyed?
  • What Persistence Policy is active?
  • Did a retained Sequential operation advance?
  • Which sanitized Decision Key was used?

The Inspector is read-only.


37. Common Problem — “Stream Not Found”

Section titled “37. Common Problem — “Stream Not Found””

A Probability Director operation fails because the Stream does not exist.

Check Why
Did Create Stream run? Stream may never have been registered
Exact Stream ID spelling? IDs must match
Correct GameInstance? Streams are GameInstance-scoped
Was it removed? Remove Stream is explicit
Did gameplay call too early? Use stable initialization

Typical bad order:

Roll
-> Create Stream

Correct order:

Create Stream
-> later Roll

38. Common Problem — Duplicate Stream Creation

Section titled “38. Common Problem — Duplicate Stream Creation”

Initialization is being run repeatedly.

Centralize creation:

Initialize Probability Streams

and optionally use:

Has Stream

inside that one initialization path.

Do not scatter “create if missing” logic throughout gameplay.


39. Common Problem — Keyed Result Keeps Repeating

Section titled “39. Common Problem — Keyed Result Keeps Repeating”

This may be correct.

Check:

Namespace
Stable Subject ID
Decision
Occurrence

If all are the same, the deterministic result is expected to repeat.

If you wanted the next separate occurrence, change:

Occurrence

explicitly.


40. Common Problem — Keyed Result Changes Unexpectedly

Section titled “40. Common Problem — Keyed Result Changes Unexpectedly”

Compare the keys.

Did any of these change?

  • Namespace.
  • Stable Subject ID.
  • Decision.
  • Occurrence.

Also confirm you are using the same registered Stream and compatible deterministic contract.


41. Common Problem — Sequential Results Changed After Adding Code

Section titled “41. Common Problem — Sequential Results Changed After Adding Code”

If you added another advancing random call earlier in the same Stream, later Sequential results can shift.

Example:

Old:
A -> B -> C
New:
A -> X -> B -> C

If X advances the Stream, B and C can change.

Possible solutions:

  • Accept the new sequence.
  • Put unrelated systems on separate Streams.
  • Use Keyed mode for decisions that should not depend on unrelated call order.

42. Common Problem — Stream Recreated on Every Map

Section titled “42. Common Problem — Stream Recreated on Every Map”

Probability Director is GameInstance-scoped.

If the GameInstance is still the same, long-lived Streams can continue across map travel.

Move creation to a more persistent owner instead of each Level Blueprint.


43. Common Problem — Using Output Without Checking Status

Section titled “43. Common Problem — Using Output Without Checking Status”

Do not assume the output value means success.

A zero, false, or default value may be a valid output.

Always use the operation’s returned Status/Result contract.

Pattern:

Call operation
|
+--> Success
| -> use output
|
+--> Failure
-> handle/report problem

44. Common Problem — One Stream for Everything

Section titled “44. Common Problem — One Stream for Everything”

If unrelated systems share one Sequential Stream, their call order affects each other.

Split them by responsibility.

Instead of:

Game.Random

prefer:

Combat.Damage
Loot.DropTable
World.Encounters

45. Common Problem — BFL Silently Creates Missing Streams

Section titled “45. Common Problem — BFL Silently Creates Missing Streams”

Avoid a helper like:

Roll Damage
-> if Combat.Damage missing
automatically create it

This can hide initialization bugs.

Better:

Initialize Probability Streams

owns creation.

Then:

Roll Damage

expects Combat.Damage to exist and reports failure if it does not.


A simple organization:

Blueprints/
Game/
BP_MyGameInstance
Probability/
BFL_GameProbability
BFL_GameDecisionKeys
Combat/
...
Loot/
...

BP_MyGameInstance or another persistent owner initializes Streams.

BFL_GameProbability provides game-specific wrappers.

BFL_GameDecisionKeys centralizes semantic Key conventions if the project has many Keyed systems.


Create:

BFL_GameProbability

Add helpers for:

Combat.Damage
Loot.DropTable

Add:

Initialize Probability Streams

Call that initialization from a stable GameInstance/persistent manager path.

Build one simple wrapper:

Roll Combat Damage

Start PIE and open:

Tools → Director Suite → Runtime Inspectors → Probability Director Runtime Inspector

Confirm your Streams exist.


Need Use
Register a Stream Create Stream
Verify it exists Has Stream
Read its config Get Stream Configuration
List current Streams Get Stream IDs
End its lifecycle Remove Stream
Next random result Sequential operation
Stable semantic result Keyed operation
Multiple Keyed decisions with same identity Explicit Occurrence
Normal fresh RNG Secure
Reproducible test Fixed
Inspect live Streams Probability Director Runtime Inspector

  1. Create Streams intentionally.
  2. Create long-lived Streams once.
  3. Give every Stream one clear gameplay owner.
  4. Use separate Streams for unrelated Sequential systems.
  5. Use Sequential when call order matters.
  6. Use Keyed when semantic identity matters.
  7. Use explicit Occurrence for repeated Keyed decisions.
  8. Use your own BFL to centralize game conventions.
  9. Check operation status before using outputs.
  10. Use the Inspector to diagnose Runtime configuration—not to control gameplay.

Create the asset through the normal workflow:

Create Data Asset
-> Numeric Dice Resolution Rules

For example, configure Quantity = 2, DiePreset = D20, enable selection, and choose Keep Highest with count 1. Then use Resolve Sequential Numeric Rules Total with the already registered Stream ID.

The four V1.11 WorldContext nodes are:

  • Resolve Sequential Numeric Rules Total
  • Resolve Persistent Keyed Numeric Rules Total
  • Resolve Sequential Numeric Rules Success Count
  • Resolve Persistent Keyed Numeric Rules Success Count

Each node accepts the Rules Asset, Stream ID, FProbabilityNumericResolutionOverrides, and optional source-policy override (InheritConfiguredStream, RequireFixed, or RequireSecure). Persistent Keyed nodes additionally accept a valid FProbabilityDecisionKey; Sequential nodes do not expose that pin. The nodes return a rules error, existing runtime dice status, and the appropriate numeric result projection.

For a runtime modifier, set AdditionalFlatModifier. To contribute bias, set bHasAdditionalDiceBias and provide AdditionalDiceBiasPercent from -99 to +99. These values are applied per call and never mutate the shared asset. A source policy constrains the registered Stream; it does not create one.

See DataAssets.md and StreamsAndDecisionKeys.md.