Use Decal Manager to place pooled impact marks and footprints, reject marks that extend beyond an exposed edge, and gradually retire older marks as new ones arrive.

Before you begin

Create one direct Surface System response before tuning decal capacity:

  1. Open Tools > Opsive > Ultimate Character Controller > Setup Manager.
  2. On Scene, under Manager Setup, select Add Managers.
  3. Select the scene’s Game object and confirm it has exactly one Surface Manager, Decal Manager, and shared Object Pool.
  4. Create a Surface Impact, Surface Effect, and Surface Type. Map the impact to the effect in the type’s Impact Effects list.
  5. Add Surface Identifier to a test collider, assign the Surface Type, and leave Allow Decals enabled.

The runtime can create fallback Decal Manager and Object Pool GameObjects when either singleton is missing, but an explicitly configured Game object gives the scene deterministic ownership and settings.

Configure Decal Manager

Select Game > Decal Manager and begin with the released defaults:

Field Default Released Version 3.2.0 behavior
Decal Limit 100 When the active decal queue reaches this value, its oldest entry moves to the weathered queue. It is not a hard limit on every visible decal.
Weathered Decal Limit 20 Each transfer into the weathered queue reduces every weathered material’s alpha by 1 / Weathered Decal Limit. When that queue reaches the value, its oldest entry moves to the final fade list.
Remove Fadeout Speed 10 Controls the per-frame interpolation of final-fade alpha toward zero. It is a speed, not a lifetime in seconds.

Keep Decal Limit and Weathered Decal Limit at least 1, and keep Remove Fadeout Speed positive. The Inspector does not constrain these integer fields; zero or negative values do not provide a useful bounded lifecycle, and a zero Weathered Decal Limit also makes the weathering division invalid.

Because each queue uses a >= comparison, the active and weathered queues normally retain at most one fewer entry than their configured values after a transfer. Decals already in the final fade list are additional. Tune the fields against the project’s peak impact rate rather than treating Decal Limit as a total object budget.

Build a compatible decal prefab

Decal Manager expects the decal data on the prefab’s root GameObject:

  1. Use a simple quad-style mesh whose local forward normal is positive Z. Its first four mesh vertices should represent the four placement-test corners.
  2. Add Mesh Filter on the root and assign a nonempty mesh with at least four vertices.
  3. Add a Renderer on the same root and assign a non-null material.
  4. Use a transparent material whose color alpha changes the visible result. Decal Manager edits Renderer.material.color.a while weathering and resets it to 1 when a pooled instance is reused.
  5. Keep the prefab at unit scale when Surface Effect will randomize its size. Avoid adding a Collider that could interfere with the placement raycasts.
  6. Save the prefab, create or select a Surface Effect, expand Decals, and add the prefab under Prefabs. Remove every null list entry.

The built-in placement test reads MeshFilter.mesh, the root Renderer, its material, and mesh.vertices[0] through [3]. A child-only mesh or renderer is not found. An invalid prefab is returned to the pool immediately and no decal remains.

Choose scale and edge behavior

Configure these fields on Surface Effect > Decals:

Field Default Decision
Prefabs Empty One prefab is selected randomly. Regular impacts update the previous-decal history and avoid that choice when alternatives exist. Footprints do not update the history, so consecutive footprints can repeat (or keep avoiding an earlier regular-impact decal).
Min Scale 1 Lower bound for the selected decal scale; the Inspector slider range is 0.01 to 2.
Max Scale 1 Upper bound for the selected decal scale; it cannot be lower than Min Scale in the custom Inspector.
Allowed Decal Edge Overlap 0.25 Shrinks the four tested corner positions toward the mesh origin. Its range is 0 to 0.5.

Released Version 3.2.0 treats scale 1 specially. When the random result is exactly 1, the instance uses the prefab’s local scale. For every other result, Decal Manager replaces local scale with (scale, scale, 1) rather than multiplying the prefab scale. Authoring the prefab at (1, 1, 1) keeps that discontinuity from changing the intended proportions. A footprint may then negate local X to mirror the chosen side.

