Skip to content

fahall/LokiCat.GodotNodeInterfaces.Observables

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 

Repository files navigation

LokiCat.GodotNodeInterfaces.Observables

Generate R3 Observables from Godot signals via Chickensoft.GodotNodeInterfaces

NuGet CI


Overview

Why bother?

We want:

  1. ✅ To program to interfaces (via Chickensoft.GodotNodeInterfaces)
  2. ✅ To use ReactiveX-style observables (via R3)
  3. ❌ But we don't want to manually wire every Godot signal ourselves

This library gives us declarative, strongly-typed observables for signals across interfaces, Godot built-ins, and [Signal] delegates — all auto-generated.

Baseline: Manual event wiring

public partial class MyMenu : Control, IControl {
  private IBaseButton button;

  public void OnReady() {
    button.Pressed += HandlePressed;
  }

  public void OnExitTree() {
    button.Pressed -= HandlePressed;
  }

  private void HandlePressed() => GD.Print("Pressed!");
}
  • ❌ Must manage subscribe/unsubscribe manually
  • ❌ Can’t compose with R3

Manual R3 Observable

Observable.FromEvent<PressedEventHandler, Unit>(
  h => () => h(Unit.Default),
  h => button.Pressed += h,
  h => button.Pressed -= h
)
.Subscribe(_ => GD.Print("Pressed"))
.AddTo(this);
  • ✅ Works
  • ❌ Verbose
  • ❌ Easy to miswire

✅ With LokiCat.GodotNodeInterfaces.Observables

button.OnPressedAsObservable()
  .Subscribe(_ => GD.Print("Pressed"))
  .AddTo(this);
  • ✅ Clean
  • ✅ Reactive
  • ✅ Auto cleanup
  • ✅ Interface- and class-based support
  • ✅ Works for custom [Signal]s and built-ins

This project provides Roslyn source generators that create R3 observables for:

  1. All events defined in Chickensoft.GodotNodeInterfaces interfaces.
  2. All signals in built-in Godot classes (e.g., Button, Node2D) if you define a partial class in your project.
  3. Any user-defined [Signal] delegate in your own Godot partial classes.

These generators output strongly-typed, composable observables for signals using idiomatic R3 patterns.

Why use this?

  • ✅ Program to interfaces with Chickensoft.GodotNodeInterfaces
  • ✅ Use ReactiveX (R3) for signal handling
  • ✅ Avoid writing manual signal-to-observable plumbing
  • ✅ Get compile-time safety and auto-cleanup via .AddTo(this)

🔍 Example

public partial class MyMenu : Control, IControl {
  private IBaseButton doSomethingButton;

  public override void _Ready() {
    doSomethingButton.OnPressedAsObservable()
      .Subscribe(_ => GD.Print("Pressed!"))
      .AddTo(this);
  }
}

If you define your own [Signal]-annotated delegates:

[Signal] private delegate void ToggledEventHandler(bool toggled);

public partial class ToggleThing : Node {
  private Subject<bool> _onToggled = new();
  public Observable<bool> OnToggled => _onToggled ??= ConnectToggled();
}

And for built-in classes:

public partial class Button : Godot.Button { }

Triggers generation of:

private Subject<Unit>? _onPressed;
public Observable<Unit> OnPressed => _onPressed ??= ConnectPressed();

✨ Features

  • 🔧 Zero config — just mark your classes partial and you're done
  • ⚙️ Supports 0–5 signal parameters
  • 📡 Built-in Godot signal support via known interface mappings
  • 🧠 Handles custom signal delegates with or without constructors
  • 📎 Avoids duplicating built-in signal wiring
  • 🧪 Thoroughly tested with diagnostic output

📦 Installation

Install via NuGet:

dotnet add package LokiCat.GodotNodeInterfaces.Observables

🧪 Tests

Covers:

  • Signals with 0–5 parameters
  • Interfaces with custom and standard delegate types
  • Edge cases like non-partial classes or invalid delegate wiring
dotnet test

🧱 How It Works

  • Scans Chickensoft.GodotNodeInterfaces for all interfaces with events.

  • For each:

    • Emits extension method On[EventName]AsObservable()
  • Separately, scans all partial classes in your project:

    • If it inherits a known Godot class (e.g., Button), emits _onX / OnX fields + observable logic for signals like pressed, toggled, etc.
    • If it defines a [Signal]-annotated delegate, generates a subject-backed observable for it.
  • Supports 0–5 parameters; skips and warns if more.


🙏 Credits


📄 License

MIT

About

Automatically extends events created in Chickensoft GodotNodeInterfaces for use as R3 Observables

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages