Skip to content

17 — Fuzzy Drone Arena Demo

🇬🇧 English | 🇺🇦 Українська

The plugin's optional FuzzyLogicDemo module ships a vivid demo scene where nine drones evaluate a single shared Mamdani fuzzy system every frame. There's no scripted choreography: each drone feeds its own orbit distance, the reactor's shared heat, and its own simulated integrity into the same UFuzzySystemAsset, and the resulting numbers drive its speed, altitude, and color in real time.

How to Run It

In the Content Browser, enable the settings (gear) icon → Show Plugin Content if Plugins isn't visible, then navigate to Plugins → Fuzzy Logic Content → Demo → FuzzyDroneArena → Maps and open FuzzyDroneArena.

Press Play or Standalone Game. The camera orbits the arena, the central reactor pulses, and the drones change trajectory and color.

Assets

All of this demo's assets live in the Content Browser under Plugins → Fuzzy Logic Content → Demo → FuzzyDroneArena (enable Show Plugin Content if Plugins isn't visible):

Asset Purpose
Maps/FuzzyDroneArena The demo map
DA_DroneBehavior The shared UFuzzySystemAsset
Materials/M_DemoGlow A parametric emissive material
DroneBehavior.json The editable JSON source of the system

How to Read the System

Unlike the Turret Defense demo's three chained systems, Drone Behavior is a single Mamdani system with three inputs and three outputs, evaluated independently by every drone. It uses the plugin's defaults throughout: Minimum for AND, Maximum for OR, Clip implication, Maximum aggregation, and Centroid defuzzification over 201 samples. Trapezoid and Triangle sets model the ranged inputs and outputs; Ramp models the two open-ended 0…1 inputs, where a falling ramp (Shoulder below Foot) reads high at the low end and a rising ramp reads high at the high end — see Membership Functions.

Drone Behavior — the Fuzzy System

Variable Range Default Set Parameters Meaning
Distance 350…1500 900 Near Trapezoid: 350 / 350 / 520 / 850 hugging the reactor; full membership up to 520, fading out to 850
Mid Triangle: 600 / 900 / 1200 working orbit; most typical around 900
Far Trapezoid: 950 / 1250 / 1500 / 1500 outer orbit; full membership from 1250
ReactorHeat 0…1 0.5 Cool Ramp: 0.75 → 0.15 reactor running cold; full membership near 0.15 and below, fading out to 0.75
Hot Ramp: 0.25 → 0.85 reactor overheating; strength rises after 0.25 and is full near 0.85
Integrity 0…1 0.8 Low Ramp: 0.65 → 0.10 damaged; full membership near 0.10 and below, fading out to 0.65
High Ramp: 0.35 → 0.90 healthy; strength rises after 0.35 and is full near 0.90
Aggression 0…1 0.5 Cautious Triangle: 0 / 0.18 / 0.55 hang back; strongest near 0.18
Watchful Triangle: 0.25 / 0.52 / 0.78 hold position and observe; strongest near 0.52
Aggressive Triangle: 0.55 / 0.88 / 1 close in; strongest near 0.88
OrbitSpeed 0…1 0.5 Slow Triangle: 0 / 0.15 / 0.52 drift; strongest near 0.15
Fast Triangle: 0.42 / 0.88 / 1 sprint; strongest near 0.88
Altitude 0…1 0.5 Low Triangle: 0 / 0.18 / 0.58 skim near the platform; strongest near 0.18
High Triangle: 0.42 / 0.82 / 1 climb well above the reactor; strongest near 0.82

Distance closes a loop with the drone's own motion: every frame it is set to the drone's current orbit radius, clamped to the variable's range, and the resulting Aggression then nudges that same radius for the next frame (see below). ReactorHeat and Integrity are simulated signals — a shared heat wave for the former, an independent phase-shifted oscillation per drone for the latter — standing in for whatever real telemetry a game would report.

The Rules

IF Distance IS Near AND ReactorHeat IS Hot THEN Aggression IS Aggressive
IF Distance IS Near AND Integrity IS Low THEN Aggression IS Cautious
IF Distance IS Mid AND Integrity IS High THEN Aggression IS Watchful
IF Distance IS Far THEN Aggression IS Watchful

IF Distance IS Near THEN OrbitSpeed IS Slow
IF Distance IS Mid THEN OrbitSpeed IS Fast
IF Distance IS Far THEN OrbitSpeed IS Fast

IF ReactorHeat IS Cool THEN Altitude IS Low
IF ReactorHeat IS Hot THEN Altitude IS High
IF Integrity IS Low THEN Altitude IS High WITH 0.75

Aggression balances opportunity against risk: being near a hot reactor pushes it up, but low integrity while near pulls it back down, and a mid-range drone only commits to Watchful while healthy. Distant drones are always Watchful — there's no rule for Far AND * that reaches Aggressive or Cautious, so a lone distant drone settles on the middle set almost regardless of its other inputs. OrbitSpeed depends on Distance alone: drones hugging the reactor slow down (to avoid overshooting it), while mid-range and far drones both move fast. Altitude tracks ReactorHeat directly, with damaged drones (Integrity IS Low) also climbing — at a reduced WITH 0.75 weight, so heat still dominates altitude when the two disagree.

