Table of Contents

Class AICombatSlots

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Hands each attacker its own standing spot in a ring around a shared target, so a pack surrounds its victim instead of piling onto one point.

public static class AICombatSlots
Inheritance
AICombatSlots
Inherited Members

Remarks

Why the NavMeshAgent's own avoidance is not enough. Unity's local avoidance is very good at what it does: two agents crossing paths will slide around each other rather than interpenetrate. But it is a local solver, and it is only ever given one problem to solve — "do not overlap right now". It has no opinion about destinations. When five attackers are all told to path to the same point, avoidance faithfully keeps them from overlapping while they all continue pushing toward that point, which produces the familiar shoving, orbiting scrum: agents jitter against each other, get shunted out of attack range, path back in, and repeat. No amount of tuning agent radius or avoidance quality fixes it, because the destinations themselves are in conflict.

The fix is to stop asking for the same point. Each attacker claims an angular slot around its target and paths to that, so the destinations are already separated by more than an agent diameter and avoidance is back to handling the incidental crossings it is good at.

Ring capacity is derived from geometry rather than configured: the number of agents of a given radius that physically fit on a circle of a given radius. Attackers beyond that capacity are placed on an outer ring rather than being squeezed in, which is what produces the natural "front rank and back rank" look when a large pack engages.

Server-only state, keyed by character ID. Entries are released on disengage and pruned when a target's ring empties.

Methods

Claim(long, long, float, float, out int, out int, out int)

Claims (or refreshes) this attacker's slot in the ring around a target.

public static void Claim(long targetID, long attackerID, float combatRadius, float agentRadius, out int slot, out int ring, out int ringCapacity)

Parameters

targetID long

The character being attacked.

attackerID long

The attacking character.

combatRadius float

Desired distance from the target for the innermost ring.

agentRadius float

The attacker's own agent radius.

slot int

The attacker's index within its ring.

ring int

Which ring the attacker is on. 0 is the innermost.

ringCapacity int

How many attackers that ring holds.

Clear()

Drops all slot state. Call on scene teardown.

public static void Clear()

GetAttackerCount(long)

Number of attackers currently engaging a target.

public static int GetAttackerCount(long targetID)

Parameters

targetID long

The target to query.

Returns

int

The attacker count.

GetRingCapacity(float, float)

How many agents of a given radius physically fit on a ring of a given radius.

public static int GetRingCapacity(float combatRadius, float agentRadius)

Parameters

combatRadius float

Ring radius.

agentRadius float

Attacker agent radius.

Returns

int

Slot count for the ring.

Remarks

Circumference divided by the space one agent needs, floored to at least one so a very tight melee radius still produces a usable ring rather than a division by zero.

GetSlotPosition(Vector3, int, int, int, float, float)

Computes the world position of a slot around a target.

public static Vector3 GetSlotPosition(Vector3 targetPosition, int slot, int ring, int ringCapacity, float combatRadius, float agentRadius)

Parameters

targetPosition Vector3

The target's position.

slot int

Slot index within the ring.

ring int

Ring index. 0 is innermost.

ringCapacity int

Slots on the ring.

combatRadius float

Distance from the target for the innermost ring.

agentRadius float

The attacker's agent radius, used to space outer rings.

Returns

Vector3

The world position this attacker should stand at.

Release(long)

Releases an attacker's slot.

public static void Release(long attackerID)

Parameters

attackerID long

The attacker giving up its slot.

ReleaseTarget(long)

Removes every slot around a target. Called when the target dies or despawns.

public static void ReleaseTarget(long targetID)

Parameters

targetID long

The target whose ring should be cleared.