Multiplayer FPS: where they die, why they die, who's winning the room.
Shooters are the canonical case for 3D analytics. The questions are positional by nature — choke points, sightlines, spawn camping, weapon dominance by area — and every one of them collapses cleanly into a small set of event IDs. This recipe is the wiring you need to answer them without a custom dashboard. It assumes you've completed the quickstart and have a project ingesting events. It works for arena, hero, battle royale, and tactical shooters; only the map size and the weapon vocabulary change.
Event vocabulary
Fire these six events. Use the preset helpers where they exist (they map to dashboard-aware styling automatically); fall back to TrackEvent for the custom ones.
death— Point + rotation. Fire on the dying player. Pass rotation so the dashboard can render facing direction. Metadata:weapon,team,killer_team,distance_m.kill— Point + rotation. Fire on the killer at their firing position. Pairs naturally withdeathto expose advantageous angles. Same metadata asdeath.kill_link— Pair. Killer position to victim position. The best single signal of sightline dominance. Metadata:weapon,headshot.spawn— Point. Fire on each respawn. Overlaid againstdeathclusters, this is how you catch spawn camping.objective_complete— Point. Capture, plant, defuse, flag touch. Metadata:objective_id,round.damage_dealt— Pair. Attacker to victim. Fire on every significant hit (gate by minimum damage so you don't flood). Pair intensity reveals duel locations even when no one dies.
Sample wiring
Drop this into your existing damage handler. The pattern is the same whether you're on a netcode-for-gameobjects, Mirror, Photon Fusion, or custom-rollback stack — fire from the authoritative side (server or host) so you don't double-count.
using UnityEngine;
using Traced;
using System.Collections.Generic;
public class CombatTelemetry : MonoBehaviour {
// Called server-side when a damage event resolves into a kill.
public void OnKill(PlayerState killer, PlayerState victim, WeaponDef weapon) {
var meta = new Dictionary<string, string> {
{ "weapon", weapon.Id },
{ "team", victim.Team },
{ "killer_team", killer.Team },
{ "distance_m", Vector3.Distance(killer.Position, victim.Position).ToString("F1") },
{ "headshot", weapon.LastHitWasHeadshot.ToString().ToLower() },
};
Traced.TracedPresets.Death(victim.Position, victim.Rotation, meta);
Traced.TracedPresets.Kill(killer.Position, killer.Rotation, meta);
Traced.TrackPair("kill_link", killer.Position, victim.Position, meta);
}
}What to look for in the dashboard
Open the project Overview and pick Spatial Density for the first pass. Toggle the death layer on. Tight clusters mean choke points or bad sightlines — walk the level in your head and ask whether each cluster matches the geometry you intended. Add the kill layer in a different color. Where the two clusters do not overlap, you're looking at someone holding an angle: kills here, deaths there, with a clean line between them.
Switch to Path Flow to read kill_link as line segments. Dense bundles of lines through a doorway or window are the level's real combat seams. If the lines are consistently long, you have a sniping problem; if consistently short, the fights are CQC and you can probably shrink the room without losing variety.
Use the recent-events table to break death down by weapon. A single weapon dominating one area is a balance signal. Set build_version in your session context and compare side-by-side after a tuning pass — the same chokepoint with fewer clustered deaths is a measurable win.
Cross-reference spawn and death with both layers visible. Death clusters within five meters of a spawn point are your spawn-camping report.
Next: level QA recipe for running a single-map iteration loop, or event shapes for the underlying primitives.