From unity-plugin
Assists maintaining legacy Unity projects with UniRx Reactive Extensions, specializing in Observable streams, operators, ReactiveProperty, MessageBroker, and MainThreadDispatcher.
npx claudepluginhub creator-hian/claude-code-plugins --plugin unity-pluginThis skill uses the workspace's default tool permissions.
UniRx is a legacy Reactive Extensions library for Unity, widely used in pre-2022 Unity projects. For new projects, prefer R3 (`unity-r3` skill).
Provides Unity C# scripting patterns covering MonoBehaviour lifecycle, coroutines, async/await, physics APIs, raycasts, collisions, animations, NavMesh, pooling, singletons, ECS, Jobs, and Burst.
Unity 6 C# scripting guide. Use when writing MonoBehaviour scripts, handling lifecycle events (Awake, Start, Update, FixedUpdate), using coroutines or async/await (Awaitable), working with ScriptableObjects, events, delegates, or core APIs like Vector3, Quaternion, Time, Debug. Based on Unity 6.3 LTS documentation.
Provides Unity ECS patterns with DOTS, Jobs, and Burst for high-performance games, managing thousands of entities, data-oriented systems, and CPU optimization.
Share bugs, ideas, or general feedback.
UniRx is a legacy Reactive Extensions library for Unity, widely used in pre-2022 Unity projects. For new projects, prefer R3 (unity-r3 skill).
Library: UniRx by neuecc
UniRx vs R3: UniRx is the predecessor to R3. R3 offers better performance and modern C# features, but UniRx is still maintained and used in many existing projects.
Status: ⚠️ Legacy library - Maintained but not actively developed. New projects should use R3.
Foundation Required: unity-csharp-fundamentals (TryGetComponent, FindAnyObjectByType), csharp-async-patterns (async fundamentals), unity-async (Unity context)
Core Topics:
Learning Path: C# events → UniRx basics → Observable composition → MVVM with UniRx
using UniRx;
using UnityEngine;
public class Example : MonoBehaviour
{
void Start()
{
// Button clicks
button.OnClickAsObservable()
.Subscribe(_ => Debug.Log("Clicked"))
.AddTo(this);
// Update loop as observable
Observable.EveryUpdate()
.Where(_ => Input.GetKeyDown(KeyCode.Space))
.Subscribe(_ => Jump())
.AddTo(this);
// Time-based
Observable.Timer(TimeSpan.FromSeconds(1))
.Subscribe(_ => Debug.Log("1 second passed"))
.AddTo(this);
}
}
using UniRx;
public class Player : MonoBehaviour
{
// IntReactiveProperty is UniRx-specific
public IntReactiveProperty Health = new IntReactiveProperty(100);
public ReadOnlyReactiveProperty<bool> IsDead;
void Awake()
{
IsDead = Health
.Select(h => h <= 0)
.ToReadOnlyReactiveProperty();
IsDead.Where(dead => dead)
.Subscribe(_ => OnDeath())
.AddTo(this);
}
public void TakeDamage(int amount)
{
Health.Value -= amount;
}
}
using UniRx;
// Global event system
public class GameEvents
{
public struct PlayerDiedEvent { }
public struct ScoreChangedEvent { public int NewScore; }
}
// Publish
MessageBroker.Default.Publish(new GameEvents.PlayerDiedEvent());
// Subscribe
MessageBroker.Default.Receive<GameEvents.PlayerDiedEvent>()
.Subscribe(_ => ShowGameOver())
.AddTo(this);
using UniRx;
public class ViewModel
{
// Command can be enabled/disabled reactively
public ReactiveCommand AttackCommand { get; }
private IntReactiveProperty mStamina = new IntReactiveProperty(100);
public ViewModel()
{
// Command only enabled when stamina > 10
AttackCommand = mStamina
.Select(s => s > 10)
.ToReactiveCommand();
AttackCommand.Subscribe(_ => ExecuteAttack());
}
}
using UniRx;
using System.Threading.Tasks;
async Task DoBackgroundWork()
{
// Do background work
await Task.Run(() => HeavyComputation());
// Return to Unity main thread
await UniRx.MainThreadDispatcher.SendStartCoroutine(UpdateUI());
}
// Input field with validation
inputField.OnValueChangedAsObservable()
.Where(text => text.Length >= 3)
.Throttle(TimeSpan.FromMilliseconds(500))
.Subscribe(text => ValidateInput(text))
.AddTo(this);
// Toggle button
toggle.OnValueChangedAsObservable()
.Subscribe(isOn => OnToggleChanged(isOn))
.AddTo(this);
// Convert coroutine to observable
Observable.FromCoroutine<string>(observer => GetDataCoroutine(observer))
.Subscribe(data => ProcessData(data))
.AddTo(this);
IEnumerator GetDataCoroutine(IObserver<string> observer)
{
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
observer.OnNext(www.downloadHandler.text);
observer.OnCompleted();
}
// ViewModel
public class PlayerViewModel : IDisposable
{
private CompositeDisposable mDisposables = new CompositeDisposable();
public IReadOnlyReactiveProperty<int> Health { get; }
public IReadOnlyReactiveProperty<string> Status { get; }
public ReactiveCommand HealCommand { get; }
private IntReactiveProperty mHealth = new IntReactiveProperty(100);
public PlayerViewModel()
{
Health = mHealth.ToReadOnlyReactiveProperty().AddTo(mDisposables);
Status = mHealth
.Select(h => h <= 30 ? "Critical" : h <= 70 ? "Wounded" : "Healthy")
.ToReadOnlyReactiveProperty()
.AddTo(mDisposables);
HealCommand = mHealth
.Select(h => h < 100)
.ToReactiveCommand()
.AddTo(mDisposables);
HealCommand.Subscribe(_ => mHealth.Value += 20).AddTo(mDisposables);
}
public void Dispose()
{
mDisposables.Dispose();
}
}
If migrating from UniRx to R3:
// UniRx
IntReactiveProperty health = new IntReactiveProperty(100);
ReadOnlyReactiveProperty<bool> isDead = health
.Select(h => h <= 0)
.ToReadOnlyReactiveProperty();
// R3 (nearly identical)
ReactiveProperty<int> health = new ReactiveProperty<int>(100);
ReadOnlyReactiveProperty<bool> isDead = health
.Select(h => h <= 0)
.ToReadOnlyReactiveProperty();
using UniRx; → using R3;IntReactiveProperty → ReactiveProperty<int>Observable.ReturnOnMainThread() and Unity's SynchronizationContextObservable.FromAsync()UniRx performance is good but R3 offers:
For performance-critical applications on Unity 2022+, migrate to R3.
Detailed UniRx patterns:
Migration Guide: See unity-r3 skill for R3 patterns and migration considerations.