Analytics pt 3: Eventos custom y funnels de conversión
Foto de cabecera por Paulo Gustavo Modesto
Con Unity Analytics inicializado y el consentimiento del usuario manejado correctamente, ya tienes datos de sesión y hardware sin esfuerzo adicional. Pero para responder las preguntas que importan para tu diseño —¿llegan los jugadores a la segunda Raid? ¿Qué bioma es más difícil? ¿Cuántos mueren antes de entender el loop?— necesitas eventos custom.
Este artículo cubre cómo pensar, implementar y visualizar esos eventos. Todo el código mostrado es el que usamos en producción en Dinopirates.
Primero: definir qué medir
Antes de escribir código, hay que tener claro cuáles son los eventos de conversión del juego. Estos son los hitos que marcan progreso real del jugador a través del experience.
La pregunta que guía esa definición: ¿qué tiene que pasar para que consideremos que un jugador “entendió” el juego?
Para el Playtest de Dinopirates, definimos un funnel de cuatro pasos:
gameStarted → firstRaidStart → secondRaidStart → surveyOpened
- gameStarted: el jugador abrió el juego y pasó la pantalla inicial. Nativo de Unity, no requiere código.
- firstRaidStart: decidió entrar a su primera Raid. Indica que el onboarding funcionó lo suficiente.
- secondRaidStart: murió o sobrevivió en su primera Raid y volvió a intentarlo. Este es el hito más revelador del core loop.
- surveyOpened: llegó a llenar la encuesta del Playtest. Nuestro evento de conversión final.
Ese funnel, con cuatro eventos, ya nos permite medir la salud del Playtest completo.
Además del funnel, hay otros eventos de contexto que agregan información útil sin ser parte de la secuencia de conversión: playerDeath, raidCompleted, raidAbandoned, nextFloorEntered, buyWeapon, openChest, entre otros.
Qué es un evento custom y qué son sus parámetros
Un evento custom es un mensaje que tu juego envía a Unity Analytics cuando ocurre algo específico. Tiene un nombre que lo identifica y puede tener parámetros: datos adicionales que describen el contexto en que ocurrió.
Por ejemplo, el evento playerDeath por sí solo te dice que alguien murió. Pero con parámetros, te dice cuándo murió, en qué bioma, con qué dificultad, y cuánto tiempo llevaba jugando esa Raid. Esa diferencia es la que convierte un evento en información accionable.
Para Dinopirates, definimos una jerarquía de clases de eventos que van acumulando parámetros según el contexto:
DPBaseEvent → TotalPlayTime
└── DPBaseSlotEvent → + SlotPlayTime, GoldCoinsPoints, CrewMembersPoints, DinopirateLevel
└── DPBaseRaidEvent → + Biome, Difficulty
└── DPBaseRaidEndEvent → + WeaponUsed, RaidDurationSeconds, EnemiesKilled, ...
Esto permite que eventos más ricos (como raidCompleted) hereden automáticamente todos los parámetros base sin repetir código, mientras que eventos simples (como surveyOpened) solo cargan lo que necesitan.
Implementación en código
Las clases de eventos
Cada evento es una clase que hereda de la clase base que corresponde a su contexto. Todas estas clases las organizamos en un archivo llamada DPAnalyticsEvents.
Unity Analytics usa este patrón para tipar los parámetros en el SDK. A continuación, estas son las clases base de la que heredan todos los otros eventos.
// DPAnalyticsEvents.cs
// Evento base con parámetros globales de sesión
public class DPBaseEvent : Event
{
public DPBaseEvent(string eventName) : base(eventName)
{
}
public int TotalPlayTime { set { SetParameter("totalPlayTime", value); } }
}
// Clases base para eventos de gameplay, con parámetros comunes a trackear en cada evento.
public class DPBaseSlotEvent : DPBaseEvent
{
public DPBaseSlotEvent(string eventName) : base(eventName)
{
}
public int SlotPlayTime { set { SetParameter("slotPlayTime", value); } }
public int GoldCoinsPoints { set { SetParameter("goldCoinsPoints", value); } }
public int CrewMembersPoints { set { SetParameter("crewMembersPoints", value); } }
public int DinopirateLevel { set { SetParameter("dinopirateLevel", value); } }
}
// Clase base para eventos relacionados a las Raids, con parámetros específicos de este tipo de eventos.
public class DPBaseRaidEvent : DPBaseSlotEvent
{
public DPBaseRaidEvent(string eventName) : base(eventName)
{
}
public string Biome { set { SetParameter("biome", value); } }
public string Difficulty { set { SetParameter("difficulty", value); } }
}
// Clase base para eventos de fin de Raid (completada, abandonada o escapada) con contexto completo de partida.
public class DPBaseRaidEndEvent : DPBaseRaidEvent
{
public DPBaseRaidEndEvent(string eventName) : base(eventName) { }
public string WeaponUsed { set { SetParameter("weaponUsed", value); } }
public int UnlockedBlueprints { set { SetParameter("unlockedBlueprints", value); } }
public int ComponentsUsed { set { SetParameter("componentsUsed", value); } }
public int RaidDurationSeconds { set { SetParameter("raidDurationSeconds", value); } }
public int EnemiesKilled { set { SetParameter("enemiesKilled", value); } }
public int CrewMembersRescued { set { SetParameter("crewMembersRescued", value); } }
}
Acá algunos ejemplos de herencia para eventos específicos:
// DPAnalyticsEvents.cs
// Evento simple: solo necesita tiempo de juego global
public class DPSurveyOpenedEvent : DPBaseEvent
{
public DPSurveyOpenedEvent() : base("surveyOpened") { }
}
// Evento de Raid: hereda bioma y dificultad además de los parámetros de slot
public class DPPlayerDeathEvent : DPBaseRaidEvent
{
public DPPlayerDeathEvent() : base("playerDeath") { }
}
// Evento de fin de Raid: contexto completo de la partida
public class DPRaidCompletedEvent : DPBaseRaidEndEvent
{
public DPRaidCompletedEvent() : base("raidCompleted") { }
}
La idea es identificar parámetros que puedan ser comunes entre eventos. Con una correcta planificación, será mucho más fácil incluirlos en Unity Dashboard.
El sender centralizado
Para no llamar AnalyticsService.Instance.RecordEvent() disperso por todo el proyecto, concentramos toda la lógica de envío en una clase estática DPAnalyticsSender. Cada método de esa clase se encarga de armar el evento con los parámetros correctos y verificar que el consentimiento esté activo antes de enviar:
public static class DPAnalyticsSender
{
// Guard: solo envía si el jugador dio consentimiento
static bool CanSend =>
TSAnalyticsManager.HasInstance &&
TSAnalyticsManager.Instance.CurrentAnalyticConsent == TSAnalyticConsent.Granted;
public static void TrackFirstRaidStart()
{
if (!CanSend) return;
var e = new DPFirstRaidStartEvent(); // Instancia el evento específico
ApplySlotParams(e); // Aplica GoldCoins, CrewMembers, DinopirateLevel, etc. Que son parámetros comunes de contexto a los eventos heredados de tipo DPBaseSlotEvent.
AnalyticsService.Instance.RecordEvent(e); // Envío de evento a Analytics
}
public static void TrackRaidCompleted(DPRaidRecord record, string weaponUsed,
int unlockedBlueprints = 0, int componentsUsed = 0)
{
if (!CanSend || record == null) return;
var e = new DPRaidCompletedEvent
{
WeaponUsed = weaponUsed,
RaidDurationSeconds = Mathf.FloorToInt(record.DurationSeconds),
EnemiesKilled = record.KilledEnemyNames?.Count ?? 0,
CrewMembersRescued = record.CrewMembersEarned,
UnlockedBlueprints = unlockedBlueprints,
ComponentsUsed = componentsUsed,
}; // Se instancia el evento específico con parámetros propios de fin de Raid.
ApplyRaidParams(e); // Aplica Biome, Difficulty, y además todos los parámetros de slot. Esta información es de contexto para todos los eventos heredados de DPBaseRaidEvent.
AnalyticsService.Instance.RecordEvent(e); // Envío de evento a Analytics
}
}
Los call sites en el resto del juego quedan limpios. Cuando un jugador completa una Raid:
DPAnalyticsSender.TrackRaidCompleted(raidRecord, currentWeapon.Name,
blueprintsUnlocked, componentsActive);
Una línea, sin acoplamiento a AnalyticsService directamente.
Configurando los eventos en Unity Dashboard
Una vez que el código está listo, el Dashboard necesita conocer tus eventos custom para poder procesarlos en funnels y reportes. Este paso se omite frecuentemente y causa que los eventos lleguen como “desconocidos”.
En el Dashboard: Analytics → Event Manager → Add New -> Custom Parameter or Custom Event

