Table of Contents

Class CharacterHitReaction

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

A short, purely cosmetic displacement of a character's model, played the moment a hit is predicted rather than when the server's correction arrives.

public class CharacterHitReaction : MonoBehaviour
Inheritance
Object
Component
Behaviour
MonoBehaviour
CharacterHitReaction
Inherited Members
MonoBehaviour.IsInvoking()
MonoBehaviour.CancelInvoke()
MonoBehaviour.StopCoroutine(Coroutine)
MonoBehaviour.StopAllCoroutines()
MonoBehaviour.destroyCancellationToken
MonoBehaviour.useGUILayout
MonoBehaviour.didStart
MonoBehaviour.didAwake
MonoBehaviour.runInEditMode
Behaviour.enabled
Behaviour.isActiveAndEnabled
Component.GetComponent<T>()
Component.TryGetComponent<T>(out T)
Component.GetComponentInChildren<T>()
Component.GetComponentsInChildren<T>()
Component.GetComponentInParent<T>()
Component.GetComponentsInParent<T>()
Component.GetComponents<T>()
Component.GetComponentIndex()
Component.CompareTag(TagHandle)
Component.transform
Component.transformHandle
Component.gameObject
Component.tag
Object.GetEntityId()
Object.GetInstanceID()
Object.GetHashCode()
Object.InstantiateAsync<T>(T)
Object.InstantiateAsync<T>(T, Transform)
Object.InstantiateAsync<T>(T, Vector3, Quaternion)
Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)
Object.Instantiate(Object, Vector3, Quaternion)
Object.Instantiate(Object, Vector3, Quaternion, Transform)
Object.Instantiate(Object)
Object.Instantiate(Object, Scene)
Object.Instantiate<T>(T, InstantiateParameters)
Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)
Object.Instantiate(Object, Transform)
Object.Instantiate<T>(T)
Object.Instantiate<T>(T, Vector3, Quaternion)
Object.Instantiate<T>(T, Vector3, Quaternion, Transform)
Object.Instantiate<T>(T, Transform)
Object.Destroy(Object)
Object.DestroyImmediate(Object)
Object.DontDestroyOnLoad(Object)
Object.DestroyObject(Object)
Object.FindObjectsOfType<T>()
Object.FindObjectsByType<T>(FindObjectsSortMode)
Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)
Object.FindObjectOfType<T>()
Object.FindFirstObjectByType<T>()
Object.FindAnyObjectByType<T>()
Object.FindFirstObjectByType<T>(FindObjectsInactive)
Object.FindAnyObjectByType<T>(FindObjectsInactive)
Object.FindObjectsByType<T>()
Object.FindObjectsByType<T>(FindObjectsInactive)
Object.ToString()
Object.name
Object.hideFlags

Remarks

Why not just predict the knockback. A knockback moves the target, and on the attacker's client that target is a peer driven by NetworkTransform. Displacing it locally is overwritten by the next transform update — one to three ticks away at the streaming LOD band — so the character snaps back, which reads worse than the delay it was meant to hide. The authoritative position cannot be predicted here and should not be.

What this does instead. It offsets MeshRoot, a child of the networked root that nothing else writes. The root keeps receiving the server's position untouched; the model leans off it and decays back to zero over ReactionSeconds. By the time the server's real displacement arrives the offset has largely resolved, so the two compose instead of fighting — the player sees the reaction on the frame they hit, and the translation catching up half a round trip later is invisible.

Cosmetic, and only cosmetic. Nothing here is read by the simulation, by hit detection, or by lag compensation — all of which work from the root transform and the position history. A client that lies to itself with this changes nothing anybody else can observe.

Fields

MaximumOffset

Metres of lean at full strength.

[Tooltip("Maximum lean distance in metres at full strength.")]
[Min(0)]
public float MaximumOffset

Field Value

float

Remarks

Deliberately small. This is a flinch, not a simulation of the knockback — the real displacement is the server's, and a large offset here would visibly double the motion when the authoritative one arrives.

ReactionSeconds

How long a reaction takes to decay back to rest, in seconds.

[Tooltip("Seconds for a hit reaction to decay back to rest.")]
[Min(0.01)]
public float ReactionSeconds

Field Value

float

Remarks

Short enough to be gone before the server's own displacement lands at a typical round trip, so the two do not visibly add. Longer than a couple of frames, or it reads as a glitch rather than as an impact.

Properties

IsPlaying

True while a reaction is still decaying.

public bool IsPlaying { get; }

Property Value

bool

Methods

Play(Vector3, float)

Starts a reaction in a world-space direction.

public void Play(Vector3 worldDirection, float strength = 1)

Parameters

worldDirection Vector3

Direction of the impact. Normalised internally; zero is ignored.

strength float

0 to 1, scaling MaximumOffset.

Remarks

Restarts rather than accumulates: a burst of hits produces one lean at the newest direction, not a model that walks away from its own root. Accumulating would let a multi-hit ability push the mesh arbitrarily far from the character it belongs to.

Silently does nothing without a mesh root — an NPC or a character whose model has not loaded has nothing to lean.

PlayOn(ICharacter, Vector3, float)

Plays a reaction on a character, if it has the component.

public static void PlayOn(ICharacter character, Vector3 worldDirection, float strength = 1)

Parameters

character ICharacter

The character being hit.

worldDirection Vector3

Direction of the impact.

strength float

0 to 1.

Remarks

A convenience so callers do not need a null-checked GetComponent at every hit site. A character without the component simply does not flinch, which is the correct degradation for something purely cosmetic.

ResetToRest()

Cancels any reaction and returns the model to rest immediately.

public void ResetToRest()

Remarks

Public so a pooling or teardown path can call it directly rather than relying on OnDisable firing — the object may be recycled without ever being disabled.