How Nine Drones Share One System

All nine drones evaluate the same DA_DroneBehavior asset, so this demo takes the crowd route rather than the component one: AFuzzyDroneAgent has no UFuzzyLogicComponent. Each drone asks UFuzzyLogicSubsystem for the compiled engine once, in BeginPlay, and holds it for its lifetime:

if (UGameInstance* GameInstance = GetGameInstance())
{
    Brain = GameInstance->GetSubsystem<UFuzzyLogicSubsystem>()->GetEngine(Behavior);
}

The first drone to reach that line compiles the system; the other eight are handed the same engine. One copy of the system exists for the whole arena instead of nine, and it is parsed once instead of nine times.

Because a shared engine keeps no per-agent state, every tick supplies all three inputs rather than only what changed — there is no component remembering them between calls. The map itself is reused, so a tick writes three floats instead of allocating:

Inputs.Add(FuzzyDroneArena::DistanceInput, DistanceInput);
Inputs.Add(FuzzyDroneArena::HeatInput, ReactorHeat);
Inputs.Add(FuzzyDroneArena::IntegrityInput, Integrity);

const FFuzzyInferenceResult Result = Brain->Evaluate(Inputs);

Be clear about the scale, though: nine drones are far too few for this to matter to a profiler. The demo is showing the shape of the pattern, not a measured win — the saving only becomes real in the hundreds. For a single detailed agent, the component is still the better default, which is what 18 — Fuzzy Turret Defense Demo shows: three chained systems on one turret, each on its own component.

The two routes are compared side by side in 14 — Architecture And Performance.

From Crisp Outputs to Motion

The three crisp numbers are read once per frame and drive the drone's actual transform and materials directly — there's no intermediate state machine:

Output Drives Formula
Aggression Desired orbit radius Lerp(1180, 560, Aggression) cm, eased toward with FInterpTo
Body color see "What the Drone Color Means" below
Point light intensity Lerp(420, 1050, Aggression)
Body glow strength Lerp(0.22, 0.58, Aggression)
Energy beam thickness 0.009 + Aggression * 0.013
OrbitSpeed Angular speed Lerp(10, 38, OrbitSpeed) degrees/second
Rotor spin rate Lerp(180, 660, OrbitSpeed) and Lerp(240, 820, OrbitSpeed) degrees/second, counter-rotating
Altitude Orbit height Lerp(290, 820, Altitude) cm, plus a small per-drone sine wobble

The fed-back Distance input closes the loop described above: a high Aggression pulls the desired radius down to 560, which lowers Distance next frame, which the rules read as Near — reinforcing the same behavior until Integrity or ReactorHeat pulls the other way.

What the Drone Color Means

The body color is not the fuzzy system's own output — it's a separate two-stage HSV blend over the same Aggression value, independent of where Aggression's own Cautious/Watchful/Aggressive sets peak:

  • 0.0 → 0.5: blends from Calm (cyan, 0.015, 0.55, 1.0) to Adaptive (violet, 0.42, 0.08, 1.0).
  • 0.5 → 1.0: blends from Adaptive to Approach (red, 1.0, 0.015, 0.04).

The HUD's legend swatches (0.0 CALM, 0.5 ADAPT, 1.0 APPROACH) mark this exact split — a deliberate, hardcoded visualization choice, not a boundary the fuzzy system itself defines. On the right of the HUD, live telemetry for the first drone (D-01) shows its Distance/ReactorHeat/Integrity inputs and Aggression/OrbitSpeed/Altitude outputs updating every frame; a pulsing TELEMETRY LOCK frame follows that drone in view, sharing its current color. The changing numbers demonstrate that the scene uses runtime inference, not pre-recorded animation.

The Role of JSON

DA_DroneBehavior was built from DroneBehavior.json via FuzzyLogicStatics.load_fuzzy_system_from_json. At runtime, the drones read the Data Asset directly; the JSON isn't parsed every frame and isn't needed by the packaged scene.

You can also open DA_DroneBehavior, change the system in the editor, and click Save To JSON to manually sync the external preset.

What to Try

  • change the shape of ReactorHeat.Hot and see how altitude reacts;
  • increase the weight of the low-integrity rule;
  • switch Centroid to Weighted Average and compare the behavior;
  • add an output for beam brightness;
  • use one Data Asset for dozens of additional agents.

The demo classes live in the plugin's own FuzzyLogicDemo module (Plugins/FuzzyLogic/Source/FuzzyLogicDemo) — they ship with the plugin by default, but can be excluded from the build entirely; see 16 — Demo Content.

The Meaning of DefaultValue

DefaultValue on an input only matters before a drone's first Tick and as the starting point for the editor's Inference preview — at runtime, Set Input overwrites Distance, ReactorHeat, and Integrity from live simulation state every single frame, so the asset's defaults are never actually evaluated in the running demo.

DefaultValue on an output doesn't participate in inference at all. It's just the default value in the variable's structure; the result is always computed from the rules and membership functions.