Para cada evento, debes definir:
- El nombre exacto (ej:
playerDeath) — debe coincidir carácter por carácter con el string en el código (ej:new DPPlayerDeathEvent() : base("playerDeath")). - Cada parámetro con su tipo de dato (
biome→ String,raidDurationSeconds→ Integer, etc.)
Unity no valida automáticamente que el nombre del evento en el código coincida con el registrado en el Dashboard. Si hay una diferencia, los eventos llegan pero no se pueden usar en funnels.
Otro factor importante es definir los tipos de datos concretos para cada parámetro. Si un evento llega con un parámetro que no existe o con un tipo diferente, ese parámetro se ignora en el Dashboard. Por ejemplo, si raidDurationSeconds llega como String en lugar de Integer, no podrás usarlo para filtrar o segmentar tus datos. Algunos tipos útiles a utilizar pueden ser String, Integer, Float, Boolean o Timestamp.

Una vez que un evento ya fue declarado, en caso de que sea String por ejemplo, se le puede definir un formato (Regex) o incluso un set de valores tipo ENUM. Es recomendado agarrar práctica con los eventos crudos antes de trabajar restricciones.

La recomendación es empezar definiendo los parámetros y luego los eventos que incluyan los parámetros. Dado que Unity no permite la eliminación de eventos o parámetros, es importante planificar bien qué datos realmente necesitas para no llenar el Dashboard con eventos que no se pueden usar.
Así mismo, se recomienda aprovechar el proceso de generación de eventos para replicarlos en todos los ambientes de una sola vez. De no hacerlo, habría que generar los eventos manualmente en cada ambiente, lo que puede llevar a errores o inconsistencias entre ambientes.

