Class AimDirectionCompression
Packs an aim direction into 32 bits as a quantised yaw/pitch pair, and — more importantly — exposes the quantisation itself so a producer can commit to the value it is about to send.
public static class AimDirectionCompression
- Inheritance
-
AimDirectionCompression
- Inherited Members
Remarks
Why this exists. The ability system is deterministic: every peer replays the same
input stream through the same simulation and derives the same ability objects locally, so
the inputs must be bit-identical everywhere. Aim used to travel as a
UnityEngine.Quaternion through FishNet's WriteQuaternion32, which is lossy. The
owning client simulated with its exact camera rotation while the server and every observer
simulated with the decoded one — so an owner's predicted shot and the server's authoritative
shot diverged by the quantisation error on every single cast.
The rule this type enforces. Quantise before you predict, never after. A producer calls Quantize(Vector3) and stores the result as the input; the wire then carries a value that is already exactly representable, so decoding it reproduces what the producer simulated with. Decode(uint) of Encode(Vector3) is the identity on any value that came out of Quantize(Vector3), which is what the round-trip tests assert.
Why direction rather than a rotation. Aim is only ever consumed as a direction —
KCCController.SetInputs takes rotation * Vector3.forward to build the movement
basis, and the ability path takes the same forward as its trace direction. Carrying a full
rotation shipped a degree of freedom (roll) that nothing reads and that made the value
harder to represent exactly.
Resolution is 360/65536 ≈ 0.0055° of yaw and 180/65535 ≈ 0.0027° of pitch (yaw wraps, so its last step joins the first; pitch spans pole to pole inclusive, so it has one fewer interval) — about 5 mm of lateral error at 50 m, comfortably below the precision any aiming decision depends on.
Fields
FallbackDirection
Direction used when asked to encode a zero-length or non-finite vector.
public static readonly Vector3 FallbackDirection
Field Value
- Vector3
Remarks
A deterministic fallback matters more than which direction it is: a NaN reaching the simulation would diverge peers permanently, whereas everyone agreeing on "forward" is merely wrong in a visible, debuggable way.
QuantizedFallbackDirection
FallbackDirection after a round trip through the packer — the vector every peer actually ends up simulating when an aim is missing.
public static readonly Vector3 QuantizedFallbackDirection
Field Value
- Vector3
Remarks
Not the same vector as FallbackDirection: encoding rounds the pitch to index
32768 rather than its exact half-step, so the decoded forward carries a tiny non-zero y.
Substituting the RAW fallback has the substituting peer simulate a direction the wire
cannot carry while every peer that received it simulates the decoded one — the same
quantise-at-the-producer rule the aim itself follows, in the one case where nothing else
is going right anyway. Computed once; Quantize is a full round trip.
Methods
Decode(uint)
Unpacks a direction previously produced by Encode(Vector3).
public static Vector3 Decode(uint packed)
Parameters
packeduintThe packed representation.
Returns
- Vector3
A unit direction.
Encode(Vector3)
Packs a direction into 32 bits: low 16 bits yaw, high 16 bits pitch.
public static uint Encode(Vector3 direction)
Parameters
directionVector3Direction to encode. Need not be normalised.
Returns
- uint
The packed representation.
Quantize(Vector3)
Rounds a direction onto the wire's representable set.
public static Vector3 Quantize(Vector3 direction)
Parameters
directionVector3Raw direction.
Returns
- Vector3
The direction the wire will carry.
Remarks
This is the call a producer makes before storing aim as input. Feeding the simulation the quantised value — rather than the raw one it happens to hold locally — is what keeps the owner, the server and every observer simulating identical input.
ToRotation(Vector3)
Rebuilds a rotation from an aim direction, for the code paths that still want one.
public static Quaternion ToRotation(Vector3 direction)
Parameters
directionVector3A direction, ideally already quantised.
Returns
- Quaternion
A rotation whose forward is
direction.
Remarks
Roll is not carried — nothing reads it — so this picks a reference up-axis and falls back to a second one when the direction is close to vertical, where UnityEngine.Quaternion.LookRotation(UnityEngine.Vector3, UnityEngine.Vector3) is otherwise degenerate. Deterministic for a given direction, which is the property that matters; a camera pitched to exact vertical would lose only its roll, which no consumer reads.