Godot EngineのC#開発を加速！GodotOnReadyで効率的なコーディング

# 目次
- [はじめに](#はじめに)
- [onreadyとは](#onreadyとは)
  - [C#ではどうするか](C#ではどうするか)
- [GodotOnReady](#GodotOnReady)
  - [導入方法](#導入方法)
  - [使用方法](#使用方法)
  - [内部動作](#内部動作)
- [さいごに](#さいごに)


# はじめに
はじめまして、ガルムと申します。
最近Unityでごたごたがあり、新しい技術に触れてみようと思いGodot Engineに立ち向かってみました。
今回はGodotEngineを使用するにあたり、GDScriptのいわゆる`@onready`に関してC#でも実現できないかと思い調査した結果を記述したいと思います。

# onready とは？
本題に移る前にGDScriptの`@onready`に関して簡単に説明します。
Godot Engineではノードという概念が集まったシーンツリーというものがアクティブになることでゲームが起動します。

ここで以下のようなコードをノード内に書いたとしましょう。
(ここでの$LabelはUnityのいわゆるGetComponent("Label")と思ってください。)

``` GDScript
var n = $Label
```

すると実行時エラーが出ます。
なぜなら、このクラスが初期化される時点ではまだLabelノードの実体がないためです。
シーンツリーは上から下に順に初期化されていくためです。

これ]]を解決するためにGDScriptには`@onready`が存在しています。
このキーワードをつけると、シーンツリー全体が初期化されたのちにLabelノードがnに代入されるようになります。

具体的には以下のように書きます。
``` GDScript
@onready var n = $Label
```

こう書くことにより正しく動作させることができます。

具体的な説明は以下のサイトを参考にしてください。
https://worktoolsmith.com/godot-onready-keyword/

## C#ではどうするか
Godot EngineではGDScript以外の言語を使用してゲームを開発することができます。
例えば、C#が挙げられますが、C#でノードを取得する場合には、以下のようにコードを書く必要があります。

``` C#
private Label _label;

public override void _Ready()
{
    _label = GetNode<Label>("Label");
}
```

一つだけなら良いですが、複数取得するとなってくるとかなり面倒です。

GDScriptの`@onready`に相当するものは提供されていないのでしょうか？
調べてみると、同様の意見があるらしくissueが上がっていることに気が付きました。

https://github.com/godotengine/godot-proposals/issues/2425

どうやらGodot Engineは提供していないらしいです。
その代わりにSource Generatorを使用した、GodotOnReadyというOSSが公開されているようです。

https://github.com/31/GodotOnReady

# GodotOnReady

実際に使用してみましょう

## 導入方法

NuGet packageに[GodotOnReady](https://www.nuget.org/packages/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]`アトリビュートをつけるだけです。

例
``` C#
[OnReadyGet] private Label _label;
```

これは、以下と同じ動作をします。

```C#
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というファイルが生成されているようです。
例
``` C# 
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()`を使用することはできません。
メインのスクリプトでは以下のコードを使用することはできないのです。
``` C#
public override void _Ready() 
```

そこで `[OnReady]` というアトリビュートが存在します。
詳しくは本家を参照してみて下さい。

## さいごに
Godot EngineをC＃使うにあたってGodotScriptのいわゆる`@onready`にあたるものがないかと調べてみました。
結果的にC#のOSSにあたる`GodotOnReady`というもの発見しました。
よかったら使ってみてください。