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.

jahro watcher modal

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:

  1. Favorites (starred watchers)
  2. Alphabetical order of custom groups
  3. Default group (if any ungrouped watchers exist)

Watcher cards

Each watcher is displayed as an interactive card showing:

  • Name - Display name from JahroWatch attribute
  • 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.