Watcher
Jahro's Watcher system lets you monitor game variables in real-time without cluttering your code with debug logs or pausing with breakpoints. It's a simple dashboard for your game state that updates live as your game runs, working seamlessly with commands and snapshots for comprehensive debugging.
Why use Watcher
Watcher provides real-time visibility into your game's state without the performance overhead of debug logs or the interruption of breakpoints. You can see values update as you play, organize related variables into groups, and focus on the data that matters most for your current debugging session.
The system is designed to be efficient. Values are only read when the Watcher interface is visible, and it automatically handles Unity types with smart formatting.
Interface overview
The Watcher interface organizes your variables into collapsible groups, with favorites appearing at the top for quick access.

Watchers are automatically organized into logical groups based on their JahroWatch attributes:
Default groups:
- Favorites - Starred watchers for quick access (always at top)
- Default - Watchers without specific group assignment
- Custom Groups - Watchers organized by developer-defined categories
Ordering:
- Favorites (starred watchers)
- Alphabetical order of custom groups
- Default group (if any ungrouped watchers exist)
Watcher cards
Each watcher is displayed as an interactive card showing:
- Name - Display name from
JahroWatchattribute - Current Value - Real-time value with automatic formatting
- Group - Category the watcher belongs to
Click any watcher item to open a detailed modal showing comprehensive information including the full description and type details.
Basic usage
Static watchers
For global game state that doesn't belong to a specific object, use static watchers.
using JahroConsole;
using UnityEngine;
public static class GameStats
{
[JahroWatch("Player Score", "Game Stats", "Current player score")]
public static int PlayerScore = 0;
[JahroWatch("Game Time", "Game Stats", "Time since game started")]
public static float GameTime => GameTimer.time;
}Instance watchers
For object-specific data, add the JahroWatch attribute to fields or properties, then register the object so Jahro can read the values.
public class PlayerController : MonoBehaviour
{
[JahroWatch("Health", "Player", "Current health points")]
public float health = 100f;
[JahroWatch("Position", "Player", "Player world position")]
public Vector3 position => transform.position;
[JahroWatch("Velocity", "Player", "Player movement velocity")]
public Vector3 velocity => GetComponent<Rigidbody>().velocity;
void OnEnable()
{
Jahro.RegisterObject(this);
}
void OnDisable()
{
Jahro.UnregisterObject(this);
}
}[JahroWatch] Attribute
The JahroWatch attribute accepts three optional parameters that help organize and document your watchers.
[JahroWatch(name, group, description)]- name: Display name in the Watcher interface (defaults to member name with leading
_removed) - group: Section name for organization (defaults to "Default")
- description: Shown in the details modal for additional context
Examples
// Basic usage - uses field name "playerHealth"
[JahroWatch]
private float _playerHealth = 100f;
// Custom name and group
[JahroWatch("Player HP", "Vitals")]
public float playerHealth = 100f;
// Full specification
[JahroWatch("Battery Level", "System", "Device battery percentage and status")]
public static string BatteryStatus => GetBatteryInfo();Supported types
Watcher automatically formats values based on their type for optimal readability.
Primitives
Basic types like int, float, double, bool are displayed as-is, while string values are truncated in the list view but show full text in the detail modal.
Unity types
Unity's built-in types receive special formatting. Vector2 and Vector3 show compact coordinates in the list with full breakdowns including magnitude in the modal. Quaternion displays both raw values and Euler angles, while Transform shows position, rotation, scale, and child count.
Components
Unity components get detailed formatting. Rigidbody shows mass, kinematic state, gravity, and angular velocity. Collider displays trigger status, material, and bounds. AudioSource shows clip, volume, loop, pitch, and mute status. Camera displays FOV, clip planes, and aspect ratio.
Arrays
Any array type shows TypeName[length] in the list view, with full contents displayed in the detail modal.
Error handling
The system gracefully handles null values, exceptions, and invalid access attempts, displaying appropriate error messages without crashing.
Advanced examples
Performance monitoring
public static class PerformanceWatcher
{
private static float _lastUpdateTime;
private static float _cachedFps;
private static string _cachedMemory;
static PerformanceWatcher()
{
Application.onBeforeRender += UpdateValues;
}
private static void UpdateValues()
{
if (Time.time - _lastUpdateTime < 0.25f) return; // 4Hz update rate
_cachedFps = 1f / Time.unscaledDeltaTime;
_cachedMemory = $"{(GC.GetTotalMemory(false) / 1024f / 1024f):F1} MB";
_lastUpdateTime = Time.time;
}
[JahroWatch("FPS", "Performance", "Current frames per second")]
public static string FPS => _cachedFps.ToString("F1");
[JahroWatch("Memory", "Performance", "Managed heap memory usage")]
public static string Memory => _cachedMemory;
}Game state monitoring
public class GameManager : MonoBehaviour
{
[JahroWatch("Game State", "Game", "Current game state")]
public GameState currentState = GameState.Menu;
[JahroWatch("Level Progress", "Game", "Current level completion percentage")]
public float levelProgress => CalculateProgress();
[JahroWatch("Player Count", "Game", "Number of active players")]
public int playerCount => PlayerManager.ActivePlayers.Count;
[JahroWatch("Enemy Count", "Game", "Number of active enemies")]
public int enemyCount => EnemyManager.ActiveEnemies.Count;
void OnEnable() => Jahro.RegisterObject(this);
void OnDisable() => Jahro.UnregisterObject(this);
}Physics debugging
public class PhysicsDebugger : MonoBehaviour
{
[JahroWatch("Velocity", "Physics", "Rigidbody velocity")]
public Vector3 velocity => GetComponent<Rigidbody>().velocity;
[JahroWatch("Angular Velocity", "Physics", "Rigidbody angular velocity")]
public Vector3 angularVelocity => GetComponent<Rigidbody>().angularVelocity;
[JahroWatch("Is Grounded", "Physics", "Whether object is touching ground")]
public bool isGrounded => Physics.Raycast(transform.position, Vector3.down, 0.1f);
[JahroWatch("Collision Count", "Physics", "Number of active collisions")]
public int collisionCount => GetComponent<Collider>().bounds.Intersects(groundBounds) ? 1 : 0;
void OnEnable() => Jahro.RegisterObject(this);
void OnDisable() => Jahro.UnregisterObject(this);
}Next Steps
Combine Watcher with other Jahro features for a complete debugging workflow:
- Snapshots - Share your debugging sessions with the team through the web console
- Commands - Create debug commands that work alongside your watchers
- Logs - View Unity logs in context with your monitored variables
Check the API Reference for advanced integration options.