Chickensoft.GodotNodeInterfaces 2.1.6-godot4.2.0-rc.2

This is a prerelease version of Chickensoft.GodotNodeInterfaces.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Chickensoft.GodotNodeInterfaces --version 2.1.6-godot4.2.0-rc.2                
NuGet\Install-Package Chickensoft.GodotNodeInterfaces -Version 2.1.6-godot4.2.0-rc.2                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="2.1.6-godot4.2.0-rc.2" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Chickensoft.GodotNodeInterfaces --version 2.1.6-godot4.2.0-rc.2                
#r "nuget: Chickensoft.GodotNodeInterfaces, 2.1.6-godot4.2.0-rc.2"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Chickensoft.GodotNodeInterfaces as a Cake Addin
#addin nuget:?package=Chickensoft.GodotNodeInterfaces&version=2.1.6-godot4.2.0-rc.2&prerelease

// Install Chickensoft.GodotNodeInterfaces as a Cake Tool
#tool nuget:?package=Chickensoft.GodotNodeInterfaces&version=2.1.6-godot4.2.0-rc.2&prerelease                

GodotNodeInterfaces

Chickensoft Badge Discord Read the docs

Godot node interfaces and adapters.


<p align="center"> <img alt="Chickensoft.GodotNodeInterfaces" src="Chickensoft.GodotNodeInterfaces/icon.png" width="200"> </p>

๐Ÿค” What...why?

In a perfect world, there are cases where mocking Godot nodes for testing makes a lot of sense:

  • You are a TDD cultist.
  • You want to unit-test a Godot node script.
  • You don't want to instantiate the corresponding scene for a node script. Why? Because instantiating a scene also instantiates any children and their scripts, and so on. Just instantiating scripts and adding them to the scene causes test coverage to be collected, and it ends up covering far more of the app than the actual system-under-test (the node script you were trying to test), which makes it hard to tell which scripts are still un-tested.

GodotSharp doesn't expose interfaces for Godot nodes, making it very expensive to mock them using proprietary enterprise-grade mocking solutions.

If you're still confused, this probably isn't for you. It's one of those "you'll know if you ever want/need this" type of things. This is really just for those completionists who like writing tests and getting 100% code coverage.

๐Ÿง I don't see anything here

That's because the interfaces and adapters are generated at build-time based on the version of the GodotSharp API being referenced.

Here's a sample of a generated interface:

/// <summary>
/// <para>Casts light in a 2D environment. A light is defined as a color, an energy value, a mode (see constants), and various other parameters (range and shadows-related).</para>
/// </summary>
public interface ILight2D : INode2D {
    /// <summary>
    /// <para>The Light2D's blend mode. See <see cref="Light2D.BlendModeEnum" /> constants for values.</para>
    /// </summary>
    Light2D.BlendModeEnum BlendMode { get; set; }
    /// <summary>
    /// <para>The Light2D's <see cref="Color" />.</para>
    /// </summary>
    Color Color { get; set; }
    /// <summary>
    /// <para>If <c>true</c>, Light2D will only appear when editing the scene.</para>
    /// </summary>
    bool EditorOnly { get; set; }

    ...

And here's the corresponding adapter:

/// <summary>
/// <para>Casts light in a 2D environment. A light is defined as a color, an energy value, a mode (see constants), and various other parameters (range and shadows-related).</para>
/// </summary>
public class Light2DAdapter : Node2DAdapter, ILight2D, INodeAdapter {
  /// <summary>Underlying Godot object this adapter uses.</summary>
  public new Light2D TargetObj { get; private set; }

  /// <summary>Creates a new Light2DAdapter for Light2D.</summary>
  /// <param name="object">Godot object.</param>
  public Light2DAdapter(GodotObject @object) : base(@object) {
    if (@object is not Light2D typedObj) {
      throw new System.InvalidCastException(
        $"{@object.GetType().Name} is not a Light2D"
      );
    }
    TargetObj = typedObj;
  }

