Damage Visualization plays a directional hit-reaction animation when the character receives enough damage from an external attacker. Use it to show where a bullet, melee strike, or another sourced hit came from while the additive Animator layer preserves the underlying locomotion pose.

The built-in ability selects one of four reactions: front-left, front-right, back-left, or back-right. Damage with no attacker, such as the standard fall-damage workflow, does not start this ability.

Before you begin

  • The character needs Ultimate Character Locomotion, a Health component that sends OnHealthDamage, and an Animator configured for UCC parameters.
  • The damage source must provide a non-null source owner as the attacker. A visual hit does not play when the Health event’s attacker is null.
  • The shipped Animator expects Damage Visualization to use Ability Index Parameter 10 and direction values 0 through 3.

Add Damage Visualization

  1. Select the character GameObject.
  2. In the Ultimate Character Locomotion component, expand Abilities.
  3. Select the plus button and add Damage Visualization.
  4. Keep Enabled selected and both Start Type and Stop Type set to Manual. The Health event starts the ability, and its completion trigger stops it.
  5. Place Damage Visualization near the top of the ability list. Its default index is 10; a higher-priority active nonconcurrent ability can prevent it from starting, while Damage Visualization can stop lower-priority nonconcurrent abilities.
  6. Keep Ability Index Parameter at 10 when using the shipped Animator Controller.
  7. Set Min Damage Amount and configure Damage Visualization Complete Event for either a duration or an animation event.
  8. Apply external damage from each side in Play Mode and confirm the four Animator branches match the hit positions.

Choose which damage shows a reaction

Min Damage Amount is the inclusive threshold for a reaction. The default is 0: damage starts the ability when its amount is equal to or greater than the threshold.

  • Raise the value when small damage-over-time ticks should not interrupt other ability logic.
  • Keep it low when every external impact needs visible feedback.
  • Damage below the threshold is ignored before direction selection.

The attacker check is separate from the amount. The Health component derives the attacker from the damage source’s source owner. If that value is null, Damage Visualization treats the damage as internal and does not start. This is why standard fall damage has no reaction, but it also means a custom projectile or hazard must identify its source owner when a reaction is expected.

How direction selects an animation

The ability transforms the damage position into the character’s local space at the moment of the hit:

AbilityIntData Local hit position Built-in reaction
0 Front and left Front Left
1 Front and right Front Right
2 Back and left Back Left
3 Back and right Back Right

The default calculation uses the hit position, not the force direction or attacker position. Supply the actual world-space impact point when dealing damage. A result of -1 prevents the ability from starting; the built-in calculation returns -1 when the local hit position has no front or back component.

The shipped transitions are in Additive Layer -> Damage Visualization. The additive layer lets the hit reaction overlay movement such as running or jumping, even though Damage Visualization itself is a nonconcurrent character ability whose list priority can affect other abilities.

Choose how the reaction completes

Damage Visualization Complete Event is an Animation Event Trigger with two modes:

  • Disable Wait For Animation Event to stop after Duration. The default duration is 0.2 seconds. Use this for simple, consistent reactions that do not require clip-specific timing.
  • Enable Wait For Animation Event to keep the ability active until OnAnimatorDamageVisualizationComplete is received. Add an Animation Event to every reaction clip that can play, set its function to ExecuteEvent, and use OnAnimatorDamageVisualizationComplete as its string value.

Choose the duration or event point at the moment the ability should release its priority. The visual animation can blend out through the Animator after that point.

How Damage Visualization runs

Damage Visualization listens for OnHealthDamage. It first checks Min Damage Amount and the attacker, then calls GetDamageTypeIndex with the damage amount, world position, force, and attacker.

When the returned index is not -1, the ability starts manually. Ability Index becomes 10 by default, and AbilityIntData exposes the selected direction index to the Animator. The ability either schedules its completion by Duration or waits for the Animator event.

Completion stops the ability and cancels any pending scheduled event. Destroying the character unregisters both damage and Animator-completion listeners.