Edge overlap works as follows:

  • 0 raycasts from the first four mesh vertices, requiring support beneath the full tested quad;
  • 0.25 tests points halfway from those vertices toward the decal origin; and
  • 0.5 skips the four placement raycasts entirely.

There is no Placement Tests toggle in released Version 3.2.0. Values below 0.5 always run the test. Each corner ray only needs to hit an eligible collider; it does not require all four rays to hit the original impact collider.

Decide whether a decal follows its receiver

After placing a decal 0.001 units above the hit point, Decal Manager checks the hit Transform’s local scale:

  • A uniformly scaled receiver becomes the decal’s parent, so the mark follows its movement and rotation.
  • A non-uniformly scaled receiver is not used as the parent. The mark is parented under Decal Manager to avoid inherited distortion, so it stays at its world placement and does not follow that receiver.

There is no Allow Non Uniform Decals control in this release. Use (1, 1, 1) receiver scale for the most predictable world size, use uniform scale when a mark must follow a moving receiver, or implement a project-owned moving-decal solution for non-uniform objects. Uniform parent scale still contributes to the decal’s final world scale.

Connect impacts and footsteps

A decal request normally travels through the Surface System:

  1. A projectile, item action, ability, or another caller supplies a RaycastHit and Surface Impact to Surface Manager.
  2. Surface Manager resolves a Surface Type from the hit collider and finds the Surface Effect mapped to that impact.
  3. Surface Identifier > Allow Decals and the fallback policy determine whether the effect may request a decal. This Boolean is cached per collider on its first lookup, so author it before Play Mode.
  4. Surface Effect selects one Decals > Prefabs entry and a scale between Min Scale and Max Scale.
  5. A normal impact requests a randomly rotated decal around the hit normal. Character Foot Effects uses the footprint overload, supplying the character’s forward direction and the left/right flip decision.

New Surface Type assets use the footprint path by default. A legacy or custom-authored type whose hidden Allow Footprints value is false sends Character Foot Effects through the regular, randomly rotated decal path instead.

Fallback Allow Decals gates cases where the Surface Impact or Surface Type was initially missing. Released Version 3.2.0 does not apply that gate reliably when both exist but their pair is missing and Surface Manager later switches to the fallback type. If a fallback must never leave a mark, map it to a Surface Effect with an empty Decals > Prefabs list. See Advanced Surface System Topics for the full fallback and cache boundaries.

Tune pooling and lifecycle

Every accepted decal is obtained with the shared Object Pool. A rejected prefab or edge placement is returned immediately. A weathered decal returns to the pool only after its final fade alpha reaches zero; it is disabled and stored beneath Object Pool until the same prefab is requested again.

The limits do not preallocate instances. For a frequently used decal, select Game > Object Pool, add a Preloaded Prefab row, assign the decal prefab, and set Count to a measured warm capacity. Preload only hot prefabs; the pool can still instantiate additional copies when all preloaded instances are active.

Decal lifetime is spawn-driven:

  1. New decals remain fully opaque in the active queue.
  2. Once Decal Limit is reached, each new accepted decal moves the oldest active decal to the weathered queue.
  3. Every such transfer reduces the alpha of all weathered decals. If no more decals arrive, their partial weathering stops indefinitely.
  4. Once Weathered Decal Limit is reached, the oldest weathered decal enters the final fade list.
  5. Only final-fade entries update by time. When one reaches zero alpha, Decal Manager returns it to the pool.

Use a separate project component when a mark requires a fixed time-to-live. Decal Manager exposes no per-decal duration and does not retire active or weathered entries merely because time passes.

