Home Watchers
Post
Cancel

Watchers

Introduction

Have you ever struggled with jumping from tab to tab, from window to window? Are you tired of the endless journey through the object hierarchy to find the right one? Are you tired of scrolling through countless logs to keep track of that one specific value?

If you answered yes to any of the above questions, You’ve come to the right place! Jokes aside, in this post we would like to introduce our custom tool called Watchers.

Watchers is a collection of handy windows and extensions that will speed up your workflow and make your life easier.

Why Watchers

  • most frequently used objects in one place and always at hand
  • tracking values of not only UnityEngine.Objects, but also instance values of ordinary classes
  • easiness of use
  • high extensibility
  • native solution
  • customizable solution with support for your own styles

Unity Installation

Since unity doesn’t support git dependencies in package.json you have to install UniRx manually. Edit manifest.json file in your Unity Packages directory

{
	"dependencies": {
		"com.gbros.tools.unirx.powerobservables": "[<https://github.com/GbrosGames/Tools.git?path=Assets/Watchers>](<https://github.com/GbrosGames/Tools.git?path=Assets/Watchers>)",
		"com.neuecc.unirx": "[<https://github.com/neuecc/UniRx.git?path=Assets/Plugins/UniRx/Scripts>](<https://github.com/neuecc/UniRx.git?path=Assets/Plugins/UniRx/Scripts>)"
	}
}

General

Watchers are created from bunch of UnityEngine.UIElements and UnityEditor.UIElements. Please see blow for detailed introduction to all core classes, methods and use case examples.

Watchers

Watcher is a base class that holds references to the boards. It’s responsible for clearing all subscriptions on the board. Every watcher can contain multiple boards.

Boards

Board inherits from Unity native GraphView. It is a place where you can add your Nodes. Boards can contain multiple cards.

Card

Card inherits from Unity native Node. It is the element that can be part of our Graph. You can create and add your custom nodes. Cards are used as data containers.

Container

Container is styled UIElement with label that can have many UI Elements.

It can be easily extended with your own UIElements and stylized through the Watchers API or with custom .uss files.

Getting Started

Since watchers is editor only solution to help you with debugging and increase your development speed we have to add proper usings at the top of the class.

#if UNITY_EDITOR
using Gbros.Watchers;
#endif

Creating Watcher

Fluent way

[Serializable] 
public class SomeClass 
{ 
	public Color SomeColor;
	public string SomeText;
}

public class SomeNotUnityObjectClass
{
    public int SomeValue { get; set; }
}


public class SomeComponent : MonoBehaviour 
{

public int SomeValue = 2;
public Vector2 SomeVector2 = Vector2.up;
public Vector3 SomeVector3 = Vector3.up;
public SomeClass SomeClass;

private void Awake()
{
    var someNotUnityObjectClass = new SomeNotUnityObjectClass
    {
        SomeValue = 123
    };
		
	#if UNITY_EDITOR
       Watchers.Watcher("Watcher A")
               .Board("Board A")
               .Card("Card A")
               .Container("Container A")
               .AddSelectorButton($"Select {gameObject.name}", gameObject)
               .AddSerializedProperty(this, x => x.SomeValue)
               .AddSerializedProperty(this, "SomeVector2")
               .AddSerializedProperty(this, x => x.SomeVector3)
               .AddSerializedProperty(this, x => x.SomeClass)
               .AddProperty("SomeValue in someNonSerializableClass", someNotUnityObjectClass, x => x.SomeValue, x => someNotUnityObjectClass.SomeValue = x)
               .AddActionButton("Some Action", () => { Debug.Log($"Value from nonUnityObjectClass - {someNotUnityObjectClass.SomeValue}"); });
	#endif
}

Classic

	var watcher = Watchers.Watcher("Watcher A");
	var board = watcher.Board("Board A");
	var card = board.Card("Card A");
	var cardContainer = card.Container("Container A");
	// add whatever VisualElement to your cardContainer

Nested

Watchers.Watcher("Watcher C")
.Board("Board A", board => 
{ 
	board.Card("Card A", card => 
	{
		card.Container("Container A")
			.AddActionButton("Click me", () => Debug.Log("Hello World"));

		card.Container("Container B")
			.Add("GeneralInfoKey", new Label("General Info"), label => label.style.color = Color.yellow)
			.AddProperty(nameof(SomeValue), this, x => x.SomeValue, x => SomeValue = x);
	});

	board.Card("Card B");
});

Watcher UI Elements

Selector Buttons

Extension that adds button which will find your object in the scene hierarchy. It is setting Selection.activeObject under the hood

.AddSelectorButton($"Select {gameObject.name}", gameObject)

Action Buttons

Extension that adds button with an action. Really useful for testing!

.AddActionButton("Click me", () => Debug.Log("Hello World"));

Properties

Add various kind of properties to your UI elements

SerializedProperty

Serialized properties can be invoked on UnityEngine.Objects like Components/MonoBehaviours/ScriptableObjects. It will be displayed in the same way as in the inspector.

.AddSerializedProperty(this, x => x.SomeClass) 
Property

Normal properties can be added through few handy overloads. Under the hood every property is a field from UI Elements that has been created for certain types like ints, floats, bools, Vector2s, Vector3s

The most generic way:

1) Getter/Setter

It will observe every value changed of a selected property

.AddProperty("PropertyName", this, x => x.SomeValue, x => SomeValue = x) 

.AddProperty(1, 2, 3, 4)

1 - display name of the property
2 - property context, for example class where it is used
3 - getter from the context
4 - setter for the property. Is not required, but if you want to have two way binding you have to use it.

2) Observable way

.AddProperty(nameof(ReactiveProperty), ReactiveProperty);

.AddProperty(1, 2)

1 - display name of the property
2 - IReactiveProperty / IObservable

Styling

USS

There are two ways of styling your watchers. The first one is to use classic stylesheets

RMB on active folder -> Create -> Watchers Stylesheet

It will create base stylesheet with initial setup that you can work on.

Next create initializer script somewhere in your solution and point it to the Path where you have created your Stylesheet.

public class WatcherInitializer
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
    public static void Initialize()
    {
        Watchers.EditorStylePath = "Assets/MyStyles.uss";
    }
}

Inline styles

Watcher cards and boards inherits from VisualElements so you can set inline styles like so

element.style.color = Color.green;

In case you want to use fluent API, watcher elements have their own callbacks

.Property("SomeValue", this, x => x.SomeValue, x => SomeValue = x).Configure(field => field.style.borderWidth = 5);

or shorter version:

.Property("SomeValue", this, x => x.SomeValue, x => SomeValue = x, field => field.style.borderWidth = 5);
Watchers
	.Watcher()
	.Board()
	.Card("PRO CARD", card => 
	{
		card.Title.style.color = Color.yellow;
		card.style.backgroundColor = Color.blue;
	})

Unity Examples

All code samples can be found on our GitHub page

Example 1

Where To Go From Here

PowerObservables

Resources

UniRx Library

This post is licensed under CC BY 4.0 by the author.