Con los eventos registrados, el funnel se construye en Analytics → Funnels → New Funnel: seleccionas los eventos en orden, defines la ventana de tiempo (por sesión, por día, o sin restricción), y el Dashboard calcula las tasas de conversión entre cada paso automáticamente.



Con esta infraestructura de eventos custom y funnels, ya puedes empezar a responder preguntas concretas sobre el comportamiento de tus jugadores. La idea es iterar opciones de diseño, medir su impacto en el funnel, y ajustar según los datos.
Al igual que en el caso de un e-commerce, el ideal es mantener una alta tasa de entrada y salida del funnel. Al dejar la medición andando un tiempo (por ejemplo, un par de semanas) se puede llegar a valores “normados”, lo que indica un Baseline de comportamiento. A partir de ahí, cualquier cambio que hagas al juego que impacte el funnel se puede comparar contra ese baseline para medir su efectividad.
BONUS TRACK: Costos de Unity Analytics
Unity Analytics es gratuito hasta 100.000 eventos por mes. Para un Playtest con 500 jugadores, es difícil llegar a ese límite incluso con eventos custom frecuentes. Sin embargo, es importante monitorear el consumo de eventos para no llevar sorpresas al lanzar el juego a producción.
En caso de exceder el límite gratuito, Unity cobra $0.0001 USD por evento adicional. Esto significa que 1 millón de eventos costaría $100 USD, lo cual es razonable para un juego con una base de jugadores activa una vez en producción.
En caso de llegar al límite, se podría tomar una decisión de eliminar eventos menos prioritarios o reducir la frecuencia de algunos eventos para mantener el consumo dentro del límite gratuito. Sin embargo, es recomendable planificar bien los eventos desde el inicio para evitar tener que hacer cambios drásticos en el futuro.
Serie: Analytics, puedes revisar las otras partes acá pt1 y pt2