Runtime lifecycle

  1. The explicit Decal Manager claims the static singleton when enabled. If none exists when a static spawn method is called, the package creates a DecalManager GameObject.
  2. Surface Effect asks Decal Manager for a regular mark or direction-aware footprint.
  3. The manager takes an instance from Object Pool, positions and rotates it at the hit, chooses its parent, applies scale, validates its mesh and material, and restores alpha to 1.
  4. For overlap values below 0.5, four short raycasts validate the shrunken mesh corners. A failed placement returns to the pool without entering a capacity queue.
  5. An accepted decal enters the active queue. New accepted decals drive active-to-weathered and weathered-to-final-fade transitions.
  6. Decal Manager enables its per-frame Update only while the final fade list has entries, then disables itself again when that list is empty. Static spawn calls still work while the component is disabled.
  7. Scene unload resets the static singleton reference. The queues are scene-local and are not carried into another scene.

Editor checkpoint

Before Play Mode, confirm that:

  • the scene has one explicit Decal Manager and Object Pool on Game;
  • Decal Limit and Weathered Decal Limit are at least 1, and Remove Fadeout Speed is positive;
  • every Surface Effect decal entry references a prefab rather than None;
  • each prefab root has Mesh Filter, a mesh with at least four meaningful corner vertices, a Renderer, and a transparent alpha-responsive material;
  • prefab scale is (1, 1, 1) unless the special scale-1 behavior is intentional;
  • every known Surface Type maps the intended Surface Impact to the Surface Effect;
  • each receiver has the intended authored Allow Decals value before its first lookup;
  • moving receivers that must carry marks have uniform scale;
  • no-decal fallback effects have an empty Prefabs list; and
  • optional Object Pool preload counts reflect measured bursts rather than the Decal Manager limits.

Verify on several surfaces in Play Mode

  1. For an observable test, temporarily set Decal Limit to 4, Weathered Decal Limit to 2, and leave Remove Fadeout Speed at 10.
  2. Prepare adjacent wood and metal colliders with different Surface Types. Map the same impact to visibly distinct decal prefabs, and add a third collider whose Surface Identifier has Allow Decals disabled.
  3. Trigger one impact near the center of each collider. Wood and metal should select their mapped marks; the no-decal collider should still run any configured audio or spawned object but leave no mark.
  4. Trigger regular impacts several times on one surface. Confirm their rotation varies and the Surface Effect does not immediately repeat the same prefab when alternatives exist.
  5. Trigger footsteps with distinct left/right flip settings. Confirm footprints face with the character and only the configured side mirrors.
  6. Hit the center and exposed edge of a large plane with overlap below 0.5. Confirm the center passes and an unsupported corner is rejected. Repeat at 0.5 and confirm the edge test is skipped.
  7. Place marks on a uniformly scaled moving receiver and a non-uniformly scaled moving receiver. Confirm only the uniformly scaled target carries its mark.
  8. Spawn enough accepted decals to exceed both small queue thresholds. Confirm older marks weather in arrival order, final-fade entries disappear smoothly, and disabled pooled instances accumulate beneath Object Pool for reuse.
  9. Stop spawning while a mark is only partly weathered. Confirm it remains at that alpha, demonstrating that weathering is driven by later accepted decals rather than elapsed time.
  10. Restore the production limits, repeat the expected peak burst, and inspect the Hierarchy, rendered result, and Profiler for acceptable active, weathered, fading, and pooled counts.

Troubleshooting and released-Version-3 limitations

