概要
Godot の C# スクリプトに関する話題です。
C# コードで独自に定義したノードのインスタンスを作成するには ↓ のようにする必要があります。
Godot のバージョン: 4.1.2
.NET のバージョン: 8.0.2
PackedScene scene = GD.Load<PackedScene>("res://MyNode.tscn");
MyNode node = scene.Instantiate<MyNode>();
// ↑ は _Ready() まで呼ばれた状態でインスタンスを取得
// 普通に new する方法はうまくいかない
// MyNode node = new MyNode();
文字列リテラルの引数を消したい
上記の方法の問題点として、文字列リテラルがコードに現れてしまいます。
文字列リテラルは書き間違いに気づきにくい点、クラス名の変更やファイルの場所の移動などリファクタリングの妨げになる点で避けたいところです。
これは、コンパイル時に Godot 側が独自に定義したノードに自動で付与する属性 ScriptPathAttribute
を活用して解決できます。
NodeLoader.cs
using Godot;
using System.Reflection;
/// <summary>
/// 新しいノードインスタンスを作成するクラス
/// </summary>
/// <typeparam name="TNode"></typeparam>
internal static class NodeLoader<TNode> where TNode : class
{
private static PackedScene? _scene;
/// <summary>
/// 新しいノードのファクトリを取得<br/>
/// 対象のノード定義ファイル("*.tscn")が存在しない場合は例外
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
internal static PackedScene Scene
{
get
{
if (_scene is null)
{
// NOTE:
// ユーザー定義のノードには、スクリプトファイルのパス属性 ScriptPathAttribute が Godot により自動で付与されます
// 例)[ScriptPath("res://MyNode.cs")] class MyNode
// この属性を利用してノード定義ファイルを読み込みます
var scriptCodePath = typeof(TNode).GetCustomAttribute<ScriptPathAttribute>()?.Path;
if (scriptCodePath is null)
throw new InvalidOperationException($"ノード定義ファイルが見つかりません: {typeof(TNode).Name}");
_scene = GD.Load<PackedScene>(scriptCodePath.Substring(0, scriptCodePath.Length - 3) + ".tscn");
}
return _scene;
}
}
/// <summary>
/// 新しいノードインスタンスを作成<br/>
/// 対象のノード定義ファイル("*.tscn")が存在しない場合は例外
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
internal static TNode New() => Scene.Instantiate<TNode>();
}
使い方
partial class MyNode : Node;
MyNode node = NodeLoader<MyNode>.New();