Righthand.GodotPropertyChangedGenerator
1.0.0-beta.5
Prefix Reserved
dotnet add package Righthand.GodotPropertyChangedGenerator --version 1.0.0-beta.5
NuGet\Install-Package Righthand.GodotPropertyChangedGenerator -Version 1.0.0-beta.5
<PackageReference Include="Righthand.GodotPropertyChangedGenerator" Version="1.0.0-beta.5"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="Righthand.GodotPropertyChangedGenerator" Version="1.0.0-beta.5" />
<PackageReference Include="Righthand.GodotPropertyChangedGenerator"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Righthand.GodotPropertyChangedGenerator --version 1.0.0-beta.5
#r "nuget: Righthand.GodotPropertyChangedGenerator, 1.0.0-beta.5"
#:package Righthand.GodotPropertyChangedGenerator@1.0.0-beta.5
#addin nuget:?package=Righthand.GodotPropertyChangedGenerator&version=1.0.0-beta.5&prerelease
#tool nuget:?package=Righthand.GodotPropertyChangedGenerator&version=1.0.0-beta.5&prerelease
Righthand Godot Properties Changed Source Generator (RGPCSG)
| Development | Release |
|---|---|
Why
This source generator is build with Godot tools in mind where your scene script has to notify Godot editor that it needs to redraw the node upon property changes. But it can be used everywhere where property change notification is required or one or more of other supported scenarios.
Imagine writing the code below for a Godot node without this source generator.
private int _simpleExportedWithDefault = 5;
public int SimpleExportedWithDefault
{
get => _simpleExportedWithDefault;
set
{
if (_simpleExportedWithDefault != value)
{
_simpleExportedWithDefault = value;
UpdateState(nameof(SimpleExportedWithDefault));
}
}
}
private void UpdateState(string? propertyName)
{
QueueRedraw();
}
Instead, the code below has the same functionality when RGPCSG is used.
[Export, DefaultValue(5)] public partial int SimpleExportedWithDefault { get; set; }
private void UpdateState(string? propertyName)
{
QueueRedraw();
}
RGPCSG generates the rest of the code for free. More advanced scenarios are supported as well.
How to use
- Add Righthand.GodotPropertyChangedGenerator package from NuGet. Make sure you enable prerelease until stable version is released
- Create exported partial properties.
- Add
void UpdateState(string? propertyName)implementation - usually one would callQueueRedraw()to redraw the node at design time. - When using
NoStoreAttribute_DisablePropertiesStoragehas to be called with code like
public override void _ValidateProperty(Dictionary property) => _DisablePropertiesStorage(property);
Sample client is included in repository.
Supported scenarios
Default behavior
Calls UpdateState upon value changes.
Default values
There are two ways to provide default values.
- When default value is a constant, a
DefaultValueAttributecan be used.
[Export, DefaultValue(5)] public partial int SimpleExportedWithDefault { get; set; }
- When default value is not a constant, a properly named
[PROPERTYNAME]Defaultstatic property of the same type can be used:
[Export] public partial Color SampleProperty { get; set; }
public static readonly Color SamplePropertyDefault = Colors.Red;
Note: this approach can be used with constant values as well.
Making properties non persistent
Godot provides a way for making properties not persistant. If such behavior is desired, property has to be
decorated with NoStoreAttribute and a call to _DisablePropertiesStorage has to be made within overriden _ValidateProperty method.
[Export, NoStore] public partial int NoStoreSimpleExported { get; set; }
public override void _ValidateProperty(Dictionary property) => _DisablePropertiesStorage(property);
Coerce values
When value has to be coerced, a properly named Coerce[PROPERTYNAME] coercion function with same types can be provided.
[Export] public partial int ExportedWithCoercion { get; set; }
public static int CoerceExportedWithCoercion(int newValue) => Math.Min(100, newValue);
Code comparision
See provided sample.
Without RGPCSG generator
public partial class TestTraditional: Node2D
{
private int _simpleExported;
public int SimpleExported
{
get => _simpleExported;
set
{
if (_simpleExported != value)
{
_simpleExported = value;
UpdateState(nameof(SimpleExported));
}
}
}
private int _noStoreSimpleExported;
public int NoStoreSimpleExported
{
get => _noStoreSimpleExported;
set
{
if (_noStoreSimpleExported != value)
{
_noStoreSimpleExported = value;
UpdateState(nameof(NoStoreSimpleExported));
}
}
}
private int _simpleExportedWithDefault = 5;
public int SimpleExportedWithDefault
{
get => _simpleExportedWithDefault;
set
{
if (_simpleExportedWithDefault != value)
{
_simpleExportedWithDefault = value;
UpdateState(nameof(SimpleExportedWithDefault));
}
}
}
private Color _notConstTypeExportedWithDefault = Colors.Red;
public Color NotConstTypeExportedWithDefault
{
get => _notConstTypeExportedWithDefault;
set
{
if (_notConstTypeExportedWithDefault != value)
{
_notConstTypeExportedWithDefault = value;
UpdateState(nameof(NotConstTypeExportedWithDefault));
}
}
}
private int _exportedWithCoercion;
public int ExportedWithCoercion
{
get => _exportedWithCoercion;
set
{
if (_exportedWithCoercion != value)
{
_exportedWithCoercion = CoerceExportedWithCoercion(value);
UpdateState(nameof(ExportedWithCoercion));
}
}
}
public static int CoerceExportedWithCoercion(int newValue) => Math.Min(100, newValue);
public override void _ValidateProperty(Dictionary property)
{
const string usageKey = "usage";
const string nameKey = "name";
if (property.TryGetValue(nameKey, out var nameValue) || nameValue.VariantType != Variant.Type.String)
{
var name = nameValue.AsString();
if (name.Equals(nameof(NoStoreSimpleExported), StringComparison.Ordinal))
{
if (!property.TryGetValue(usageKey, out var usageVariant)
|| usageVariant.VariantType != Variant.Type.Int)
{
var usage = usageVariant.AsInt32();
var newUsage = usage &= ~(int)PropertyUsageFlags.Storage;
if (usage != newUsage)
{
property[usageKey] = Variant.CreateFrom(newUsage);
}
}
}
}
base._ValidateProperty(property);
}
private void UpdateState(string? propertyName)
{
QueueRedraw();
}
}
With RGPCSG generator
public partial class Test : Node2D
{
[Export] public partial int SimpleExported { get; set; }
[Export, NoStore] public partial int NoStoreSimpleExported { get; set; }
[Export, DefaultValue(5)] public partial int SimpleExportedWithDefault { get; set; }
[Export] public partial Color NotConstTypeExportedWithDefault { get; set; }
public static readonly Color NotConstTypeExportedWithDefaultDefault = Colors.Red;
[Export] public partial int ExportedWithCoercion { get; set; }
public static int CoerceExportedWithCoercion(int newValue) => Math.Min(100, newValue);
private void UpdateState(string? propertyName)
{
QueueRedraw();
}
public override void _ValidateProperty(Dictionary property) => _DisablePropertiesStorage(property);
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.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.0-beta.5 | 74 | 1/25/2026 |
| 1.0.0-beta.4 | 70 | 1/24/2026 |
| 1.0.0-beta.3 | 64 | 1/18/2026 |
| 1.0.0-beta.2 | 67 | 1/17/2026 |
| 1.0.0-beta.1 | 66 | 1/17/2026 |
* adds #nullable enable to generated files