FloSoft.AnimationTreeGen
1.0.0
dotnet add package FloSoft.AnimationTreeGen --version 1.0.0
NuGet\Install-Package FloSoft.AnimationTreeGen -Version 1.0.0
<PackageReference Include="FloSoft.AnimationTreeGen" Version="1.0.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="FloSoft.AnimationTreeGen" Version="1.0.0" />
<PackageReference Include="FloSoft.AnimationTreeGen"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add FloSoft.AnimationTreeGen --version 1.0.0
#r "nuget: FloSoft.AnimationTreeGen, 1.0.0"
#:package FloSoft.AnimationTreeGen@1.0.0
#addin nuget:?package=FloSoft.AnimationTreeGen&version=1.0.0
#tool nuget:?package=FloSoft.AnimationTreeGen&version=1.0.0
FloSoft.AnimationTreeGen
Roslyn source generator that emits typed StringName constants and state enums
for Godot AnimationTree parameter paths.
Replaces brittle code like:
_tree.Set("parameters/StanceSelector/transition_request", $"{stance}_{weapon}");
with:
_tree.Set(Anim.StanceSelector.TransitionRequest, ...);
Typos in parameter paths or state names become compile errors instead of silent runtime failures.
Install
Published on nuget.org.
dotnet add package FloSoft.AnimationTreeGen
or in your .csproj:
<PackageReference Include="FloSoft.AnimationTreeGen" Version="1.0.0" />
The package is analyzer-only (no runtime DLL — the [AnimationTree]
attribute is injected as internal source into your assembly by the
generator itself, via RegisterPostInitializationOutput). Marked
developmentDependency so it doesn't flow transitively to projects that
depend on yours; no extra asset attributes are required.
For each .tres or .tscn you want to bind, register it as an AdditionalFile:
<ItemGroup>
<AdditionalFiles Include="character/character_tree.tres" />
<AdditionalFiles Include="scene/character_scene.tscn" />
</ItemGroup>
Minimal example
Note: you need to add the [TreeBind("res://some/path/to/tree.tres")]
attribute AND make your class partial so the generator can extend it.
using Godot;
using FloSoft.AnimationTreeGen;
[TreeBind("res://character/character_tree.tres")]
public partial class Character : Node3D
{
public void GoArmed()
{
var tree = GetNode<AnimationTree>("AnimationTree");
tree.Set(
Anim.ArmedToggle.TransitionRequest,
Anim.ArmedToggle.StateNames.Armed); // literal Godot value, typo-safe
}
}
Binding an AnimationTree embedded in a .tscn
You can also point at a scene file directly:
[TreeBind("res://scene/character.tscn")] // auto-pick when there's one AnimationTree node
public partial class Character : Node3D { ... }
[TreeBind("res://scene/character.tscn", Node = "BodyTree")] // disambiguate when there are several
public partial class BodyAnim { ... }
[TreeBind("res://scene/character.tscn", Node = "FaceTree")]
public partial class FaceAnim { ... }
If the scene contains exactly one [node type="AnimationTree" ...], the Node = "..." argument is optional. With multiple AnimationTree nodes, omitting it raises ATG006 and the generator lists the available names.
Generated shape
The generator emits a nested Anim static class — one nested type per named
node in the BlendTree:
public static class Anim
{
public static class ArmedToggle
{
public const string TransitionRequest = "parameters/ArmedToggle/transition_request";
public const string CurrentState = "parameters/ArmedToggle/current_state";
public const string CurrentIndex = "parameters/ArmedToggle/current_index";
// Both shapes are emitted for Transition states — pick whichever fits the call site.
public enum States { Unarmed, Armed }
public static class StateNames
{
public const string Unarmed = "unarmed";
public const string Armed = "armed";
}
}
// ... one nested class per named node
}
Generated members are const string rather than StringName fields — Godot's AnimationTree.Set(...) accepts either, and const string has zero runtime initialization (the compiler inlines the literal at every call site, and there's no Godot.* type referenced anywhere in the generated code). Trade-off: each call allocates a fresh StringName via implicit conversion — negligible for typical state-change calls; cache locally if you have a hot per-frame loop.
Use States (the enum) for typed method signatures, switch expressions, and anywhere C# can statically check the value. Use StateNames for tree.Set(...) / Get(...) calls that ultimately need the literal Godot string — the value matches what was declared in the .tres/.tscn input_<N>/name field exactly, including casing.
Supported nodes
| Godot type | Emitted members |
|---|---|
AnimationNodeTransition |
TransitionRequest, CurrentState, CurrentIndex, plus States enum and StateNames const-string class from input_<N>/name |
AnimationNodeBlend2, Blend3 |
BlendAmount |
AnimationNodeAdd2, Add3 |
AddAmount |
AnimationNodeOneShot |
Request, Active, InternalActive |
AnimationNodeTimeScale |
Scale |
AnimationNodeAnimation |
Animation — the node's design-time animation key (e.g. for AnimationPlayer.Play(...)), not a runtime parameter path |
Tree shape supported in v1:
AnimationTree.tree_root = AnimationNodeBlendTree with the children above.
Nodes whose type isn't on this list are skipped with an ATG004 warning so
the rest of the tree still generates.
Diagnostics
| ID | Severity | Meaning |
|---|---|---|
ATG001 |
Error | Referenced .tres/.tscn is not in <AdditionalFiles> |
ATG002 |
Error | .tres/.tscn parse failure |
ATG003 |
Error | tree_root is missing or not an AnimationNodeBlendTree |
ATG004 |
Warning | Child node has unsupported AnimationNode type, skipped |
ATG005 |
Warning | Decorated class is not declared partial |
ATG006 |
Error | Scene contains multiple AnimationTree nodes; Node = "..." required |
ATG007 |
Error | Specified AnimationTree node not found in scene |
Not yet supported in v1
Deferred (PRs welcome):
AnimationNodeStateMachineastree_root- Nested
BlendTrees BlendSpace1D/BlendSpace2DExtResource(external) tree roots
Edge cases that currently fall through to a normal C# compile error:
- Generic decorated classes
- Nested decorated classes
- Decorated class names that shadow C# keywords
- Duplicate node-name or state-name collisions after identifier sanitization
Adding a new node type
The supported-nodes table is data, not code:
- Add a
NodeTypeDescriptorentry tosrc/FloSoft.AnimationTreeGen/Mapping/NodeParameterMap.cs— Godot type name, parameter name pairs, and theHasStatesflag. - Add a hand-crafted
.tresfixture undertests/FloSoft.AnimationTreeGen.Tests/Fixtures/exercising the new type as a child of aBlendTree. - Add an
[InlineData]row to the existing[Theory]inNodeParameterMapTests, plus an emitter test if the output shape is novel. - If you also added a new diagnostic, append it to
src/FloSoft.AnimationTreeGen/AnalyzerReleases.Unshipped.md.
Then dotnet test.
Local development
# Unit tests
dotnet test tests/FloSoft.AnimationTreeGen.Tests
# End-to-end: verify the generator output compiles in real consumers
dotnet build samples/SampleConsumer # .tres binding
dotnet build samples/SceneConsumer # .tscn binding
# Produce a local nupkg, then verify it installs cleanly
dotnet pack src/FloSoft.AnimationTreeGen -c Release -o nupkgs
dotnet build samples/PackagedConsumer
samples/PackagedConsumer/ consumes the local nupkg from nupkgs/ via a
project-local NuGet.config, so re-packing and rebuilding it is a fast
end-to-end check. It's deliberately not part of solution-wide flows
(dotnet build/dotnet test against the .sln would fail with NU1301 on a
fresh clone, since nupkgs/ doesn't exist yet) — always invoke it explicitly
after a dotnet pack.
Repo layout
src/FloSoft.AnimationTreeGen/ ← the source generator (netstandard2.0); also injects the [AnimationTree] attribute
tests/FloSoft.AnimationTreeGen.Tests/ ← parser + model + emitter + generator tests (net9.0)
samples/SampleConsumer/ ← .tres binding via ProjectReference (fast iteration)
samples/SceneConsumer/ ← .tscn binding with two AnimationTree nodes + Node = "..." disambiguation
samples/PackagedConsumer/ ← consumes the local nupkg (shipping verification)
Learn more about Target Frameworks and .NET Standard.
This package has 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 | 131 | 5/31/2026 |