R3 (Reactive Extensions) library expert specializing in modern reactive programming patterns, event-driven architectures, and Observable streams. Masters R3-specific features, async enumerable integration, and Unity-optimized reactive patterns. Use PROACTIVELY for R3 implementation, reactive programming, or MVVM/MVP architecture.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
references/reactive-fundamentals.mdR3 is a modern, high-performance Reactive Extensions library for Unity developed by Cysharp (same author as UniTask), providing observable streams and reactive patterns optimized for Unity.
Library: R3 by Cysharp
R3 vs UniRx: R3 is the modern successor to UniRx with better performance, async enumerable support, and Unity 2022+ optimization. For legacy UniRx projects, see unity-unirx skill.
Foundation Required: unity-csharp-fundamentals (TryGetComponent, FindAnyObjectByType), csharp-async-patterns (async fundamentals), unity-async (Unity context)
Core Topics:
Learning Path: C# events → Reactive patterns → Observable composition → MVVM architecture
using R3;
// Create observable from events
button.OnClickAsObservable()
.Subscribe(_ => Debug.Log("Button clicked!"))
.AddTo(this);
// Property observation
this.ObserveEveryValueChanged(x => x.transform.position)
.Subscribe(pos => Debug.Log($"Position: {pos}"))
.AddTo(this);
// Time-based observables
Observable.Interval(TimeSpan.FromSeconds(1))
.Subscribe(x => Debug.Log($"Tick: {x}"))
.AddTo(this);
// Reactive state management
public class Player : MonoBehaviour
{
public ReactiveProperty<int> Health { get; } = new(100);
public ReadOnlyReactiveProperty<bool> IsDead { get; }
public Player()
{
IsDead = Health.Select(h => h <= 0).ToReadOnlyReactiveProperty();
}
}
IAsyncEnumerable<T>) integrationCore R3 concepts:
Transformation and composition:
Application patterns:
AddTo(this) for MonoBehaviour lifecycle management// Button with throttle to prevent spam
button.OnClickAsObservable()
.Throttle(TimeSpan.FromSeconds(1))
.Subscribe(_ => OnButtonClick())
.AddTo(this);
// Input validation
inputField.OnValueChangedAsObservable()
.Where(text => text.Length > 3)
.Throttle(TimeSpan.FromSeconds(0.5))
.Subscribe(ValidateInput)
.AddTo(this);
public class GameState : MonoBehaviour
{
public ReactiveProperty<int> Score { get; } = new(0);
public ReactiveProperty<int> Lives { get; } = new(3);
public ReadOnlyReactiveProperty<bool> GameOver { get; }
public GameState()
{
GameOver = Lives.Select(l => l <= 0).ToReadOnlyReactiveProperty();
GameOver.Where(over => over)
.Subscribe(_ => OnGameOver())
.AddTo(this);
}
}
// Combine position and health for decision making
Observable.CombineLatest(
playerTransform.ObserveEveryValueChanged(t => t.position),
playerHealth.Health,
(pos, health) => new { Position = pos, Health = health }
)
.Where(state => state.Health < 30)
.Subscribe(state => FindNearestHealthPack(state.Position))
.AddTo(this);
ToUniTask()Observable.FromAsync()SelectMany for flatteningSample or Buffer