LokiCat.Godot.R3.ObservableSignals
1.0.70
dotnet add package LokiCat.Godot.R3.ObservableSignals --version 1.0.70
NuGet\Install-Package LokiCat.Godot.R3.ObservableSignals -Version 1.0.70
<PackageReference Include="LokiCat.Godot.R3.ObservableSignals" Version="1.0.70" />
<PackageVersion Include="LokiCat.Godot.R3.ObservableSignals" Version="1.0.70" />
<PackageReference Include="LokiCat.Godot.R3.ObservableSignals" />
paket add LokiCat.Godot.R3.ObservableSignals --version 1.0.70
#r "nuget: LokiCat.Godot.R3.ObservableSignals, 1.0.70"
#:package LokiCat.Godot.R3.ObservableSignals@1.0.70
#addin nuget:?package=LokiCat.Godot.R3.ObservableSignals&version=1.0.70
#tool nuget:?package=LokiCat.Godot.R3.ObservableSignals&version=1.0.70
LokiCat.Godot.R3.ObservableSignals
A source generator that turns [Signal]
-annotated delegates into fully reactive R3 Observable<T>
properties.
Supports zero to five signal arguments, [RxProperty]
bindings, [InverseSignal]
auto-generation, and full Godot editor compatibility.
✨ What It Does
This generator lets you define signals with [Signal]
in combination with [RxObservable]
or [RxProperty]
:
[Signal]
[RxObservable]
public delegate void JumpEventHandler();
💥 And automatically provides:
- ✅ A backing
Subject<T>
orRxVar<T>
- ✅ A public observable or reactive property (
OnJump
,IsDead
, etc.) - ✅ Lazy
.Subscribe(...)
wiring that callsEmitSignal(...)
automatically
No need to write Connect()
, EmitSignal()
, or observable plumbing by hand.
🚀 Quick Start
✅ Define a signal:
[Signal]
[RxObservable]
public delegate void DamageTakenEventHandler(int amount);
✅ Emit it:
_onDamageTaken.OnNext(42);
✅ Observe it:
OnDamageTaken.Subscribe(amount => GD.Print($"Took {amount} damage!"));
🚫 [Signal]
alone will not generate code. You must pair it with [RxObservable]
or [RxProperty]
.
🔄 RxProperty Support
Use [RxProperty]
to generate a reactive IRxProp<T>
that syncs with a signal:
[Signal]
[RxProperty]
public delegate void IsDeadEventHandler(bool isDead);
Generates:
public IRxProp<bool> IsDead => _isDead;
private RxVar<bool> _isDead = new RxVar<bool>(false);
/* And wiring to the Godot signal */
🧠 Naming Rules
The generated property name:
- Always starts with
Is
- Removes a leading
Is
only if it's a full word prefix (i.e.Is
followed by an uppercase letter) - Does not remove
Is
from words likeIsland
,Isotope
, etc. - Strips the
EventHandler
suffix
Examples:
Delegate Name | Generated Property Name |
---|---|
IsDeadEventHandler |
IsDead |
DeadEventHandler |
IsDead |
IslandEventHandler |
IsIsland |
IsIslandEventHandler |
IsIsland |
IslandTimeEventHandler |
IsIslandTime |
IsIslandTimeEventHandler |
IsIslandTime |
IsNotNotarizedEventHandler |
IsNotarized |
IsNoteInverselyInverseEventHandler |
IsNoteInversely |
🔁 Inverse Signal Support
Generate inverse signals from a single source:
🗒️ Godot does not support easy transformation of signals. This generator allows you to define a signal and automatically generate its inverse.
💡 TODO: Add more robust signal transformation support via
[SignalTransform(<TBD Syntax>)]
attribute.
[Signal]
[RxObservable]
public delegate void VisibleEventHandler(bool value);
// inferred from name
[Signal]
[InverseSignal]
public delegate void NotVisibleEventHandler(bool value);
// explicit source:
[Signal]
[InverseSignal(nameof(IsVisibleEventHandler))]
public delegate void HiddenEventHandler(bool value);
✅ Emits EmitSignal("Hidden", !value)
when OnIsVisible
is triggered.
🚫 Only valid for bool
signals
🚫 Cannot be used with [RxProperty]
🔍 Inferred Naming Behavior
When no nameof(...)
is used, the generator infers the target by removing the words Not
or Inverse
only when they are complete words — using word-boundary-aware matching. It does not remove them when embedded in words:
Inverse Name | Inferred Target Name |
---|---|
NotVisibleEventHandler |
VisibleEventHandler |
InverseHiddenEventHandler |
HiddenEventHandler |
IsNotNotarizedEventHandler |
IsNotarizedEventHandler ✅ no strip inside word |
IsNoteInverselyInverseEventHandler |
IsNoteInverselyEventHandler ✅ no strip |
IsInvisible |
InvisibleEventHandler ❗ no automatic mapping to IsVisible |
For symmetric relationships (e.g. IsVisible
/IsInvisible
), use [InverseSignal(nameof(...))]
explicitly.
✅ Supported Signal Signatures
Delegate Type | Observable Type | Emission |
---|---|---|
delegate void JumpEventHandler() |
Observable<Unit> |
EmitSignal("Jump") |
delegate void DamageEventHandler(int) |
Observable<int> |
EmitSignal("Damage", dmg) |
delegate void HitEventHandler(Vector3, Node) |
Observable<(Vector3, Node)> |
EmitSignal("Hit", ...) |
delegate void IsDeadEventHandler(bool) |
IRxProp<bool> ([RxProperty] ) |
EmitSignal("IsDead", value) |
Attribute | Observable Type | Parameter Count |
---|---|---|
[RxProperty] |
IRxProp<T> ,RxVar<T> |
0 or 1 (e.g. <Unit> or <T>) |
[RxObservable] |
Observable<int> , Subject<T> |
5 (e.g. <T1,T2,T3,T4,T5> |
[InverseSignal] |
N/A | 1 bool |
delegate void IsDeadEventHandler(bool) |
IRxProp<bool> ([RxProperty] ) |
EmitSignal("IsDead", value) |
Up to 5 parameters are supported.
🚨 Generator Diagnostics
ID | Reason |
---|---|
SIGOBS001 |
Missing R3.Unit for zero-arg signals |
SIGOBS002 |
Signal delegate has more than 5 parameters |
SIGOBS003 |
Manual EmitSignal("X") bypasses observable |
SIGOBS004 |
[InverseSignal] used with [RxProperty] or [RxObservable] |
SIGOBS005 |
More than one [InverseSignal] targets a single signal |
SIGOBS009 |
[RxProperty] and [RxObservable] used together |
SIGOBS010 |
Could not resolve nameof(...) in [InverseSignal] |
SIGOBS011 |
Malformed attribute body in generated code |
SOEG0001 |
Generator is running (informational) |
💡 Best Practices
- ✅ Use
_onX.OnNext(...)
to emit signals - ✅ Subscribe using
OnX.Subscribe(...)
- ⚠️ Do not call
EmitSignal(...)
manually — it will not notify observers - 🧠 Prefer
[RxProperty]
for stateful values - 🔁 Use
[InverseSignal]
for UI toggles, state flips, or binding inverses
📦 Installation
dotnet add package LokiCat.Godot.R3.ObservableSignals
Also define:
namespace R3 {
public readonly struct Unit {} // required for 0-arg signals
}
📜 License
MIT License
Learn more about Target Frameworks and .NET Standard.
-
net7.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
1.0.70 | 234 | 7/2/2025 |
1.0.69 | 118 | 6/21/2025 |
1.0.68 | 105 | 6/21/2025 |
1.0.65 | 141 | 6/19/2025 |
1.0.64 | 140 | 6/19/2025 |
1.0.63 | 139 | 6/19/2025 |
1.0.62 | 137 | 6/19/2025 |
1.0.61 | 139 | 6/19/2025 |
1.0.60 | 141 | 6/19/2025 |
1.0.59 | 139 | 6/19/2025 |
1.0.58 | 142 | 6/19/2025 |
1.0.57 | 139 | 6/19/2025 |
1.0.56 | 144 | 6/19/2025 |
1.0.55 | 139 | 6/19/2025 |
1.0.54 | 139 | 6/18/2025 |
1.0.53 | 140 | 6/18/2025 |
1.0.52 | 137 | 6/18/2025 |
1.0.51 | 145 | 6/18/2025 |
1.0.47 | 153 | 6/14/2025 |
1.0.40 | 154 | 6/14/2025 |
1.0.38 | 163 | 6/14/2025 |
1.0.37 | 138 | 5/9/2025 |
1.0.36 | 81 | 5/9/2025 |
1.0.35 | 81 | 5/9/2025 |
1.0.34 | 82 | 5/9/2025 |
1.0.33 | 146 | 5/8/2025 |
1.0.32 | 146 | 5/8/2025 |
1.0.31 | 146 | 5/1/2025 |
1.0.30 | 152 | 5/1/2025 |
1.0.29 | 151 | 5/1/2025 |
1.0.28 | 150 | 4/30/2025 |
1.0.26 | 147 | 4/30/2025 |
1.0.25 | 146 | 4/30/2025 |
1.0.24 | 144 | 4/30/2025 |
1.0.23 | 152 | 4/30/2025 |
1.0.22 | 150 | 4/30/2025 |
1.0.21 | 142 | 4/30/2025 |
1.0.20 | 146 | 4/30/2025 |
1.0.19 | 142 | 4/29/2025 |
1.0.18 | 159 | 4/29/2025 |
1.0.17 | 156 | 4/28/2025 |
1.0.16 | 159 | 4/27/2025 |
1.0.15 | 166 | 4/27/2025 |
1.0.14 | 159 | 4/27/2025 |
1.0.13 | 157 | 4/27/2025 |
1.0.12 | 155 | 4/27/2025 |
1.0.11 | 152 | 4/27/2025 |
1.0.9 | 160 | 4/27/2025 |
1.0.7 | 161 | 4/27/2025 |
1.0.1 | 152 | 4/27/2025 |
1.0.0 | 159 | 4/27/2025 |
0.0.6 | 247 | 4/19/2025 |
0.0.5 | 169 | 4/19/2025 |