Herramientas de depuración en Blueprints (cuándo usar cada una)
Depurar en Blueprints consiste en observar el flujo de ejecución y el estado de los datos mientras el juego corre. La clave es elegir la herramienta adecuada según el tipo de problema: si necesitas confirmar “¿llega aquí?”, usa trazas; si necesitas ver “¿por qué tomó esta rama?”, usa breakpoints y paso a paso; si necesitas saber “¿qué valor tiene esta variable justo antes de fallar?”, usa Watch Values y el Blueprint Debugger.
Print String (trazas rápidas)
Print String es la forma más rápida de confirmar que un evento se ejecuta y qué valores están entrando/saliendo. Úsalo para validar hipótesis, no como solución permanente.
- Cuándo usarlo: confirmar que un evento se dispara, verificar valores de entrada, detectar ramas que no se ejecutan.
- Cuándo evitarlo: cuando ya necesitas inspección detallada (mejor breakpoints/Debugger) o cuando ensucia la pantalla/log.
Guía práctica:
- Inserta
Print Stringjusto antes de una decisión (por ejemplo, antes de unBranch). - Concatena información útil: nombre del actor, valor de una variable, resultado de un cast. Ejemplo:
"Stamina=" + ToString(Stamina) + " IsSprinting=" + ToString(bIsSprinting). - Activa Print to Log si quieres revisar el historial en el Output Log.
Breakpoints (pausar en un nodo)
Un breakpoint detiene la ejecución cuando el flujo llega a un nodo específico, permitiéndote inspeccionar el estado del Blueprint en ese instante.
Guía práctica:
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
- En el Blueprint, haz clic derecho sobre un nodo y selecciona Add Breakpoint (o usa el atajo disponible en tu editor).
- Ejecuta el juego en Play In Editor.
- Cuando se detenga, observa el flujo resaltado y revisa variables en el panel de detalles/depuración.
- Desactiva o elimina el breakpoint cuando termines para evitar pausas accidentales.
Step / Resume (paso a paso)
Con la ejecución pausada, Step avanza nodo a nodo y Resume continúa hasta el siguiente breakpoint o hasta que termine el flujo. Esto es ideal para entender por qué una condición se evalúa de cierta manera o en qué orden se ejecutan llamadas.
Guía práctica:
- Coloca un breakpoint antes de la zona sospechosa (por ejemplo, antes de un
Sequenceo un conjunto de ramas). - Cuando se pause, usa Step para avanzar y observa qué nodos se iluminan y qué valores cambian.
- Si ya confirmaste el problema, usa Resume para volver al flujo normal.
Watch Values (vigilar variables y pines)
Watch te permite “anclar” el valor de una variable o incluso de un pin para ver cómo cambia durante la ejecución, especialmente útil cuando el valor se modifica en varios lugares.
Guía práctica:
- Durante depuración, haz clic derecho sobre una variable o un pin y selecciona Watch this value.
- Ejecuta la acción en el juego que reproduce el bug.
- Observa el valor en el panel de Watch; si cambia inesperadamente, ya tienes una pista del origen.
Blueprint Debugger (vista centralizada de ejecución)
El Blueprint Debugger reúne información de instancias, breakpoints, pila de llamadas y valores observados. Es especialmente útil cuando hay múltiples instancias del mismo Blueprint (por ejemplo, varios enemigos) y necesitas depurar una instancia específica.
Guía práctica:
- Abre el Blueprint Debugger desde las herramientas del editor.
- Selecciona la instancia correcta del Blueprint (por ejemplo, el enemigo que está fallando, no otro).
- Revisa breakpoints activos y la ejecución resaltada.
- Combina con Watch Values para ver variables clave sin saturar con
Print String.
Errores comunes y cómo identificarlos rápidamente
1) Referencias nulas (None / Null)
Una referencia nula ocurre cuando intentas usar un objeto que no existe (o aún no está asignado). En Blueprints suele manifestarse como errores en el log del tipo “Accessed None trying to read property…”.
Señales típicas:
- Errores “Accessed None” al ejecutar una interacción.
- El bug aparece solo en ciertas condiciones (por ejemplo, al respawnear o al cambiar de nivel).
Checklist de depuración:
- Coloca un breakpoint justo antes del nodo que falla y revisa la referencia.
- Agrega una validación con
Is Validantes de usar la referencia. - Confirma el momento de asignación: ¿se asigna en BeginPlay, al overlap, al spawn, o depende de otra acción?
// Patrón típico (visual): Reference --> Is Valid? --> (Valid) usar --> (Not Valid) log/recuperar2) Casts fallidos
Un cast fallido sucede cuando intentas convertir una referencia a un tipo que no corresponde. El flujo “Cast Failed” se ejecuta, pero a veces no se maneja y el sistema queda en un estado incompleto.
Señales típicas:
- La lógica “a veces funciona” dependiendo de qué actor esté interactuando.
- Variables específicas del tipo casteado nunca se actualizan.
Checklist de depuración:
- Coloca
Print Stringen la salida Cast Failed con el nombre del actor recibido (Get Display Name). - Verifica el origen de la referencia: ¿viene de un overlap genérico? ¿de un hit? ¿de un array?
- Si el cast se usa solo para “llamar una acción”, considera reemplazarlo por una Interface (reduce acoplamiento y fallos).
3) Lógica innecesaria en Tick
Usar Event Tick para tareas que podrían ser event-driven (por eventos, timers o cambios de estado) genera consumo constante y dificulta la depuración, porque el flujo se ejecuta cientos de veces por segundo.
Señales típicas:
- Caídas de rendimiento sin razón aparente.
- Valores que “parpadean” o se sobreescriben continuamente.
- Difícil reproducir el bug porque el estado cambia cada frame.
Checklist de refactor:
- Pregunta: “¿Esto necesita actualizarse cada frame?” Si no, muévelo a un evento (por ejemplo, al presionar un input, al comenzar/terminar un estado, al entrar/salir de un trigger).
- Para actualizaciones periódicas, usa un Timer (intervalo fijo) en lugar de Tick.
- Para cambios de UI o efectos, actualiza solo cuando cambie el valor (patrón: SetX que dispara actualización).
4) Colisiones mal configuradas (eventos que no se disparan)
Muchos “bugs de lógica” son en realidad problemas de colisión: el evento no se dispara porque el canal, la respuesta o la generación de eventos no está configurada como se espera.
Señales típicas:
- No se ejecuta BeginOverlap o Hit aunque los actores se toquen.
- Funciona en un actor pero no en otro (por presets distintos).
Checklist de depuración:
- Verifica que el componente correcto tenga Generate Overlap Events activado.
- Confirma que ambos actores/componentes tengan respuestas compatibles (Overlap vs Block vs Ignore) en el canal correspondiente.
- Usa
Print Stringen el evento de overlap/hit para confirmar qué actor está entrando y con qué componente. - Si hay múltiples componentes, asegúrate de estar escuchando el evento en el componente que realmente colisiona (no en el root si no colisiona).
Buenas prácticas para Blueprints mantenibles (calidad y legibilidad)
Nombres claros y consistentes
- Variables booleanas: prefijo
bo nombres que indiquen estado:bIsSprinting,bHasKeycard. - Funciones: verbos + objeto:
ApplyDamage,StartInteract,UpdateStaminaUI. - Eventos personalizados: intención clara:
OnStaminaDepleted,OnInteractionStarted.
Comentarios útiles (no decorativos)
Comenta el “por qué” y las suposiciones, no lo obvio. Un buen comentario explica una regla de negocio o una razón técnica.
- Malo: “Resta stamina”.
- Bueno: “La stamina solo se drena si el jugador está en sprint y está en el suelo; evita drenaje en el aire para no penalizar saltos”.
Funciones pequeñas y con una sola responsabilidad
Divide la lógica en funciones que puedas probar mentalmente. Si una función hace demasiadas cosas, será difícil de depurar y reutilizar.
- Ejemplo de separación:
CanSprint(validación) +StartSprint(cambio de estado) +DrainStamina(consumo) +UpdateSprintFX(feedback).
Categorías y organización de variables
Usa categorías para agrupar variables por sistema (por ejemplo: Movement, Combat, UI, Debug). Esto acelera la inspección durante depuración y reduce errores por editar la variable equivocada.
Reutilización mediante componentes e interfaces
- Componentes: encapsulan una mecánica reutilizable (por ejemplo, “StaminaComponent”, “InteractComponent”). Mejoran testeo y reducen duplicación.
- Interfaces: permiten llamar acciones sin cast directo (por ejemplo,
BPI_InteractableconInteract). Disminuyen casts fallidos y acoplamiento.
Sesión guiada de refactor: de Blueprint desordenado a Blueprint mantenible
En esta sesión, tomaremos un ejemplo típico: un Blueprint de personaje con una mecánica (por ejemplo, sprint con stamina) implementada “rápido” en un solo gráfico con ramas, prints y lógica repetida. El objetivo es reorganizarlo en variables bien tipadas, funciones pequeñas, eventos claros y puntos de depuración limpios.
Escenario inicial (síntomas del Blueprint desordenado)
- Gran cantidad de nodos conectados en una sola zona (spaghetti).
- Uso de
Tickpara drenar stamina aunque el jugador no esté sprintando. - Variables sin categorías y nombres ambiguos (por ejemplo,
Value,Temp). - Varios
Print Stringpermanentes. - Cast a un tipo específico para actualizar UI, con fallos ocasionales.
Paso 1: Congelar el comportamiento actual (para no romperlo)
Antes de refactorizar, crea un “punto de referencia” para comparar.
- Reproduce el caso: iniciar sprint, drenar stamina, detener sprint, recuperar stamina.
- Agrega temporalmente 2–3 trazas clave (no más):
Print Stringal iniciar sprint: muestraStaminaybIsSprinting.Print Stringal detener sprint.Print Stringcuando stamina llega a 0.
Esto te permite confirmar que el comportamiento final del refactor coincide con el original.
Paso 2: Definir variables bien tipadas y con categorías
Crea/renombra variables con intención clara. Ejemplo para un sistema de stamina:
| Variable | Tipo | Categoría | Notas |
|---|---|---|---|
Stamina | Float | Stamina | Valor actual |
MaxStamina | Float | Stamina | Constante editable |
StaminaDrainPerSecond | Float | Stamina | Consumo al sprintar |
StaminaRegenPerSecond | Float | Stamina | Regeneración al no sprintar |
bIsSprinting | Boolean | Movement | Estado |
StaminaTickTimerHandle | TimerHandle | Stamina | Para timers (si aplica) |
Regla práctica: si una variable representa un “estado” (encendido/apagado), debe ser boolean y nombrarse como estado. Si representa una “cantidad”, debe ser numérica y con unidad implícita (por segundo, máximo, actual).
Paso 3: Extraer funciones (1 responsabilidad por función)
Identifica bloques repetidos o decisiones centrales y conviértelos en funciones. Propuesta:
CanStartSprint()→ devuelve Boolean (y opcionalmente una razón para debug).StartSprint()→ seteabIsSprinting, aplica cambios de movimiento, inicia drenaje.StopSprint()→ revierte estado y detiene drenaje.ModifyStamina(Delta)→ suma/resta y clampa entre 0 y Max.OnStaminaChanged()→ punto único para actualizar UI/feedback.HandleStaminaDepleted()→ se llama cuando llega a 0 (detener sprint, etc.).
Tip de calidad: cuando una función tiene más de 2–3 ramas internas y además llama a UI, sonido y movimiento, probablemente debas dividirla.
Paso 4: Reemplazar Tick por eventos/timers (si corresponde)
En lugar de drenar stamina en cada frame, usa un timer mientras el jugador está sprintando. Esto reduce carga y hace la depuración determinista.
Implementación sugerida (patrón):
- En
StartSprint: inicia un timer repetitivo (por ejemplo, cada 0.1s) que llame aDrainStaminaTick. - En
StopSprint: limpia el timer de drenaje y, si quieres, inicia otro timer para regeneración (o regenera por eventos de estado).
DrainStaminaTick(): ModifyStamina(-StaminaDrainPerSecond * Interval); if Stamina <= 0 -> HandleStaminaDepleted()Para regeneración, aplica el mismo patrón cuando bIsSprinting sea falso, o regenera solo cuando el estado cambie (por ejemplo, iniciar un timer de regen al detener sprint).
Paso 5: Centralizar la actualización (UI/feedback) en un solo punto
Un error común es actualizar UI desde múltiples lugares, lo que genera inconsistencias. En su lugar:
- Haz que
ModifyStaminallame aOnStaminaChangedsolo cuando el valor realmente cambie. - Dentro de
OnStaminaChanged, llama a la UI mediante una Interface (si tu UI o HUD implementa una interfaz), evitando casts directos.
Ejemplo conceptual: si existe BPI_StaminaListener con OnStaminaUpdated(Current, Max), el personaje solo necesita llamar a la interfaz en el objeto que corresponda, sin asumir su clase concreta.
Paso 6: Añadir puntos de depuración “limpios” (sin ruido)
En vez de muchos Print String dispersos, crea un pequeño bloque de debug reutilizable:
- Variable
bEnableDebugStamina(Categoría: Debug). - Función
DebugLogStamina(Message)que solo imprime sibEnableDebugStaminaestá activa.
DebugLogStamina(Message): if bEnableDebugStamina -> Print String(Message, Print to Log=true)Así puedes activar/desactivar depuración sin borrar nodos.
Paso 7: Validaciones para evitar referencias nulas y casts fallidos
En los puntos donde interactúas con otros objetos (UI, componentes, actores), aplica estas reglas:
- Antes de usar una referencia:
Is Valid. - Si dependes de una instancia específica (por ejemplo, un widget): inicialízala en un punto controlado y guarda la referencia; si no existe, loguea una vez (no cada frame).
- Si solo necesitas “enviar un mensaje”: usa Interface en lugar de cast.
Paso 8: Verificación final con Blueprint Debugger
Ahora compara contra el comportamiento “congelado” del Paso 1.
- Coloca breakpoints en
StartSprint,StopSprintyHandleStaminaDepleted. - Usa Step para confirmar el orden:
CanStartSprint→StartSprint→ timer →ModifyStamina→OnStaminaChanged. - Agrega Watch a
StaminaybIsSprintingpara ver cambios sin imprimir.
Mini-checklist de calidad antes de dar por terminado un Blueprint
- ¿Hay nodos en Tick que podrían ser eventos o timers?
- ¿Hay casts que podrían ser interfaces?
- ¿Las referencias externas están validadas con
Is Valid? - ¿Las funciones son pequeñas y con nombres verbales?
- ¿Las variables tienen categorías y tipos correctos (sin “Temp” permanentes)?
- ¿La actualización de UI/feedback está centralizada?
- ¿Los prints están detrás de un flag de debug o eliminados?