OmniWalk is a high-performance C++ middleware framework for Unreal Engine 5.4+ that delivers "Impossible Locomotion." Designed to replicate the high-fidelity magnetism found in titles like Ratchet & Clank and Dead Space, it allows any character to traverse arbitrary geometry — walls, ceilings, pipes, and planets — with zero technical friction.
While UE5.4 introduced native multi-directional gravity, OmniWalk solves the remaining "showstopper" problems: camera gimbal-lock, control inversion, triangle-normal jitter, and complex input remapping.
🚀 The "Zero-Config" unified component
Unlike competing frameworks that require rebuilding your character from a specific parent class, OmniWalk Pro features a unified component architecture.
Drop-In Integration: Drag the OmniWalkPro component onto any ACharacter (Pawn, MetaHuman, Synty, etc.).
Auto-Hijack Logic: Upon BeginPlay, the component automatically configures Pawn settings (Yaw usage, floor angles), injects a Camera Stabilizer, and intercepts the Input Buffer to project world-space movement onto local surface planes.
🧠 Key Technical USP's
Native 5.4+ Gravity Integration: Leverages the re-designed CharacterMovementComponent gravity architecture for frame-perfect physics — not forces-based hacks.
Gimbal-Free Camera Solver: A custom C++ Camera modifier resolves the "Euler Singularity." Mouse/Stick look remains intuitive and non-inverted, even when standing upside down on a ceiling.
Arbitrary Surface Adhesion: No Volumes required. Walks on any StaticMesh, DynamicMesh, or Landscape normal via optimized trace-averaging.
Input Projection Technology: Automatically "steals" world-space movement input and flattens it against the current surface normal, preventing the character from becoming "stuck" against vertical walls.
Slate Telemetry Debugger: A dedicated Editor window (Window > OmniWalk Debugger) provides real-time telemetry on gravity vectors, alignment strength, and trace hits.
🛠 Technical Specifications
Performance: All core logic is written in C++ and executes in TG_PrePhysics, ensuring zero latency between surface detection and movement integration. Requires the Enhanced Input plugin to be enabled.
Orientation Math: Uses Quaternions (QInterpTo) and Slerp-normalization to handle 360-degree transitions without gimbal flips.
Procedural Demo Included: Includes a C++ Helix-Generator that constructs "impossible" walking paths at runtime to demonstrate system stability across extreme centripetal forces.
📋 Package Contents
Runtime Module: Core Adhesion Component, Camera Stabilizers, Input Hijacker, and World Subsystem.
Editor Module: Slate UI Telemetry and Debugging tools.
Source Code: Fully commented C++ code.
Example Content: Procedural demo level and a "Zero-Touch" implementation guide.
Updates and bugfixes are continuous for this plugin, and new features are expected to be added, including vertical wall sliding and wall-to-wall jumps.
An update was submitted today. Thanks for reading.
Reworked the Setup Wizard from an example-level wrapper into a real onboarding tool with separate Project Setup and Example Tools modes.
Added current-project audit coverage for Enhanced Input, starter asset presence, current-level GameMode readiness, and selected Character actor readiness.
Added starter Blueprint planning, path validation, and non-destructive creation for:
BP_OW_PlayerController
BP_OW_FirstPersonCharacter
BP_OW_ThirdPersonCharacter
BP_OW_FirstPersonGameMode
BP_OW_ThirdPersonGameMode
Added relinking so generated GameMode Blueprints automatically point to the generated Character and Controller Blueprints.
Added one-click actions to assign the generated first-person or third-person GameMode to the current level.
Added selected-Character tagging workflow for OmniWalk.Enabled, including cleanup of blocking tags such as OmniWalk.NoAutoInject, OmniWalk.Disabled, and OmniWalk.Ignore.
Added direct actions to open the manual, support page, and Fab listing from the wizard.
Added editor automation coverage for content-path validation and starter asset plan drift.
Added KismetCompiler as an editor dependency for starter Blueprint configuration and compile steps.
Fixed UE 5.7 Slate/API compatibility issues discovered during live verification:
SWrapBox now uses UseAllottedSize
LeftChopInline now uses EAllowShrinking::No
Verified OmniWalkDevEditor builds successfully against UE 5.7.
Verified both setup-wizard automation tests pass:
OmniWalk.Editor.SetupWizard.PathValidation
OmniWalk.Editor.SetupWizard.StarterPlan
Observed a non-blocking Unreal warning that Visual Studio 2026 toolchain 14.50 is not the engine’s preferred compiler version, but the build completed successfully.
0.4 introduced some issues, 0.4.1 now corrects those.
Changelog: 0.4.1 (1 May 2026)
Fixed
Fixed Spider Walk traversal on the OmniWalk character so it now wraps across sharp 90-degree wall ledges instead of unpredictable behavior.
Improved convex-edge probing to support wall-to-top and top-to-down face transitions, not just floor-to-wall cases.
Fixed Setup Wizard result handling so Project Setup and Example Tools report their own action status independently.
Fixed Setup Wizard auto-injection tagging so opted-out actors are skipped and preserve tags such as OmniWalk.Disabled, OmniWalk.Ignore, and OmniWalk.NoAutoInject.
Fixed showcase demo map generation so OW_Example is playable immediately after creation.
Fixed generated showcase runtime setup to include OmniWalkFirstPersonGameMode, a PlayerStart, and AOmniWalkShowcaseLevel.
Fixed legacy generated-map cleanup so old native demo generator actors and incompatible placed pawn actors are removed from managed example levels.
Fixed showcase planetoid behavior by giving the planetoid collision and a matching point-gravity volume.
Fixed trigger-zone demo sizing by adding configurable trigger collision box extents and applying them in the showcase.
Fixed Surface Response showcase wiring by assigning physical materials to generated response strips and giving the example character a surface-response component.
Fixed Zero-G showcase support by adding UOmniWalkZeroGComponent to the example character and routing movement input into thruster input while Zero-G is active.
Changed
Reworked the generated showcase map into a guided, low-pressure feature tour rather than a challenging obstacle course.
Expanded the showcase route to introduce major systems in sequence:
Core surface adhesion.
Wall/ceiling traversal.
MagBoot states.
Spider Walk convex ledges.
Point-gravity planetoid traversal.
Directional gravity rooms.
Trigger zones.
Zero-G pads and magnetized exits.
Surface Response material strips.
Finale climb/summit.
Made showcase progression easier with wider paths, larger landings, clearer labels, shorter climbs, broad steps, rest platforms, catch decks, and oversized Zero-G pads.
Replaced the spider-walk pyramid-style demo with a readable wall/top/down training block with rails and visual markers.
Reworked the planetoid section with connected ascent/descent ramps and a wide equatorial ring road.
Rebalanced the Zero-G chamber with closer platforms, larger pads, visible entry/exit affordances, side posts, and a low recovery deck.
Shortened and widened the finale route to make completion reliable during a first playthrough.
Updated generated surface-strip labels to match the actual response multipliers.
Added
Added automated coverage for generated showcase map creation.
Added automated coverage for showcase runtime plumbing.
Added automated checks that the example character includes Surface Response and Zero-G support.
Added tests covering Setup Wizard tag persistence and opt-out behavior.
Added documentation for the new guided showcase route, easier demo tuning, Zero-G component behavior, Surface Response showcase setup, trigger-zone extents, and Setup Wizard opt-out tags.
Documentation
Updated Plugins/OmniWalk/Docs/Manual.html with:
Setup Wizard opt-out tag behavior.
Separate Project Setup vs Example Tools result reporting.
New guided showcase map description and route diagram.
Easier showcase progression notes.
Updated Spider Walk ledge traversal behavior.
Updated planetoid gravity-volume behavior.
Zero-G input/thruster behavior.
Surface Response demo material/table behavior.
Trigger-zone configurable collision extents.
Verification
Ran Automation RunTests OmniWalk.Editor.SetupWizard successfully.
Enhanced Input Auto-Injector & WASD Project-Fix ( HandleAutoConfigureEnhancedInput ):
• Scans your active editor actor selection or Content Browser Blueprint selections.
• Auto-enables the internal input remapping fix ( bUseInternalInputFix = true and bProjectGravityFromSurface = true ) on the UOmniWalkPro default components. This allows existing characters with legacy XY input vectors to seamlessly walk on walls instantly without any blueprint graph changes.
Physics Collision Profile & Level Mesh Auditor ( HandleAuditLevelCollisions / HandleFixLevelCollisions ):
• Scans the active level for Static Meshes configured with NoCollision that would otherwise cause surface traces to fail.
• Provides a “One-Click Fix” to repair those meshes, setting their collision status to QueryAndPhysics and assigning a standard BlockAll profile so wall climbing is perfect.
Dynamic Material Surface Response Table Configurator ( HandleConfigureResponseTable ):
• Auto-generates, duplicates, and saves a default showcase UOmniWalkSurfaceResponseTable asset in your starter directory.
• Auto-binds and links the Response Table to the UOmniWalkSurfaceResponse component of all selected Characters or Blueprint CDOs.
Locomotion Presets Injector ( HandleApplyLocomotionPreset ):
• Adds an elegant Slate dropdown menu allowing you to instantly apply optimized physics presets to selected character/Blueprint UOmniWalkPro components:
• Spider Climber: Optimized for 90° convex ledges, sticky transitions, and fast ceiling walking.
• Wall Runner: Tuning for high-speed wall sliding and input-based braking/hanging.
• Zero-G Float: Configured for volume-based orbital flight (stops projecting gravity from surface meshes).
• Standard Adhesion: Standard stable C++ defaults.
Direct Telemetry Debugger Launcher ( HandleLaunchTelemetryDebugger ):
• Adds a direct visual shortcut button to instantly invoke the live SOmniWalkDebugger nomad tab next to your active editor viewport.
Overhauls and Fixes
• CDO Constructor Error Resolution: Fully refactored the skeletal mesh and animation asset loading in AOmniWalkThirdPersonCharacter out of the CDO static constructor ( ConstructorHelpers::FObjectFinder ) and into a dynamic, safe load block inside BeginPlay() using FPackageName::DoesPackageExist . This completely resolved severe red CDO
constructor errors on startup.
• Look Action Skipping Safeguard: Upgraded AOmniWalkPlayerController 's IA_Look package resolution to use FPackageName::DoesPackageExist path checks before loading UInputActions from disk, eliminating all noisy SkipPackage package streaming warnings.
• Spider Walk (Convex Traversal) System Overhaul: Overhauled the convex edge detection algorithm in UOmniWalkPro to resolve slow-movement, zero-input, and high-speed transition dismount bugs:
• Added Projected Actor Forward Fallback to maintain surface tracking when input and velocity project to zero on the surface plane.
• Replaced single far sweeps with Dual Over-Edge Probes (close sweep at CapsuleRadius + 15cm and far sweep at CapsuleRadius + FMath::Min(ConvexCheckDistance, 60.0f) ) to guarantee stable ledge sweeps at any velocity.
• Relaxed the transition boundary check limit AheadDot from -CapsuleRadius * 0.75f to -CapsuleRadius * 1.5f to prevent premature dismount rejection during sprinting and sliding.
• Coyote Time & Setup Wizard Unit Testing:
• Added public, Blueprint-callable getters GetCoyoteTimer() and WasOnSurface() to AOmniWalkExampleCharacter to facilitate robust test verification.
• Refactored the CoyoteTime locomotion unit tests in OmniWalkSetupWizardTests.cpp to use Direct C++ Interface Invocations via Cast (e.g. Cast ), bypassing script/headless VM event routing constraints on pure C++ instances in offline editor tests.
• 100% pass rate on all 9 automation tests with zero warnings/errors in the test runner.
Hot Path Tracing: Injected TRACE_CPUPROFILER_EVENT_SCOPE macros into high-frequency loops (UOmniWalkPro::TickComponent and UOmniWalkPro::DetectSurfaceAsync) to allow developers to accurately monitor the ms budget consumed by OmniWalk’s surface tracing and gravity interpolation logic.
Physics Field Validation: Re-checked the “Gravity Wake” localized physics displacement tool, negating the need for a new physics system.
Setup Wizard UX Improvements: Overhauled the SOmniWalkSetupWizard slate interface for improved user onboarding.
Implemented “Generate Playable Third-Person Setup” for One-Click onboarding matching the First-Person workflow.
Added self-documenting UI Tooltips (ToolTipText) to all Project Setup buttons to clarify background asset generation and GameMode assignment actions.
Conflict — two systems fighting over the same state
Logic — incorrect or dead behavior
Gap / Quality — missing, degraded, or untidy
New — added API or capability
Changed — refactor / rename
Validated — confirmed by build / test
Surface, Gravity & Multiplayer Hardening
Networking & Multiplayer
Wall-slide no longer disconnects clients.Server_SetWallSliding_Validate rejected the
documented -1 “keep current speed” sentinel, and a failed RPC validation closes the client’s
connection, so toggling wall-slide on a client instantly kicked the player. Validation now
accepts the sentinel and only rejects non-finite / over-cap values. → UOmniWalkPro
Gravity now syncs to all players. State went out via a Client RPC (owner-only), so
simulated proxies never updated. Switched to NetMulticast (Multicast_SyncGravityState);
simulated proxies are now driven by replication instead of fighting it with local traces.
→ UOmniWalkPro
Mag-boot state applies locally on replication via new ApplyMagBootState(), so the
handler no longer bounces a Server_ RPC on proxies (warn-spam + drop). → UOmniWalkPro
Gravity & Surface Walking
Unified gravity-override channel. Gravity volumes and trigger zones now hold their gravity
even while the character is grounded (previously clobbered every tick by surface detection) and
decay shortly after the character leaves. → AOmniWalkTriggerZone, AOmniWalkSplineVolume, UOmniWalkPro
Killed per-frame client warning spam from external gravity drivers — the override path is
silently authority-only and replicates the result back. → UOmniWalkPro
Adhesion gate fixed. A tautological || SpeedToward > 0.0f made “needs adhesion” always
true; adhesion now engages only when falling or not already snapping into the surface. → UOmniWalkPro
Async traces no longer disable wall climbing. Async mode resolved only the floor; wall +
convex-edge (spider-walk) detection now runs each frame via shared DetectWallOrConvex(). → UOmniWalkPro
Gravity-volume containment no longer depends on component visibility (which diverged
between editor and packaged/runtime-spawned volumes). Bounds are chosen by GravityType.
→ AOmniWalkGravityVolume
Cinematic & Zero-G
Cinematic mode suspends the walker and transitions start from the current surface normal, not world-up — no snap when starting on a wall. → UOmniWalkCinematic, UOmniWalkPro
Zero-G flight suspends the walker so it stops fighting 6DOF orientation (mag-boot Off
alone didn’t). → UOmniWalkZeroGComponent
Configuration & Quality
UOmniWalkPro seeds tunables from UOmniWalkSettings
in its constructor (per-instance / Blueprint edits still win). → UOmniWalkPro, UOmniWalkSettings
Removed a duplicated include / copyright block in OmniWalkPro.cpp.
Automation test harness added: covers the surface “wall-like” classifier and
replication-state equality (run via OmniWalk. in the Automation window). → OmniWalkProTests.cpp
New API
// Gravity override: external drivers steer gravity without being clobbered by surface detection.
void UOmniWalkPro::SetGravityOverride(FVector NewUp, bool bImmediate = false, float HoldSeconds = 0.25f);
void UOmniWalkPro::ClearGravityOverride();
bool UOmniWalkPro::IsGravityOverrideActive() const;
// Full suspend — cutscenes / zero-G yield all per-tick control to an external controller.
void UOmniWalkPro::SetMovementControlSuspended(bool bSuspended);
bool UOmniWalkPro::IsMovementControlSuspended() const;
// Pure, testable surface classifier.
static bool UOmniWalkPro::IsSurfaceWallLike(const FVector& Normal, const FVector& Up, float Threshold);
While the walker is suspended (cinematic / zero-G), the per-tick multicast sync is skipped,
so a networked cinematic gravity flip won’t auto-replicate to observers. Single-player /
standalone is unaffected.
Please note that the plugin is under heavy development, and cannot be guaranteed to work in all use cases until it hits the 1.0.0 milestone.
The 0.6.0 update is still under Fab review/vetting at the time of this writing.
Do you have any advice on installing this plugin for Linux users? I’m on UE 5.6 and in the past once whenever i purchased or downloaded a plugin, I rebuilt the plugin from its source files to make it compatible with the editor which I built from source.
I downloaded the plugin using Lutris (since epic games launcher doesn’t work natively on Linux) and that worked, but there aren’t any source files, so i’m unsure how to get it working on my Linux machine. I’m on Arch if that helps any.
Fixed an out-of-bounds crash in the hover vehicle.TickComponent looped a hardcoded i < 4 while indexing TraceOffsets[Index], an EditAnywhere array. Any craft configured with
fewer than four suspension points (a bike, a tripod) read past the end and crashed on first tick.
The loop now follows TraceOffsets.Num(), and an empty array disables the component with a
warning instead of faulting. → UOmniWalkHoverComponent
Fixed an editor/game hang in the surface nav volume.BuildGraph stepped a sample loop with X += VoxelSize; VoxelSize had no clamp, so 0 (or a negative) never terminated. Now clamped
by property metadata and at runtime; metadata does not apply to Blueprint writes or older
serialised assets. → AOmniWalkSurfaceNavVolume
Nav graph generation is no longer quadratic. Node de-duplication linear-scanned every node
already generated, inside a triple voxel loop × 6 directions; edge building was a full O(N²) pair
sweep with up to 3 synchronous line traces each; all on the game thread at BeginPlay. Replaced
with a uniform spatial hash; both passes are now near-linear. Added MaxNodes (default 20 000),
a pre-build size warning, and a warning when a graph is built at runtime rather than in-editor.
→ AOmniWalkSurfaceNavVolume
A-star no longer degenerates. The open set was a TArray linear-scanned for the minimum with TArray::Contains for membership; O(N) per expansion, no closed set, so nodes were re-expanded.
Now a binary heap with a TBitArray closed set and hash-accelerated nearest-node lookup.
→ AOmniWalkSurfaceNavVolume::FindPath
Networking & Multiplayer
Replicated surface normals were being destroyed on the wire.FOmniWalkReplicationState
used FVector_NetQuantize, which serialises via SerializePackedVector<1, 20> — scale 1, zero
decimal places. Every component of a unit normal rounded to -1, 0 or 1: a wall normal of (0.6, 0.8, 0) arrived as (1, 1, 0), failed the receiving IsNormalized() check, and was discarded with a warning. Simulated proxies therefore never received correct gravity for any
non-axis-aligned surface. Switched to FVector_NetQuantizeNormal (16 bits per component across [-1,1]), pinned by a new automation test so it cannot be silently downgraded again.
→ FOmniWalkReplicationState
Dedicated-server auto-injection was completely broken.InitializeTaggedActors returned early
on NM_DedicatedServer, so the server never created the component. Clients injected locally and
fired Server_* RPCs at a component that did not exist server-side, with no authority anywhere to
drive gravity. The bail-out is removed; injection now runs wherever the component can do work —
authority, or any locally controlled pawn. → UOmniWalkSubsystem
Autonomous proxies no longer fight their own prediction. Client-side interpolation was gated on GetOwnerRole() < ROLE_Authority, which includes ROLE_AutonomousProxy — so the local player’s own
pawn blended toward the 10 Hz server state and ran local detection every frame. Interpolation is
now simulated-proxy only; autonomous proxies keep their prediction and adopt only the authoritative
booleans. → UOmniWalkPro
Stopped a per-frame wall-slide RPC storm. The per-tick auto-enable called SetWallSliding(true)
every frame while on a wall; on a client that re-sent Server_SetWallSliding until the 10 Hz
multicast echoed back; most rejected by the rate limiter, each with a warning. A request latch now
sends once per state change. → UOmniWalkPro
Gravity state is no longer broadcast unconditionally.Multicast_SyncGravityState fired every
100 ms per authoritative pawn regardless of change; a stationary character cost the same bandwidth
as a moving one, multiplied by pawns × clients. The struct’s operator== existed but was never
called in production; its only consumer was a unit test. It is now the send-side change check,
with a tolerance coarser than the wire quantisation. → UOmniWalkPro
Gravity & Surface Walking
Rotating-platform gravity tracking used the wrong delta. The code computed Current * Previous.Inverse(). FTransform composes A * B as “apply A then B” — the Relative * ParentWorld idiom — so that yields the base’s local-frame delta, which matches the
world delta only when the platform’s rest rotation is identity. A tilted or tumbling planetoid drifted
the surface normal. Corrected to Previous.Inverse() * Current. → UOmniWalkPro
Async traces no longer jitter on walls.OnAsyncTraceComplete wrote the floor-only result
into the same cache the tick then overwrote with the wall/convex-inclusive result, so the two clobbered
each other on alternate frames. The async floor result now has its own storage. Also fixed a stale-handle
path that cleared the pending flag early and could open a second overlapping trace. → UOmniWalkPro
Spline gravity volumes now use the spline as their bounds. Containment routed Point and Spline
through a sphere centred on the actor (500 cm default), so a tube of any real length was mostly outside
its own containment test and gravity cut out along most of it. Spline volumes now measure distance to
the spline, via a new SplineTubeRadius. → AOmniWalkGravityVolume
Gravity volume priority is honoured at runtime. Volumes were sorted only at registration, so any
runtime change to Priority silently had no effect. The highest-priority containing volume is now
chosen by inspection, and stale entries are skipped. → UOmniWalkSubsystem
Mag-Boot presets no longer destroy your tuning.Standard hard-wrote AdhesionForce = 3500, bAutoEnableWallSlide = true and bWallSlideResistsInput = true — so a Blueprint that set AdhesionForce = 8000 lost it the first time a trigger zone or Zero-G transition touched MagBootState. The component now captures your values once and Standardrestores them; MagLock and Hover are multipliers on your baseline. Defaults reproduce the previous absolute
values exactly at stock adhesion, pinned by a test. → UOmniWalkPro
Physics field no longer double-counts gravity, and its MaxAffectedObjects cap is now distance-ranked. Overlap order is not stable between queries, so a naive “first N” cap churned the
affected set and produced spurious Enter/Leave events every sample.
→ UOmniWalkHoverComponent, UOmniWalkPhysicsField
Animation & Foot IK
Fixed a data race in the Foot IK anim node.UpdateInternal called OwnerActor->FindComponentByClass<...>() every frame — but it runs on the animation worker thread
when multi-threaded animation update is enabled, the engine default. All owning-actor access moved to PreUpdate (game thread), with the component resolved once and cached instead of an O(n) scan per frame.
→ FAnimNode_OmniWalkFootIK
Fixed the world-to-component rotation conversion. The code wrote World * Comp⁻¹. FQuat
composes as (A * B).RotateVector(v) == A.RotateVector(B.RotateVector(v)), so the component-space
equivalent is Comp⁻¹ * World. The original was correct only when the mesh’s component rotation was
identity — which, for a character on walls and ceilings, it essentially never is. Foot rotation IK was
wrong on every non-upright surface. → UOmniWalkFootIKComponent
PelvisAlpha is no longer dead. It was computed every tick and never read, so IK snapped fully on
the frame it was enabled. The node now scales its entire contribution by it, and the component eases the
weight back out on disable. → UOmniWalkFootIKComponent, FAnimNode_OmniWalkFootIK
Bone transforms are sorted before submission. The pose blend requires ascending compact-pose index;
pelvis-before-feet is a property of the skeleton’s ordering, not a guarantee for arbitrary rigs.
→ FAnimNode_OmniWalkFootIK
Cinematic & Zero-G
180-degree gravity flips no longer degenerate. The transition linearly lerped between normals;
between near-antiparallel vectors that passes through (or near) zero, which normalises to nothing — so a
ceiling flip, the headline use case, snapped instead of animating. Replaced with arc interpolation,
including an explicit antiparallel branch where FindBetweenNormals has no unique answer.
→ UOmniWalkCinematic
Zero-G rotation no longer spins forever. Angular damping and integration lived inside AddThrusterInput, so they only ran while the player held the stick — release input and the character
span indefinitely. Both moved to TickComponent. → UOmniWalkZeroGComponent
Zero-G drag is now framerate-independent. Linear (*= 0.99) and angular (*= 0.95) damping were
fixed per-tick multipliers, so a character decelerated several times faster at 120 fps than at 30. Now
exponential in DeltaTime, exposed as LinearDamping / AngularDamping — defaults reproduce the
previous feel at 60 fps — plus a new MaxAngularSpeed clamp. → UOmniWalkZeroGComponent
Zero-G magnetise now uses a configurable TraceChannel instead of a hardcoded ECC_WorldStatic, and
warns instead of silently doing nothing when the owner has no CharacterMovementComponent.
→ UOmniWalkZeroGComponent
Input, Camera & Example Character
Removed unconditional per-frame logging from the example character.HandleMove carried a UE_LOG(..., Log, ...) marked “ALWAYS log for debugging” that fired on every input frame — an FString format plus a log write per frame in Shipping, since LogOmniWalk is declared with
compile-time verbosity All. Now behind #if WITH_EDITOR plus the debug switch, at Verbose.
→ AOmniWalkExampleCharacter
The example character works with any PlayerController again. It hard-required AOmniWalkPlayerController::GetGravityRelativeDirection and, failing that, logged at Error
verbosity every input frame and returned — producing zero movement plus an unbounded error flood for
any buyer using their own controller. It now falls back to UOmniWalkBlueprintLibrary::AddGravityRelativeMovement (same math, controller-agnostic) and notes the
fallback once. → AOmniWalkExampleCharacter
Fixed inverted handedness in the wall movement basis. The wall branch built Cross(Forward, GravityUp) while the ground branch used Cross(GravityUp, Forward) — opposite sign —
and relied on a camera-forward dot to correct it. Looking straight at a wall, which is the wall-walking
posture, makes that dot approach zero, so strafe direction flipped unpredictably. The basis is now built
with the correct handedness up front and aligned against the well-conditioned camera right.
→ UOmniWalkBlueprintLibrary
The controller no longer hijacks project input.bAutoSetGameInputMode forced FInputModeGameOnly and hid the cursor from BeginPlay, breaking any project that starts on a menu;
and the controller probed the buyer’s own content for a stock IA_Look, then registered its mapping
context at priority 100 — outranking essentially any project mapping and doubling look input. Both
are now opt-in (bAutoSetGameInputMode, bAutoDiscoverProjectLookAction, both default false), the
priority is configurable and defaults to 0, and explicit LookActionAsset / LookMappingContextAsset references are supported. → AOmniWalkPlayerController
Look input survives re-possession.SetupEnhancedLookInput early-returned once the action and
context existed, so a fresh InputComponent handed over on respawn never got the binding and look input
silently died. The bound component is now tracked, with bindings cleared before rebinding so handlers
cannot stack. → AOmniWalkPlayerController
Bounded the look-context retry.Tick polled GetLocalPlayer() every frame forever when no local
player existed. Now capped at roughly 5 seconds with a single warning. → AOmniWalkPlayerController
Debug Visualisation & Diagnostics
Fixed permanently jammed debug HUD text. With the shipped defaults
(bDebugVisualization = true, DebugDrawDuration = -1), all seven DrawDebugString calls passed -1.
Verified against engine source: AHUD treats TimeRemaining == -1 as never expires, and DrawDebugString passes bSkipOverwriteCheck = true so entries append rather than replace, capped
at 128 per actor. Every call passed TestBaseActor = nullptr, piling them all onto WorldSettings. At
5-8 strings per frame the list saturated with frozen, never-expiring labels in under a second, after
which new state text was silently dropped. → UOmniWalkPro
DebugDrawDuration now behaves as documented. The header and guide both claimed -1 = one frame, 0 = disabled. Engine source (GetDebugLineLifeTime: LifeTime > 0 ? LifeTime : DefaultLifeTime) shows 0 and -1 are identical — one frame — for shapes, and 0 does not disable anything; for HUD text
the mapping is inverted. The value is now translated per primitive so all three documented values are
true, and 0 genuinely disables drawing.
→ UOmniWalkPro, Docs/DebugVisualizationGuide.md
All debug visualisation now defaults off.UOmniWalkPro, AOmniWalkPatrolPath and UOmniWalkSurfacePathFollower shipped with debug drawing enabled — the patrol path at a 5-second
lifetime, so it accumulated. Six debug properties across the plugin are now consistently false, seeded
from new Project Settings.
Convex-traversal probes honour the configured lifetime instead of a hardcoded 2 s, which stacked
roughly 120 primitives per probe at 60 fps while spider-walking. → UOmniWalkPro
Log noise cut for shipped games. 45 diagnostic Log-verbosity calls demoted to Verbose; only 3
remain — module start/stop and the nav-graph build result. None were stripped in Shipping before, because
the category is declared with compile-time verbosity All.
Configuration & Packaging
DefaultOmniWalk.ini had three dead keys and two dead sections — verified by grep, zero code
references. DefaultSurfaceTraceChannel had no matching property, and the trace channel was never seeded
from settings at all; [/Script/OmniWalk.OmniWalkSubsystem] and [/Script/OmniWalk.OmniWalkPro] could
never apply, because neither class is config-backed and ReplicationInterval / bReplicateToAllClients
did not exist. The file is now a single valid section, every key resolves, and the missing properties were
added. Channel values need the ECC_ prefix — a bare WorldStatic fails to import silently.
→ UOmniWalkSettings, Config/DefaultOmniWalk.ini
Trigger zones only tick when they have work. Every zone in a level ticked unconditionally; tick is
now enabled on demand. → AOmniWalkTriggerZone
Blueprint API
54 tuning properties are now runtime-settable.AdhesionForce, AlignmentSpeed, TraceDistance, SurfaceTraceChannel, bControlRotation, bEnableConvexTraversals, bProjectGravityFromSurface and
more were EditAnywhere only — editable in the details panel but unreachable from Blueprint, so you could
not ramp adhesion for a power-up or hand rotation to a montage. All are now BlueprintReadWrite.
→ UOmniWalkPro
16 const getters changed from BlueprintCallable to BlueprintPure — no more exec pins on IsGrounded, GetCurrentSurfaceNormal, IsWallSlidingEnabled, GetDefaultGravityUp, GetGravityRelativeDirection, GetCurrentGravityUp and others, so they can be used inline in
expressions. See Breaking below.
A further 13 getters had a redundant BlueprintCallable removed where BlueprintPure was already
present (AOmniWalkPatrolPath, UOmniWalkCinematic, UOmniWalkAIPatrol, AOmniWalkTriggerZone, UOmniWalkSurfaceResponse). These were already pure — no behavioural or pin change.
The plugin now exposes 30 BlueprintPure getters in total.
New Mag-Boot tuning knobs:MagLockAdhesionMultiplier, MagLockGroundFriction, MagLockBrakingDeceleration, HoverAdhesionMultiplier, HoverGroundFriction, HoverBrakingDeceleration.
New settings:DefaultSurfaceTraceChannel, bDefaultDebugVisualization, DefaultDebugDrawDuration, bWatchForRuntimeSpawns. → UOmniWalkSettings
Lifecycle
Auto-injection is no longer a one-shot snapshot. It ran once, 0.1 s after BeginPlay, so player
respawns, streamed-in levels and runtime-spawned NPCs were never considered. An optional spawn watcher
(bWatchForRuntimeSpawns, default on) now injects into anything tagged OmniWalk.Enabled that appears
later, deferred a tick so possession state is final. → UOmniWalkSubsystem
Validation
Automation coverage expanded from 2 to 5 core tests — 15 total with the editor suite — now covering
the replication-normal precision requirement, Mag-Boot preset scaling, and the debug-lifetime mapping.
Each pins a specific defect fixed above so it cannot regress. → OmniWalkProTests.cpp
Verified on four engine versions with byte-identical plugin source, confirmed by diff -rq:
Engine
Editor Dev
Shipping
Debug-draw stripped
Tests
UE 5.5.4
15/15
UE 5.6.1
15/15
UE 5.7.4
15/15
UE 5.8.2
15/15
Shipping verified at symbol level.dumpbin -symbols across all plugin Shipping objects shows zero engine DrawDebug* calls linked, on every engine version — the debug defect above is
structurally impossible in Shipping. The same objects in an editor build reference DrawDebugLine, DrawDebugSphere, DrawDebugPoint, DrawDebugDirectionalArrow and DrawDebugString.
Breaking
The BlueprintPure conversion breaks existing Blueprint graphs. The 16 genuinely converted
getters lose their exec pins, so UE recreates those nodes and drops the exec wires through them. Open
affected graphs and reconnect execution flow around the now-pure getter nodes. This is the reason for the
minor version bump. The other 13 cleaned-up getters were already pure and are unaffected.
bAutoSetGameInputMode now defaults to false. If you relied on AOmniWalkPlayerController
forcing Game-Only input, re-enable it explicitly.
bDebugVisualization now defaults to false on UOmniWalkPro, AOmniWalkPatrolPath and UOmniWalkSurfacePathFollower. Turn it on per-component, or project-wide via Project Settings → Plugins → OmniWalk → Default Debug Visualization.
Upgrade notes
Clean rebuild required — UPROPERTY / UFUNCTION specifiers changed, so UHT must regenerate
reflection data.