Symptom Check Fix
No decal appears, but audio or particles do. Check Allow Decals, the resolved impact/type/effect pair, Decals > Prefabs, fallback policy, prefab validity, and edge rejection. Correct the authored route, remove null prefab entries, and test a valid quad prefab at the center of a large receiver.
A spawn logs a null-reference exception. A selected Prefabs element is None; Decal Manager’s low-level spawn path assumes a non-null original. Remove every null entry and keep the list empty when the effect should not request a decal.
The decal appears to spawn and immediately disappears. The prefab lacks a root Mesh Filter, mesh, Renderer, or material, or a corner placement ray missed. Put all required components on the prefab root and test with a simple four-corner quad away from edges.
Edge rejection is inconsistent. The first four mesh vertices are not the intended corners, another eligible collider satisfies a corner ray, or overlap is 0.5 and skips testing. Use a predictable quad mesh, isolate the test geometry, and select a value below 0.5.
Old decals never begin weathering. The active queue has not reached Decal Limit, or no accepted decals arrived afterward. Lower the threshold for the test or generate more accepted marks. Use a custom time-to-live for age-based removal.
A partly faded decal remains forever after impacts stop. It is still in the weathered queue; only new accepted decals change weathered alpha. Continue spawning until it enters the final fade list, or implement a time-based lifecycle.
More decals are visible than Decal Limit. Active, weathered, and final-fade queues coexist; the field is not a total cap. Budget all three queues, increase fade speed, reduce both queue thresholds, or own a hard-cap policy in project code.
Final-fade decals do not disappear. Remove Fadeout Speed is zero/negative, or Weathered Decal Limit is invalid. Restore positive values; the released defaults are 10 and 20.
A decal pops out instead of fading visibly. The material is opaque or its shader does not use color alpha for transparency. Use a transparent shader/material whose visible alpha follows Renderer.material.color.a.
A mark is too large, stretched, or changes size unexpectedly. The prefab is not unit scale, the chosen scale is exactly 1 versus another value, or a uniformly scaled parent contributes to world scale. Author the prefab at unit scale, use a controlled Min/Max range, and keep receiver scale at (1, 1, 1) where practical.
A mark does not follow a moving non-uniform object. Version 3.2.0 parents it under Decal Manager to avoid inherited distortion. Use a uniformly scaled receiver or a project-owned moving-decal implementation.
Changing Allow Decals during Play Mode has no effect. Surface Manager cached the Boolean for that collider during its first identifier lookup. Author it before the first hit, reload the scene, or implement the dynamic gate outside the built-in cache.
A fallback creates a decal with Fallback Allow Decals disabled. The original impact and type existed but their pair was missing; the later fallback-type route bypasses that gate. Add the explicit pair or use a fallback Surface Effect whose Prefabs list is empty.
Footprints rotate randomly instead of facing the character. A legacy/custom Surface Type has hidden Allow Footprints disabled, or the call used the regular decal path. Use the footprint path and verify Character Foot Effects direction/flip inputs. New Surface Types default to footprints enabled.
Pool errors or stale marks appear after project code removes a decal. The instance was destroyed or disabled outside Decal Manager/Object Pool ownership. Let Decal Manager retire its marks, or use ObjectPoolBase.Destroy only for project-owned instances created by that same pool.

Developer reference

DecalManager is in Opsive.UltimateCharacterController.SurfaceSystem. Its public configuration properties are DecalLimit, WeatheredDecalLimit, and RemoveFadeoutSpeed.

DecalManager.Spawn(GameObject, RaycastHit, float, float) places a regular, randomly rotated decal. DecalManager.SpawnFootprint(GameObject, RaycastHit, float, float, Vector3, bool) uses a direction and optional X-axis flip. Both methods are static, return void, and bypass Surface Manager’s impact mapping, identifier permission, and fallback policy when called directly. They do not report whether prefab validation or the edge test rejected the placement.

The active, weathered, final-fade, renderer, and mesh collections are private. There is no public per-decal remove method, count query, placement-result event, fixed-lifetime setting, or queue-reset API. Derive a separate project system when those controls are required rather than editing the manager’s internal lists.

Decals are transient visual feedback. Decal Manager does not save them, restore them after a scene load, or send a network message. A multiplayer game must reproduce an impact request on the clients that should see it and provide matching Surface System assets; Decal Manager itself performs no replication. Pool returns and scene-unload cleanup also publish no gameplay event.