Combat Integration¶
Massive Swarm System ships a small combat layer that connects swarm attack profiles to your game objects. This page covers every contract and component involved so you can wire attack damage into your own player, enemies, and destructibles.
Overview¶
When a swarm agent lands a melee strike or contact tick, it calls ApplyDamage on whatever it finds at the target. One interface covers it: implement IDamageable, or attach one of the ready-made sample components, and damage flows through.
The system is intentionally thin. There is no global health manager, no required ScriptableObject, and no dependency on Unity's physics event system. Implement that interface or subclass one of the ready-made components, and attack damage flows through automatically.
Contracts¶
These interfaces live in the core runtime assembly, MassiveSwarmSystem.Runtime, and are safe to implement in any of your own scripts. They stay in place when you remove the samples.
IDamageable¶
The contract for anything that can take damage.
public interface IDamageable
{
bool CanTakeDamage { get; }
bool ApplyDamage(float damage);
bool ApplyDamage(float damage, in SwarmDamageContext context);
}
The swarm checks CanTakeDamage before calling ApplyDamage. Return false from CanTakeDamage to make the object temporarily immune (invincibility frames, dead state, not yet spawned).
ApplyDamage should return true if damage was actually applied, false if ignored (already dead, zero damage, etc.).
The second overload receives hit metadata: knockback direction, hit point, hit normal. Use it to push a character controller away from the attacker or to spawn a directional blood decal. Swarm contact and melee damage always calls this one.
Both overloads are required, and that is deliberate
Swarm contact damage only ever calls the context overload. If that overload were optional you could write a component that compiles, registers as a target, gets chased, gets attacked, and never loses a point of health, with nothing logged to tell you why. Requiring it means the compiler catches that instead. If you do not want the metadata, one line covers it:
SwarmDamageContext¶
A struct passed alongside damage that describes how the hit happened.
| Property | Type | What it contains |
|---|---|---|
Source |
UnityEngine.Object |
The agent or weapon that caused the damage |
HitPoint |
Vector3 |
World-space impact position |
HitDirection |
Vector3 |
Normalized direction the hit came from |
HitNormal |
Vector3 |
Surface normal at the hit point |
PushbackStrength |
float |
Configured push magnitude from the attack profile |
HasHitPoint |
bool |
Guards before reading HitPoint |
HasHitDirection |
bool |
Guards before reading HitDirection |
HasHitNormal |
bool |
Guards before reading HitNormal |
HasSource |
bool |
Guards before reading Source |
HasPushback |
bool |
True when PushbackStrength > 0 |
Use SwarmDamageContext.None when you need an empty context (no hit metadata). Use SwarmDamageContext.FromHit(...) to build one with all fields populated.
GetPushbackDirection(receiverPosition, flattenToXZPlane = true) resolves a pushback vector from whichever hit data is available. It prefers HitDirection, falls back to the receiver-to-hit-point direction, then to HitNormal. With flattenToXZPlane left at its default the Y component is zeroed before the vector is normalized, so ground knockback stays level. Pass false when you want the vertical component kept, for example to launch a target upward. Either way the result feeds straight into a CharacterController.Move or Rigidbody.AddForce call.
SwarmHitPoint¶
A static helper that turns a physics cast hit into a contact point safe to put in a SwarmDamageContext. Read this one before you write your own weapon, because the bug it fixes is easy to hit and hard to recognize.
A sweep that starts already overlapping a collider comes back degenerate: Unity reports distance 0, a zero-vector point, and a normal that only mirrors the sweep direction. Pass RaycastHit.point straight through and your hit VFX and floating damage numbers spawn at the world origin instead of on the agent. A weapon swinging through a packed swarm starts overlapped on most frames, so in this asset that case is the normal one, not the edge case.
using MassiveSwarmSystem.Runtime.Combat;
Vector3 contact = SwarmHitPoint.Resolve(hit, sweepOrigin);
var context = SwarmDamageContext.FromHit(this, contact, direction, hit.normal, pushback);
| Method | What it does |
|---|---|
Resolve(in RaycastHit hit, Vector3 referencePoint) |
Returns hit.point for a real sweep. For a zero-distance overlap it returns the closest point on the collider to referencePoint, which is normally your sweep origin |
OnCollider(Collider collider, Vector3 referencePoint) |
The closest-point half on its own, for overlap queries that never produce a RaycastHit |
Box, sphere, capsule and convex mesh colliders resolve exactly. Concave meshes and terrain hand the input back unchanged from Collider.ClosestPoint, so those fall back to the collider bounds, then to its transform position. It lives in MassiveSwarmSystem.Runtime, so it survives sample removal. SwarmProjectile and RotatingWeapon both call it, and copying either of them gets you the correct behavior for free.
ITargetable¶
Marks an object as a valid auto-target for ProjectileWeapon and other targeting systems.
public interface ITargetable
{
bool IsTargetable { get; }
Vector3 TargetPoint { get; }
Transform AimTransform { get; }
}
IsTargetable lets you gate whether a weapon can pick this object right now. Return false when the object is dead or out of play. TargetPoint is the world-space position the weapon aims at; for a character it should be the body center, not the pivot. AimTransform is used by the targeting system to resolve hierarchy ownership and avoid self-hit.
Target Body Radius¶
The swarm reads how wide the target's body is from the SwarmTarget component on the target GameObject. See Targets for the body-radius source dropdown (Auto Detect / Manual) and the inspector readout.
All three sample target components (PlayerCombatTarget, DestructibleCombatTarget, SwarmAgentCombatTarget) implement ITargetable. They are damage handlers, and the body radius comes from the adjacent SwarmTarget component, not from the combat target component.
DamageableBase¶
DamageableBase is an abstract MonoBehaviour that implements IDamageable for you. It manages a health pool, fires events, and calls protected virtual methods so subclasses can respond without re-implementing the core health logic.
This one ships with the samples, not the core runtime
DamageableBase and CombatTargetBase compile into MassiveSwarmSystem.Samples.StarterKit, which is the removable Starter Combat Kit tier. The interfaces above are different: they live in MassiveSwarmSystem.Runtime and survive sample removal. If you subclass DamageableBase in your own scripts, either keep the Starter Kit installed or copy the two files into your own assembly first. See Samples & Cleanup.
When to use it: you want health tracking with events out of the box. Subclass it instead of implementing IDamageable directly.
When to use raw interfaces instead: you already have a health system and just need the swarm to call into it. Implement IDamageable directly on your existing component.
Inspector fields¶
| Field | What it controls |
|---|---|
| Max Health | Starting and maximum health value |
| Current Health | The live value. It is serialized, so you can watch it fall in the inspector during Play mode |
| Reset Health On Enable | When on, health is restored to maximum each time the component enables |
Events¶
| Event | Signature | Fires when |
|---|---|---|
OnHealthUpdated |
Action<float> |
Any health change (argument is the delta) |
OnDamageTaken |
Action<float, SwarmDamageContext> |
Damage was applied (argument is the amount taken) |
OnHealthRestored |
Action<float> |
RestoreHealth actually raised current health (argument is the amount restored). It does not fire for ResetHealth or for the refill that Reset Health On Enable performs |
OnDied |
Action<SwarmDamageContext> |
Health reached zero |
Runtime API¶
Read and write health from your own scripts without touching the serialized fields.
| Member | Type | What it gives you |
|---|---|---|
MaxHealth |
float, read-only |
The configured maximum |
CurrentHealth |
float, read-only |
Live health |
CurrentHealthNormalized |
float, read-only |
Health as 0..1, already clamped. This is the one to drive a fill bar with |
IsAlive |
bool, read-only |
True while health is above zero |
CanTakeDamage |
bool, read-only, virtual |
Whether a hit would land right now. Auto-targeting reads this, so overriding it changes who the swarm picks |
IsInvulnerable |
bool, read/write |
God mode. Damage is absorbed and health never changes |
RestoreHealth(float amount) |
bool |
Heals, clamped at max. Returns whether anything changed, and fires OnHealthRestored with the amount actually gained |
ResetHealth() |
void |
Refills to maximum. Deliberately does not fire OnHealthRestored |
IsInvulnerable is not serialized and has no inspector field, so nothing in a shipped scene can turn it on. It is also kept out of CanTakeDamage on purpose: an invulnerable target stays selectable, reachable and attackable on exactly the normal schedule, and the only difference the player sees is that it survives. That makes it the right switch for a measurement rig or a scripted invulnerability window, and the wrong one for making the swarm ignore something.
Two things to watch when wiring a heal pickup. RestoreHealth returns false for an amount of zero or less and for a target already at full health, so use the return value rather than assuming the heal landed. And OnHealthRestored reports the clamped gain, not the amount you asked for, so a 50-point heal on a target 10 points from full reports 10.
Protected virtual methods¶
Override these in your subclass to add behavior at the right moment.
| Method | When it runs |
|---|---|
HandleDamageTaken(float, in SwarmDamageContext) |
After each damage application, before events fire |
HandleDeath(in SwarmDamageContext) |
When health first hits zero, before the OnDied event |
CanTakeDamageInternal() |
Called by CanTakeDamage to add your own immune conditions |
Do not override HandleDeath directly if you subclass CombatTargetBase
CombatTargetBase seals HandleDeath to guarantee the death VFX always spawns before your subclass logic. Override OnDeath instead. See CombatTargetBase below.
CombatTargetBase¶
CombatTargetBase extends DamageableBase and adds a pooled death-effect VFX system. All three ready-made target components inherit from it.
When to subclass it: you want death burst VFX plumbing without writing it yourself, or you want to keep your component compatible with the sample scene setup.
When to use plain DamageableBase instead: you don't need a death effect, or you're managing VFX in your own code.
Inspector fields (Death group)¶
| Field | What it controls |
|---|---|
| Death Effect Prefab | Optional pooled VFX prefab spawned when health hits zero. Leave empty to skip |
| Death Effect TTL | Seconds the effect lives before returning to the pool |
| Death Effect Anchor | Pivot uses this GameObject's pivot (or simulation pose for swarm agents). Center uses the world-space center of the resolved renderer's bounding box, useful when the pivot is at the character's feet |
| Death Effect Offset | World-space offset added to the resolved anchor position. Use a positive Y value to lift the burst above ground |
| Death Effect Anchor Renderer | Optional explicit renderer used when anchor mode is Center. Auto-finds the first renderer in the hierarchy if left empty |
Subclassing CombatTargetBase¶
Override OnDeath(in SwarmDamageContext context) to run your type-specific death response. The death VFX always spawns before OnDeath is called.
protected override void OnDeath(in SwarmDamageContext context)
{
// Your logic here: disable, destroy, trigger animation, etc.
}
Override GetDeathEffectPivot() to return a custom spawn position when the visual transform pivot is not where you want the burst, for example the center of a bounding box or a simulated pose position.
Sample Components¶
Every sample component on this page compiles into MassiveSwarmSystem.Samples.StarterKit, the Starter Combat Kit tier, and works as-is in your scenes. They are also the reference implementations to copy when you need custom behavior.
Add them via Component → Massive Swarm System → Combat → ... or by searching the Add Component menu.
PlayerCombatTarget¶
Use this on the player character to handle swarm damage and auto-targeting by ProjectileWeapon.
Inherits from CombatTargetBase. Implements ITargetable.
Inspector fields
| Field | What it controls |
|---|---|
| Aim Point | Optional override for the aim point reported to targeting systems. Falls back to the CharacterController center, then a collider center, then the Transform pivot |
| Is Targetable | Lets you remove this object from targeting lists without disabling the component |
| Disable Game Object On Death | When on, the whole GameObject is deactivated when health reaches zero |
Body radius for reach calculations is configured on the adjacent SwarmTarget component (typically Auto Detect from the player's CharacterController / collider).
Health and death events come from DamageableBase. Subscribe to OnDied, OnDamageTaken, or OnHealthUpdated in your player controller to drive UI and game state.
DestructibleCombatTarget¶
Use this on world objects that should be destroyable by swarm agents or projectiles: barricades, crates, turrets, doors.
Inherits from CombatTargetBase. Implements ITargetable.
Inspector fields
| Field | What it controls |
|---|---|
| Aim Point | Optional override for the aim point. Falls back to the collider center, then the Transform pivot |
| Is Targetable | Removes this object from targeting lists while keeping it alive |
| Death Response | Disable Game Object (default), Destroy Game Object, or None, which lets the death VFX and debris remain |
| Disable Colliders On Death | When on, all Collider components on this GameObject and its children are disabled on death. Most useful with Death Response = None so agents can pass through the debris |
Body radius for reach calculations is configured on the adjacent SwarmTarget component (Auto Detect picks up the collider).
SwarmAgentCombatTarget¶
Use this when individual swarm agents should be damageable, for example when the player fires a ProjectileWeapon at the swarm.
Inherits from CombatTargetBase. Implements ITargetable. Requires a SwarmAgent component on the same GameObject.
This component lives on the agent visual GameObject
Swarm agent visuals are pooled and reused. Do not cache a reference to SwarmAgentCombatTarget beyond the current frame, because the GameObject may be returned to the pool at any moment. Subscribe to OnDied or OnDamageTaken in the same frame you resolve the component, and unsubscribe when done.
Inspector fields (Damage Response group)
| Field | What it controls |
|---|---|
| Fallback Pushback Strength | Pushback magnitude used when the damage context has a pushback direction but no explicit strength |
| Pushback Strength Multiplier | Scales all incoming pushback before it is applied to the simulation. Use to tune how far agents are knocked back |
| Knockback Duration | How long the external-force control window lasts after a hit. Negative values use the default from SwarmSettings |
| Flatten Pushback To Movement Plane | When on, the pushback direction is flattened to the XZ plane before being applied, preventing agents from launching vertically |
| Death Dissolve Duration | Seconds the VAT mesh dissolves between death and pool return. Set to 0 to skip the dissolve and despawn instantly. |
Body radius. This component does not carry one, and the shipped MSS Combat Agent prefab has no SwarmTarget on it. An agent only needs a body radius when something is treating it as a target in return, which means a second swarm chasing these agents. Add SwarmTarget yourself for that case and its Auto Detect will pick up the agent's collider.
Death behavior. When health hits zero, the component calls SwarmManager.DespawnAgentAsDying (if dissolve duration is greater than zero) or SwarmManager.DespawnAgent (instant). The simulation slot is freed and the visual is returned to the pool.
Scripting per-agent reactions. Once you have a hit callback here, you can reach SwarmAgent on the same GameObject to apply knockback (AddImpulse) or a damage flash (PulseTint). See SwarmAgent for the full API reference and a copy-pasteable knockback recipe.
Hit detection: decide which position you mean
The agent's drawn body, and the collider riding on it, does not sit exactly on the simulation position. Visual Interpolation offsets it by up to one fixed step of movement. Render Smoothing can add several units once a crowd packs in: measured runs at around 3000 agents settle near 4 units apart. That gap is deliberate, so raycasts and collider queries hit what the player actually sees. The swarm's own contact damage and attacks are unaffected either way, because they always resolve against the simulation position.
Two rules follow. If your hit detection goes through the agent's collider (raycasts, projectile overlaps, triggers), it already agrees with the screen, so leave it alone. If it computes hits from positions instead (distance checks, melee reach, server-side validation), read the true position through the SwarmManager per-agent API or a SwarmAgentHandle rather than the visual Transform. Do not mix the two. A body drawn a couple of units off can pass your collider test and then fail your position check on the same hit.
Solid or trigger is a setting, not a fixed property
Every agent gets a collider at spawn so your queries can find it. Whether it is solid or a trigger is Agent Collider Is Trigger on SwarmSettings, off by default. Solid is what makes an agent a physical obstacle to Unity physics, so a CharacterController walking into the crowd shoves through 500 bodies. Turn it on and agents stay findable by queries while physics ignores them, at the cost of swapping OnCollisionEnter for OnTriggerEnter in your own scripts. The swarm's body blocking never uses these colliders, so it behaves the same either way.
On-Hit Feedback: Damage & Heal Numbers¶
The StarterKit ships drop-on feedback helpers that react to the health events any DamageableBase fires: a hit flash (DamageFlashFeedback, white at 75% alpha by default), a world-space health bar (HealthBarDisplay), and floating damage/heal numbers (FloatingTextFeedback).
Add them from the Scene View overlay or the Add Component menu. The two overlay groups do not offer the same set. Swarm Agent appears for agent prefabs and carries Flash When Hit, Spawn On Death and Floating Combat Text. Health Bar lives in the Combat Helpers group, which only shows for objects with a SwarmTarget.
FloatingTextFeedback¶
Shows rising damage and heal numbers over anything with a DamageableBase. It listens for OnDamageTaken and OnHealthRestored and hands them to a text manager that spawns and pools the numbers. There is no per-agent Update, so it scales to large swarms.
Add it with the overlay's Floating Combat Text button or via Component → Massive Swarm System → Combat → Floating Text Feedback. It has to sit on the same GameObject as the damageable it reads.
Inspector fields
| Field | What it controls |
|---|---|
| Damage Style | Style asset for damage numbers. Auto-fills with MSS Floating Text Damage when the component is added |
| Heal Style | Style asset for heal numbers. Auto-fills with MSS Floating Text Heal when the component is added |
| Use Hit Point | On: a damage number spawns at the weapon's hit point when the hit carried one, otherwise above the target. Off: always above the target. Heal numbers always spawn above the target |
| World Offset | Where numbers spawn relative to the target when no hit point is used (world units) |
Already on the shipped combat agents
The MSS Combat Agent prefab and the demo VAT guardians carry FloatingTextFeedback by default, so the sample swarms pop numbers the moment you shoot them. Remove the component from a prefab if you don't want them there.
Styling the numbers. A FloatingTextStyle asset sets the color, font size, how long a number lasts, how far it rises, its sideways drift, and a leading character (the shipped heal style uses +). It also holds the merge window: repeated hits on one target inside that window fold into a single climbing number instead of stacking. Duplicate a shipped style, tweak it, and drop it into the slot to restyle without touching code. Under Advanced the style also points at the label prefab a number is drawn on. That slot fills itself in when the style is created and most projects never touch it, but because it is a direct reference rather than a lookup by name, the label art only reaches your build when something in it actually shows a number.
The text manager. A FloatingTextManager owns the pool and ticks every number from one Update. It creates itself the first time a number is shown, so you never place one by hand. To change the on-screen cap (64 by default, oldest recycled past it) or swap the font on every number, add an MSS Floating Text Manager to the scene and set its fields.
DamageFlashFeedback¶
Tints the target's renderers on hit and fades the tint out. Safe on a whole swarm: one shared driver ticks every flash, and only a component that is currently flashing registers with it, so an agent nobody has shot costs nothing per frame. It handles VAT-animated agents as well as ordinary renderers.
| Field | What it controls |
|---|---|
| Visual Root | Transform whose renderers get tinted. Falls back to this object's own Transform, which is usually what you want |
| Flash Color | The overlay color. Default is white at 75% alpha |
| Flash Duration | How long one flash lasts, in seconds. Default 0.12 |
| Flash Curve | Tint strength over that duration. The default ramps from full to nothing, so the flash decays. Flatten it for a hard blink |
| Use Unscaled Time | Keeps the flash running at normal speed while the game is paused or in slow motion |
HealthBarDisplay¶
A world-space bar on a Canvas that fills from a DamageableBase and turns to face the camera.
| Field | What it controls |
|---|---|
| Fill Image | The UI Image whose fill amount tracks health. Its Image Type must be Filled or nothing moves |
| Target | The DamageableBase to read. Leave it pointing at the object the bar belongs to |
| Billboard Camera | Camera the bar turns toward. Falls back to Camera.main at startup |
Not for every agent in the swarm
This one billboards in its own LateUpdate, so each instance costs a Unity callback every frame. That is fine for the handful of things it was built for: the player, a boss, a few turrets. Put it on a thousand agents and you have added a thousand callbacks per frame, which is exactly the cost this asset exists to avoid. If you want bars over swarm agents at scale, drive the billboarding from one shared updater instead, the way DamageFlashFeedback does.
ProjectileWeapon¶
A self-contained turret/ranged-weapon component that finds the nearest ITargetable, rotates toward it, and fires pooled SwarmProjectile instances. Designed for the sample turrets but works on any GameObject.
Add it via Component → Massive Swarm System → Combat → Projectile Weapon.
The weapon also needs a SwarmProjectile prefab assigned. That prefab is a thin pooled MonoBehaviour that moves itself in Update and performs sphere-cast collision in FixedUpdate, calling IDamageable.ApplyDamage on the first damageable it hits.
Targeting group¶
| Field | What it controls |
|---|---|
| Muzzle | Transform used as the raycast origin and projectile spawn point. Falls back to the weapon Transform |
| Targeting Radius | Sphere radius (world units) searched each fixed frame for valid targets |
| Target Mask | Layer mask for the overlap query. Only colliders on these layers are considered |
| Target Trigger Interaction | Whether the overlap query includes trigger colliders |
| Overlap Capacity | Maximum number of colliders the overlap buffer can hold per query |
Turret Aim group¶
| Field | What it controls |
|---|---|
| Yaw Pivot | Optional Transform rotated horizontally toward the target. Falls back to the weapon Transform |
| Pitch Pivot | Optional Transform rotated vertically. Usually a child of the yaw pivot |
| Yaw Limits | Horizontal rotation limits (min and max). -180 to 180 allows full rotation |
| Pitch Limits | Vertical rotation limits (min and max), clamped to ±89 |
| Aim Turn Speed Degrees | Rotation speed in degrees per second. Set to 0 to snap instantly |
| Fire Aim Tolerance Degrees | Maximum angle between current aim and target before a shot is allowed |
Sight group¶
| Field | What it controls |
|---|---|
| Require Line Of Sight | When on, the weapon only fires if no obstructing collider is between muzzle and target |
| Sight Obstruction Mask | Layers that block line of sight. Defaults to Nothing, so no layer blocks sight until you assign one |
| Sight Radius Padding | Extra radius for sight sphere casts. Small non-zero values catch thin blockers near the path |
Sight Obstruction Mask defaults to Nothing
Out of the box, Require Line Of Sight is on but no obstruction layers are set, so walls and obstacles never block firing. Assign the relevant layers (walls, floors, cover) to get expected behaviour.
Firing group¶
| Field | What it controls |
|---|---|
| Projectile Prefab | The SwarmProjectile prefab to spawn. Required; the weapon does nothing without it |
| Projectile Root | Optional parent Transform for spawned projectiles (keeps the hierarchy tidy) |
| Owner Root | Optional Transform whose hierarchy is ignored by targeting and self-hit checks. Falls back to transform.root |
| Fires Per Second | Fire rate |
| Damage | Damage applied to the first damageable hit by each projectile |
| Projectile Pushback Strength | Pushback strength passed to SwarmDamageContext.FromHit on impact |
| Projectile Speed | Travel speed in world units per second |
| Projectile Lifetime | Seconds a projectile stays in flight before it despawns itself, when it has not hit anything |
| Projectile Hit Radius | Sphere-cast radius for collision detection. Zero uses a pure raycast |
| Projectile Collision Mask | Layers the projectile collides with |
| Projectile Trigger Interaction | Whether projectile collision includes trigger colliders |
| Projectiles Per Shot | Number of projectiles launched each shot. Above 1, the extra projectiles fan out across the multi-shot spread |
| Multi Shot Spread Degrees | Total fan angle spread across the projectiles when more than one fires per shot |
Pool sizes are configured on the SwarmPoolManager component under its Shared Pool Defaults group, not on this component: Initial Pool Size (default 64) is how many instances are prewarmed the first time a prefab is requested, and Max Pool Size (default 512) caps how many are kept alive for reuse. The manager keeps three separate sets of pools, one for projectiles, one for effects and one for everything else spawned through it, so a burst of impact VFX cannot starve the bolts.
Audio and Muzzle Flash groups¶
Both are optional. Leave them empty and the weapon fires silently with no flash.
| Field | What it controls |
|---|---|
| Audio Source | The AudioSource that plays the fire sound. Put one on the weapon or its muzzle. The rest of the Audio group stays greyed out until this is assigned |
| Fire Sfx | Clip played once per shot. A volley of several projectiles still plays one sound |
| Fire Volume | Volume passed to PlayOneShot |
| Fire Pitch Range | Minimum and maximum pitch. Each shot draws a random value in between, so repeated fire does not sound looped |
| Muzzle Flash Prefab | VFX spawned at the muzzle on each shot. It goes through the same SwarmPoolManager as the projectiles, so it is pooled rather than instantiated |
| Muzzle Flash TTL | Seconds before the flash is released back to the pool |
The SwarmProjectile prefab¶
The weapon fires these, but the impact belongs to the projectile, so this is where you add a spark on hit. Add the component via Component → Massive Swarm System → Combat → Swarm Projectile. Speed, damage, lifetime and layer masks are passed in by whatever launches it; the fields below are authored on the prefab.
| Field | What it controls |
|---|---|
| Hit Effect Prefab | VFX spawned at the impact point when the bolt hits something damageable. Pooled through SwarmPoolManager. Empty means no impact VFX |
| Hit Effect TTL | Seconds before that VFX returns to the pool. Default 2 |
| Hit Effect Orientation | Hit Normal (default) faces the effect away from the surface it struck, Bullet Forward matches the direction of travel, Identity keeps whatever rotation the prefab was authored with |
| Hit Effect Rotation Offset Euler | Extra rotation applied on top of the chosen orientation. Use it when the prefab's own forward axis is not the one you want pointing outward |
| Trail | Optional TrailRenderer for a tracer streak. Assign the one on the prefab, not a scene object. It is cleared on launch, so a recycled bolt does not draw a line from wherever it last died |
Impact points go through SwarmHitPoint, so a point-blank shot into a packed crowd puts the effect on the agent rather than at the world origin.
Choosing What to Use¶
| Your situation | What to add |
|---|---|
| Player character needs health + swarm damage + auto-targeting | PlayerCombatTarget |
| World prop needs health + swarm damage + auto-targeting | DestructibleCombatTarget |
| Individual agents need to take player damage | SwarmAgentCombatTarget on the agent prefab |
| You want a ready-made turret that fires at agents | ProjectileWeapon + a SwarmProjectile prefab |
| You want floating damage / heal numbers over a target | FloatingTextFeedback (one click from the overlay; already on the shipped combat agents) |
| You have your own health system and just need swarm damage to call into it | Implement IDamageable directly |
| You want health tracking with events but no VFX | Subclass DamageableBase |
| You want health tracking + death VFX | Subclass CombatTargetBase and override OnDeath |
For the reach formula that governs when an agent can land a hit, see Attack Profile: How Reach Is Measured.
Sample Helpers¶
Lightweight components for adding melee and contact damage to non-swarm GameObjects. They use the same IDamageable contract as the rest of the combat layer.
Add them via Component → Massive Swarm System → Combat → ... or by searching the Add Component menu.
Rotating Weapon¶
A sweeping melee weapon that rotates between two angles, performs a sphere-cast + overlap-sphere pass along the arc, and calls IDamageable.ApplyDamage on anything it touches. Each activation hits a given target at most once, regardless of how many frames the sweep overlaps it.
Use this for enemies or hazards that need a procedural swing attack: spinning blades, arm swings, area denials.
Timing
| Field | What it controls |
|---|---|
| Swing Duration | How long one full sweep takes (start angle to end angle) |
| Delay Between Activations | Idle time after a swing completes before the next one starts |
| Initial Delay | Wait before the very first swing after the component enables |
Swing Rotation
| Field | What it controls |
|---|---|
| Swing In World Space | On by default. Holds the arc at a fixed world orientation, so the owner turning never spins it, while the weapon still follows the owner's position. Turn it off to swing relative to the owner's facing, like a handheld slash |
| Swing Plane | XZ rotates the weapon around the Y axis (top-down sweep); XY rotates around the Z axis (side-on sweep) |
| Start Angle Degrees | Local rotation angle at the beginning of the sweep |
| End Angle Degrees | Local rotation angle at the end of the sweep |
Hit Sphere
| Field | What it controls |
|---|---|
| Hit Sphere Distance From Base | How far along the weapon the hit sphere sits. This is a local offset, so it scales with the weapon transform, and with Size Multiplier at runtime, rather than being a fixed world distance |
| Hit Sphere Radius | Radius of the hit sphere. Scaled by the GameObject's lossy scale at runtime |
| Query Capacity | Maximum colliders processed per sweep or overlap query |
| Collision Mask | Layers the swing physically checks. Defaults to Everything, so walls and props can block targets behind them |
| Target Mask | Layers eligible to take damage. Colliders outside this mask can still block the swing through Collision Mask |
| Query Trigger Interaction | Whether the query includes trigger colliders |
Damage
| Field | What it controls |
|---|---|
| Damage | Flat damage passed to IDamageable.ApplyDamage |
| Pushback Strength | Written into SwarmDamageContext for receivers that use it |
| Owner Root | Optional transform excluded from hit detection and written as the damage instigator. Falls back to transform.root if not set |
Visuals
| Field | What it controls |
|---|---|
| Visual Root | Optional child object toggled on/off during the swing. When not set, the component enables and disables child Renderer components instead |
| Use Scale Tween | When on, the weapon scales in at swing start and out at swing end |
| Scale Tween Duration | Duration of the scale-in and scale-out phases |
Audio
Optional. With no Audio Source the weapon swings silently and the rest of the group stays greyed out.
| Field | What it controls |
|---|---|
| Audio Source | The AudioSource that plays both sounds. Put one on the weapon |
| Swing Sfx | Whoosh played at the start of every swing, whether or not it connects |
| Swing Volume | Volume for the whoosh |
| Swing Pitch Range | Minimum and maximum pitch, drawn at random per swing so a spinning blade does not sound like a loop |
| Hit Sfx | Impact sound, played once per swing the first time the arc connects. A swing that hits five agents still plays it once |
| Hit Volume | Volume for the impact |
Debug
| Field | What it controls |
|---|---|
| Draw Debug Gizmos | Draws the swing arc and hit sphere in the Scene view. On by default |
Growing the weapon at runtime
SizeMultiplier is a script-only property, with no serialized field and nothing to find on the prefab, so it is worth knowing it exists. It applies uniform growth on top of the authored local scale and moves the model, the swing reach and the hit-sphere radius together, which means what the player sees is what the swing hits. 1 is the authored size, and it clamps to the range 0.1 to 8.
That is the whole implementation of a bigger-weapon upgrade.
Spawn Prefab On Death¶
Spawns one pooled prefab when the DamageableBase on the same GameObject reports OnDied. Loot drops, gibs, a scorch decal, an ammo pickup. It works on anything with a DamageableBase, not just swarm agents: props, towers, the player.
Add it with the overlay's Spawn On Death button or via Component → Massive Swarm System → Combat → Spawn Prefab On Death.
| Field | What it controls |
|---|---|
| Prefab | What to spawn, through SwarmPoolManager. Empty disables the component |
| Initial Pool Size | How many to prewarm the first time this prefab is requested. Default 16. Later spawns reuse the existing pool and ignore this value, so raising it after the first request changes nothing |
| Spawn Offset | World-space offset from the dying object's pivot. The default lifts the spawn 0.3 units so a pickup does not appear inside the floor |
| Inherit Rotation | On, the spawned instance takes this transform's rotation. Off, it spawns with identity rotation |
This is separate from the death VFX slot on CombatTargetBase, which keeps its own anchor and centering rules for particle bursts. Use that one for the death effect and this one for something that stays in the world.
Touch Damage Source¶
Applies damage to any IDamageable that enters its trigger collider. Requires a trigger collider on the same GameObject. Adds a kinematic Rigidbody automatically at runtime if none is present.
Use this for hazards or enemies that damage on contact: lava zones, spinning saws, enemy bodies.
Damage
| Field | What it controls |
|---|---|
| Damage | Amount applied per damage event |
| Reapply Delay | Seconds between re-applications while the target stays inside. Set to 0 or below to hit once per continuous touch |
| Pushback Strength | Written into SwarmDamageContext for receivers that use it |
| Owner Root | Optional transform excluded from self-overlap. Falls back to transform.root if not set |
Target Filter
| Field | What it controls |
|---|---|
| Target Filter Mode | None hits everything; Tag requires a matching tag; Layer requires a matching layer; Tag And Layer needs both; Tag Or Layer needs either |
| Required Tag | Tag compared when the filter mode includes Tag. Empty by default, so set a tag before choosing a Tag-inclusive filter mode, or no target will match |
| Target Layers | Layer mask compared when the filter mode includes Layer |
Required Tag must exist in your project
If Required Tag names a tag that has not been defined in your project's Tag Manager, Unity throws a runtime exception. The component catches it and logs an error once, but no damage is applied until you fix the tag.
Quickstart: wire up damage in this order
- Add
PlayerCombatTargetto your player GameObject. Set Max Health to match your game's health scale. - Subscribe to
OnDiedto trigger your game-over or respawn flow. - Add
DestructibleCombatTargetto any world objects that should also be targetable (turrets, barricades). - On agent prefabs where you want the player to shoot back, add
SwarmAgentCombatTarget. Set Death Dissolve Duration to0if the VAT material has no dissolve shader. - To give a turret ranged attacks, add
ProjectileWeapon, assign aSwarmProjectileprefab, and set Target Mask to the agent layer. - For damage and heal numbers over a target, add
FloatingTextFeedback(the agent prefabs already have it). The styles fill in on their own. - If reach calculations feel off, check Attack Profile: Target Body Radius and confirm your targets return a valid radius.