An API designed to streamline C# with Godot Engine without changing any fundamentals. It utilizes cached reflection for optimal performance, and provides power to easily extend the API with your own attributes.
NOTE: Abandoned since I do not use Godot actively anymore, and moved to other projects.
GDMechanic is functionally complete, but a few features are not fully tested.
Most documentation is provided, but extensions are not documented.
GDMechanic was created with Godot 3.1 alpha, and does not work in earlier versions due to namespace changes between 3.0.6 and 3.1 in GodotSharp.
GDMechanic provides a set of core attributes for wiring fields, properties, and methods.
Call this.Wire()
in the node's _Ready() method.
public override void _Ready()
{
this.Wire();
...
}
It is recommended to create a template for wired nodes, like the below.
using Godot;
using GDMechanic.Wiring;
using GDMechanic.Wiring.Attributes;
public class %CLASS% : %BASE%
{
public override void _Ready()
{
this.Wire();
}
}
You can place this template in Godot's "script_templates" folder in order for the IDE to pick it up.
Returns a reference to a node based on the provided node path.
[Node("UI/Score")] private RichTextLabel _scoreText;
Returns a reference to a child based on match type.
[Child] private Player _player;
// or
[Child(Matchtypes.Name)] private Player _player;
[Child(MatchTypes.Type)] private RichTextLabel _score;
Returns a reference to a sibling based on match type.
Returns a reference to the node's parent.
[Parent] private Mommy _mommy;
Adds this node to the specified group.
[Group("Huggables")]
public class Teddy : Node2D
{
...
}
Connects the specified signal from the specified source. The source can either be a node path or a reference from a field on the same node.
[Child] private Button _button2;
[SignalReceiver("Button", "pressed")]
public void OnButtonPressed()
{
...
}
[SignalReceiver("_button2", "pressed", SourceTypes.Reference)]
public void OnButton2Pressed()
{
...
}
Adds a child Timer node with the specified criteria, and assigns a reference to it to the field.
[Timer(nameof(OnTimerTimeout), waitTime: 1.5f, oneShot: true)]
private Timer _timer;
GDMechanic provides an extension method to Timer that makes it easy to start a timer with a different wait time, without overriding the default.
_timer.Start(6f);
You can create your own attributes quite easily. Create a class that extends Attribute and implement IStateWirer, IMethodWirer, or IClassWirer. Also, be sure to add the corresponding attribute targets to the attribute via AttributeUsage.
IStateWirer: AttributeTargets.Field | AttributeTargets.Property
IMethodWirer: AttributeTargets.Method
IClassWirer: AttributeTargets.Class
Then implement the necessary methods. GDMechanic will automatically be able to use it.
Example from NodeAttribute:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class NodeAttribute : MechanicAttribute, IStateWirer
{
private readonly string _path;
/// <summary>
/// Returns a reference to a node based on the provided node path.
/// </summary>
/// <param name="path"></param>
public NodeAttribute(string path) {
_path = path;
}
public void Wire(Node node, CachedNodeStateInfo state)
{
state.SetValue(node, node.GetNode(_path));
}
}
GDMechanic provides extension methods for various nodes. Here are some examples.
public static T InstanceToParent<T>(this PackedScene packedScene, Node parent) where T: Node
public static IEnumerable<object> GetNodesOfType<T>(this SceneTree sceneTree) where T : Node
public static void TranslateX(this Node2D node, float xDisplacement)
Reimplementation of GDScript's "rand" functions. It uses System.Random under the hood, so it will not have the same output as GDScript's "rand" functions.
Rng.RandRange(0f, 6f);
Randomly returns true or false, based on the specified ratio.
if (Rng.Chance(0.05f))
{
GD.Print("Critical hit!");
}
Node that organizes a collection of timers for easy access through delegates. It is designed to be used with TimerSystemAttribute and TimerReceiverAttribute, although it is fully functional on its own.
[TimerSystem]
private TimerSystem _timerSystem;
public void StartTimer() {
_timerSystem.Start(OnTimerTimeout);
}
[TimerReceiver(waitTime: 0.25f, oneShot: true)]
public void OnTimerTimeout()
{
...
}
[TimerReceiver(waitTime: 3f, oneShot: true)]
public void OnDeathTimeout()
{
...
}
Alternatively you can place TimerReceiverAttribute on the same field as the TimerSystem reference.
[TimerSystem]
[TimerReceiver(nameof(OnTimerTimeout), waitTime: 0.25f, oneShot: true)]
private TimerSystem _timerSystem;
public void StartTimer() {
_timerSystem.Start(OnTimerTimeout);
}
public void OnTimerTimeout()
{
...
}
[TimerReceiver(waitTime: 3f, oneShot: true)]
public void OnDeathTimeout()
{
...
}