目次
はじめに
はじめまして、ガルムと申します。
最近Unityでごたごたがあり、新しい技術に触れてみようと思いGodot Engineに立ち向かってみました。
今回はGodotEngineを使用するにあたり、GDScriptのいわゆる@onready
に関してC#でも実現できないかと思い調査した結果を記述したいと思います。
onready とは?
本題に移る前にGDScriptの@onready
に関して簡単に説明します。
Godot Engineではノードという概念が集まったシーンツリーというものがアクティブになることでゲームが起動します。
ここで以下のようなコードをノード内に書いたとしましょう。
(ここでの$LabelはUnityのいわゆるGetComponent("Label")と思ってください。)
var n = $Label
すると実行時エラーが出ます。
なぜなら、このクラスが初期化される時点ではまだLabelノードの実体がないためです。
シーンツリーは上から下に順に初期化されていくためです。
これ]]を解決するためにGDScriptには@onready
が存在しています。
このキーワードをつけると、シーンツリー全体が初期化されたのちにLabelノードがnに代入されるようになります。
具体的には以下のように書きます。
@onready var n = $Label
こう書くことにより正しく動作させることができます。
具体的な説明は以下のサイトを参考にしてください。
https://worktoolsmith.com/godot-onready-keyword/
C#ではどうするか
Godot EngineではGDScript以外の言語を使用してゲームを開発することができます。
例えば、C#が挙げられますが、C#でノードを取得する場合には、以下のようにコードを書く必要があります。
private Label _label;
public override void _Ready()
{
_label = GetNode<Label>("Label");
}
一つだけなら良いですが、複数取得するとなってくるとかなり面倒です。
GDScriptの@onready
に相当するものは提供されていないのでしょうか?
調べてみると、同様の意見があるらしくissueが上がっていることに気が付きました。
どうやらGodot Engineは提供していないらしいです。
その代わりにSource Generatorを使用した、GodotOnReadyというOSSが公開されているようです。
GodotOnReady
実際に使用してみましょう
導入方法
NuGet packageにGodotOnReadyを追加することで使用できます。
.csprojが以下のようになっていれば成功です。
<Project Sdk="Godot.NET.Sdk/3.3.0">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GodotOnReady" Version="1.2.4" />
</ItemGroup>
</Project>
使用方法
メンバ変数に[OnReadyGet]
アトリビュートをつけるだけです。
例
[OnReadyGet] private Label _label;
これは、以下と同じ動作をします。
private Label _label;
public override void _Ready()
{
_label = GetNode<Label>("Label");
}
また、引数に"My/Button/Somewhere"
といった文字列を渡すことで子以下のノードから取得することができます。
他にも[OnReady]
といったアトリビュートがあります。
詳細は本家を読んでみてください。
内部動作
使用方法のみを説明してもつまらないので、内部でどのように動作しているかも見ていきたいと思います。
このOSSでは、Source Generator
が使用されています。
Source Generator
についての詳細は割愛しますが、いいかんじにコードを生成してくれるものだと思ってください。
Godot EngineでもC#を使用するときは内部的にSource Generatorが使われています。
興味がある方は調べてみてください。
さて、今回のOSSではどのようなコードが自動生成されるのでしょうか?
調べてみると、Parctial_XXX.csというファイルが生成されているようです。
例
using Godot;
using System;
partial class Player
{
[Export] public NodePath SpritePath { get; set; }
public override void _Ready()
{
base._Ready();
if (SpritePath != null)
{
_sprite = GetNode<global::Godot.Sprite2D>(SpritePath);
}
if (_sprite == null)
{
throw new NullReferenceException($"'{((Resource)GetScript()).ResourcePath}' member '_sprite' is unexpectedly null in '{GetPath()}', '{this}'. Ensure 'SpritePath' is set correctly, or set OrNull = true in the attribute to allow null.");
}
}
}
/*
---- END OF GENERATED SOURCE TEXT ----
Roslyn doesn't clear the file when writing debug output for
EmitCompilerGeneratedFiles, so I'm writing this message to
make it more obvious what's going on when that happens.
*/
Godot Engineが提供している、_Ready()
を使用してNodeを取得しているようです。
ただ、これだと、本来の_Ready()
を使用することはできません。
メインのスクリプトでは以下のコードを使用することはできないのです。
public override void _Ready()
そこで [OnReady]
というアトリビュートが存在します。
詳しくは本家を参照してみて下さい。
さいごに
Godot EngineをC#使うにあたってGodotScriptのいわゆる@onready
にあたるものがないかと調べてみました。
結果的にC#のOSSにあたるGodotOnReady
というもの発見しました。
よかったら使ってみてください。