    /// <summary>
    /// <para>The Light2D's blend mode. See <see cref="Godot.Light2D.BlendModeEnum" /> constants for values.</para>
    /// </summary>
    public Light2D.BlendModeEnum BlendMode { get => TargetObj.BlendMode; set => TargetObj.BlendMode = value; }

    ...

And here's what the adapter factory looks like:

public static class GodotNodes {
  private static readonly Dictionary<Type, Func<Node, IGodotNodeAdapter>> _adapters = new() {
      [typeof(INode)] = node => new NodeAdapter(node),
      [typeof(IAnimationPlayer)] = node => new AnimationPlayerAdapter(node),
      [typeof(IAnimationTree)] = node => new AnimationTreeAdapter(node),
      [typeof(ICodeEdit)] = node => new CodeEditAdapter(node),
      [typeof(IGraphEdit)] = node => new GraphEditAdapter(node),

      ...

๐Ÿคฏ Using Interfaces

GodotNodeInterfaces adds some extended method variants for common child node operations to adapters and vanilla Godot nodes (via extension methods).

  • AddChildEx()
  • FindChildEx()
  • FindChildrenEx()
  • GetChildEx<T>()
  • GetChildEx()
  • GetChildCountEx()
  • GetChildrenEx()
  • GetChildOrNullEx()
  • GetChildrenEx()
  • GetNodeEx()
  • GetNodeOrNullEx<T>()
  • GetNodeOrNullEx()
  • HasNodeEx()
  • RemoveChildEx()

These methods receive arbitrary objects that can be either Godot node instances or Godot interface adapters. The methods that return nodes will return adapted nodes so that you can continue to use interface types.

If you use these methods instead of the Godot version, you can mock subtree operations, making it easier to unit-test node scripts.

๐Ÿ“ฆ Installation

Just install the package from nuget!

dotnet add package Chickensoft.GodotNodeInterfaces 

๐Ÿ“– Usage

You can use interfaces in your Godot node scripts and use the various Ex methods mentioned above to manipulate the scene tree.

Once you have that setup, you can use it in your node scripts.

public partial class MyNode : Node2D {
  public ISprite2D Sprite { get; set; } = default!;

  public override void _Ready() {
    Sprite = this.GetNodeEx<ISprite2D>("Sprite");
  }
}

๐Ÿงช Testing

All Godot node adapter implementations have a FakeNodes dictionary that is checked first by each of the Ex node manipulation methods, like GetNodeEx, GetChildrenEx, etc. If a mocked node is found in the dictionary for the given path, it will be returned instead of looking for an actual node. This allows Godot nodes to be tested.

For example, here's a node which grabs a reference to one of its child nodes in _Ready.

using Chickensoft.GodotNodeInterfaces;
using Chickensoft.PowerUps;
using Godot;
using SuperNodes.Types;

[SuperNode(typeof(AutoNode))]
public partial class MyNode : Node2D {
  public override partial void _Notification(int what);

  public INode2D MyChild { get; set; } = default!;

  public void OnReady() {
    // This automatically finds the node and creates a Godot node adapter
    // so that we can refer to it by its interface.
    MyChild = this.GetNodeEx<INode2D>("MyChild");
  }
}

Using the AutoNode PowerUp from the Chickensoft PowerUps allows us to use a fake node tree for our node instance. More on that in a second.

Since we're using interfaces to refer to the child node, we can mock it in our tests.

[Test]
public void LoadsGame() {
  var node = new MyNode();

  var myChild = new Mock<INode2D>().Object;

  // We can fake the children of the node we're testing by supplying
  // a dictionary of node paths that correspond to mock node objects.
  // Since we're referencing nodes by interfaces in our node script, this
  // works!
  node.FakeNodeTree(new() {
    ["MyChild"] = myChild.Object
  });

  node.OnReady();

  // Make sure our node did what we expected it to do.
  node.MyChild.ShouldBe(myChild.Object);
}

๐Ÿ’ Getting Help

Is this package broken? Encountering obscure C# build problems? We'll be happy to help you in the Chickensoft Discord server.

๐Ÿ’ช Contributing

This project contains a very hastily written console generator program that uses reflection (yep!) to find all the types in GodotSharp that are or extend the Godot Node class, and then generates interfaces, adapters, and an adapter factory.

The actual package is left empty โ€”ย we generate the project in CI/CD so that we can keep this package up-to-date with renovatebot and automatically release a new version whenever a new GodotSharp package drops.

๐Ÿž Debugging

A VSCode launch profile is provided that allows you to debug the generator program. This can be great when trying to figure out why it's generating invalid code.

Important: You must setup a GODOT environment variable for the launch configurations above. If you haven't done so already, please see the Chickensoft Setup Docs.

๐Ÿš Renovatebot

This repository includes a renovate.json configuration for use with Renovatebot to help keep it up-to-date.

Renovatebot Pull Request

Unlike Dependabot, Renovatebot is able to combine all dependency updates into a single pull request โ€”ย a must-have for Godot C# repositories where each sub-project needs the same Godot.NET.Sdk versions. If dependency version bumps were split across multiple repositories, the builds would fail in CI.

The easiest way to add Renovatebot to your repository is to install it from the GitHub Marketplace. Note that you have to grant it access to each organization and repository you want it to monitor.

The included renovate.json includes a few configuration options to limit how often Renovatebot can open pull requests as well as regex's to filter out some poorly versioned dependencies to prevent invalid dependency version updates.

If your project is setup to require approvals before pull requests can be merged and you wish to take advantage of Renovatebot's auto-merge feature, you can install the Renovate Approve bot to automatically approve the Renovate dependency PR's. If you need two approvals, you can install the identical Renovate Approve 2 bot. See this for more information.


๐Ÿฃ Package generated from a ๐Ÿค Chickensoft Template โ€” https://chickensoft.games

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories (2)

Showing the top 2 popular GitHub repositories that depend on Chickensoft.GodotNodeInterfaces:

Repository Stars
chickensoft-games/GameDemo
The Chickensoft Game Demo โ€” a fully tested, third-person 3D game built with Godot and C#. Now with saving and loading!
chickensoft-games/AutoInject
Node-based dependency injection for C# Godot scripts at build-time, including utilities for automatic node-binding, additional lifecycle hooks, and .net-inspired notification callbacks.
Version Downloads Last updated
2.2.31 259 12/4/2024
2.2.30 293 11/13/2024
2.2.29 387 10/30/2024
2.2.28 355 10/13/2024
2.2.27 157 10/9/2024
2.2.26 125 10/4/2024
2.2.25 243 9/25/2024
2.2.24 271 9/18/2024
2.2.23 245 9/10/2024
2.2.22 667 8/16/2024
2.2.21 146 8/15/2024
2.2.20-godot4.3.0-rc.3 82 8/14/2024
2.2.19-godot4.3.0-rc.3 69 8/14/2024
2.2.18-godot4.3.0-rc.3 83 8/13/2024
2.2.17-godot4.3.0-rc.3 73 8/8/2024
2.2.16-godot4.3.0-rc.3 73 8/8/2024
2.2.15-godot4.3.0-rc.2 49 8/2/2024
2.2.14-godot4.3.0-rc.2 53 8/1/2024
2.2.13-godot4.3.0-rc.1 58 8/1/2024
2.2.12-godot4.3.0-rc.1 81 7/25/2024
2.2.11-godot4.3.0-beta.3 80 7/10/2024
2.2.10-godot4.3.0-beta.3 56 7/9/2024
2.2.9-godot4.3.0-beta.2 80 6/20/2024
2.2.8-godot4.3.0-beta.1 69 6/20/2024
2.2.7-godot4.3.0-beta.1 67 6/12/2024
2.2.6-godot4.3.0-beta.1 54 6/11/2024
2.2.5-godot4.3.0-beta.1 77 5/31/2024
2.2.4 848 5/30/2024
2.2.3 251 5/16/2024
2.2.2 141 5/15/2024
2.2.1 175 5/11/2024
2.2.0 406 5/11/2024
2.1.44 115 5/8/2024
2.1.43 152 4/30/2024
2.1.42 165 4/17/2024
2.1.41 135 4/17/2024
2.1.40-godot4.2.2-rc.3.1 68 4/12/2024
2.1.39-godot4.2.2-rc.3.1 58 4/11/2024
2.1.38-godot4.2.2-rc.3.1 60 4/10/2024
2.1.37-godot4.2.2-rc.3 63 4/9/2024
2.1.36-godot4.2.2-rc.2 73 3/20/2024
2.1.35-godot4.2.2-rc.2 59 3/19/2024
2.1.34-godot4.2.2-rc.2 64 3/14/2024
2.1.33-godot4.2.2-rc.2 64 3/13/2024
2.1.32-godot4.2.2-rc.1 56 3/12/2024
2.1.31-godot4.2.2-rc.1 65 3/3/2024
2.1.30-godot4.2.2-rc.1 63 3/2/2024
2.1.29-godot4.2.2-rc.1 65 2/29/2024
2.1.28-godot4.2.2-rc.1 71 2/14/2024
2.1.27-godot4.2.2-rc.1 67 2/14/2024
2.1.26-godot4.2.2-rc.1 69 2/13/2024
2.1.25-godot4.2.2-rc.1 71 2/8/2024
2.1.24-godot4.2.2-rc.1 65 2/6/2024
2.1.23-godot4.2.2-rc.1 66 2/2/2024
2.1.22-godot4.2.2-rc.1 71 1/26/2024
2.1.21-godot4.2.2-rc.1 55 1/26/2024
2.1.20 373 1/23/2024
2.1.19 136 1/10/2024
2.1.18 945 12/20/2023
2.1.17 144 12/18/2023
2.1.16 168 12/16/2023
2.1.15 153 12/13/2023
2.1.14 151 12/12/2023
2.1.13-godot4.2.1-rc.1 85 12/8/2023
2.1.12-godot4.2.1-rc.1 75 12/7/2023
2.1.11 164 12/4/2023
2.1.10 184 11/30/2023
2.1.9 145 11/30/2023
2.1.8-godot4.2.0-rc.2 83 11/26/2023
2.1.7-godot4.2.0-rc.2 67 11/26/2023
2.1.6-godot4.2.0-rc.2 69 11/25/2023
2.1.5-godot4.2.0-rc.1 79 11/22/2023
2.1.4-godot4.2.0-rc.1 83 11/17/2023
2.1.3-godot4.2.0-beta.6 69 11/14/2023
2.1.2-godot4.2.0-beta.6 62 11/14/2023
2.1.1-godot4.2.0-beta.6 70 11/13/2023
2.1.0-godot4.2.0-beta.5 515 11/9/2023
2.0.0-godot4.2.0-beta.5 111 11/8/2023
1.8.10-godot4.2.0-beta.5 69 11/7/2023
1.8.9-godot4.2.0-beta.5 76 11/6/2023
1.8.8-godot4.2.0-beta.4 75 11/3/2023
1.8.7-godot4.2.0-beta.4 69 10/31/2023
1.8.6-godot4.2.0-beta.3 76 10/27/2023
1.8.5-godot4.2.0-beta.3 68 10/24/2023
1.8.4-godot4.2.0-beta.2 72 10/20/2023
1.8.3-godot4.2.0-beta.2 95 10/20/2023
1.8.2-godot4.2.0-beta.2 67 10/19/2023
1.8.1-godot4.2.0-beta.2 69 10/19/2023
1.8.0-godot4.2.0-beta.1 111 10/17/2023
1.7.0-godot4.2.0-beta.1 81 10/17/2023
1.6.0-godot4.2.0-beta.1 75 10/17/2023
1.5.0-godot4.2.0-beta.1 76 10/17/2023
1.4.0-godot4.2.0-beta.1 82 10/17/2023
1.3.0-godot4.2.0-beta.1 76 10/17/2023
1.2.0-godot4.2.0-beta.1 81 10/15/2023
1.1.1-godot4.2.0-beta.1 78 10/15/2023
1.1.0-godot4.2.0-beta.1 82 10/15/2023
1.0.1-godot4.2.0-beta.1 83 10/15/2023
1.0.0-godot4.2.0-beta.1 78 10/15/2023

Chickensoft.GodotNodeInterfaces release.