Verify in Play Mode

  1. Keep the Ultimate Character Locomotion and Animator Inspectors visible.
  2. Apply damage equal to Min Damage Amount with a valid attacker and confirm Damage Visualization becomes active.
  3. Confirm Ability Index is 10 and AbilityIntData is 0, 1, 2, or 3 for front-left, front-right, back-left, and back-right hits.
  4. Confirm the corresponding additive reaction plays without replacing the underlying locomotion animation.
  5. Apply damage below Min Damage Amount and confirm the ability does not start.
  6. Apply damage with a null attacker, including fall damage, and confirm the ability does not start.
  7. In duration mode, confirm the ability stops after the configured Duration.
  8. In event mode, confirm every reaction clip sends OnAnimatorDamageVisualizationComplete and the ability stops at the intended frame.
  9. Start a lower-priority nonconcurrent ability, apply damage, and confirm Damage Visualization takes priority. Repeat with a higher-priority ability and confirm the intended priority rule.

Troubleshoot Damage Visualization

  • The character takes damage but the ability never starts: confirm the amount meets Min Damage Amount, the Health event has a non-null attacker, and Damage Visualization is enabled.
  • A projectile or hazard causes no reaction: check its DamageSource and source owner. Health sends a null attacker when no source owner is available.
  • Fall damage does not play a hit reaction: this is expected because fall damage has no external attacker.
  • The wrong directional animation plays: pass the world-space impact position rather than the attacker position or force vector, then verify the character’s facing at the damage frame.
  • Every hit plays the same clip: confirm the Animator transitions test AbilityIntData values 0 through 3 and the damage position changes between tests.
  • No reaction plays while another ability is active: place Damage Visualization above the ability it should interrupt. A higher-priority nonconcurrent ability can block it.
  • Movement or another action stops unexpectedly: Damage Visualization is nonconcurrent and can stop lower-priority nonconcurrent abilities. Adjust list priority only after deciding which response should win.
  • The ability stops before the visible reaction finishes: increase Duration or move OnAnimatorDamageVisualizationComplete later in the clip.
  • The ability remains active indefinitely: when Wait For Animation Event is enabled, confirm every reachable reaction clip sends OnAnimatorDamageVisualizationComplete with the exact string value.
  • A custom reaction never plays: confirm the override returns the expected new AbilityIntData value and the Additive Layer contains a transition for that value.

Developer reference

Override GetDamageTypeIndex to add another damage category. Return -1 to suppress the reaction. This example uses AbilityIntData 4 for damage above 20 and preserves the built-in directional result for other hits.

using UnityEngine;
using Opsive.UltimateCharacterController.Character.Abilities;

[System.Serializable]
public class MyDamageVisualizationAbility : DamageVisualization
{
    /// <summary>
    /// Returns the value assigned to the AbilityIntData Animator parameter.
    /// </summary>
    protected override int GetDamageTypeIndex(float amount, Vector3 position, Vector3 force, GameObject attacker)
    {
        if (amount > 20) {
            return 4;
        }

        return base.GetDamageTypeIndex(amount, position, force, attacker);
    }
}

Replace the built-in ability with the subclass so both do not respond to the same Health event. In the Animator Controller, add the custom clip to Additive Layer -> Damage Visualization and create a transition whose AbilityIntData condition equals 4.

Animator Controller Damage Visualization sub-state machine with a custom reaction selected when AbilityIntData equals 4

If the ability waits for an animation event, add OnAnimatorDamageVisualizationComplete to the custom clip as well.

The feature-specific runtime surface is intentionally small:

  • AbilityIntData: returns the selected damage type index.
  • GetDamageTypeIndex(float, Vector3, Vector3, GameObject): is the protected virtual extension point for reaction selection.
  • OnHealthDamage: supplies the amount, position, force, attacker, and hit Collider used to trigger the workflow.
  • OnAnimatorDamageVisualizationComplete: stops the active reaction.

Min Damage Amount and Damage Visualization Complete Event are serialized Inspector settings but are not exposed as public properties by the current Damage Visualization class.