Godot4.1.3でGodot State Charts 機能拡張プラグインを使う １／２

　Godot4用の機能拡張用プラグイン「Godot State Charts」をインストールすることで、状態遷移を簡単に実装することができるようになります。アプリを実装すると、変数とif文で管理するには複雑な状態遷移が必要になることがあるので、先行投資的な気持ちで使ってみました。

 https://github.com/derkork/godot-statecharts

こちらにマニュアルがあります。

https://github.com/derkork/godot-statecharts/blob/main/manual/manual.md

# Godot State Chartについて
　State ChartのGodot実装です。最初からGodot用に設計し、ノードやシグナルをフル活用しているため、Godotユーザは理解しやすいと思います。また状態遷移は並列化したり、階層化にも対応しているため、複雑な状態をスマートに管理・制御することができそうです。また組み込みのデバッグビューを使用すると実行中に状態や、遷移条件を確認することができるため、便利です。
 　ただし「状態遷移図」として表示できないため、あまり大規模な状態遷移をつくると全体を把握するのは難しそうですが、変数とif文やテーブルで管理するのに比べると、簡単で見やすくなります。

# サンプルプログラムについて
　Godot State Chartsの使い方は動画で紹介されています。説明を補足しながら実装していきます。

https://www.youtube.com/watch?v=E9h9VnbPGuw

# Version
 Godot4.1.3
 Godot State Charts 0.7.1　※

 ※まだ新しくてユーザも少ないため、バグや不足機能がある場合は教えてほしいとのことです

# Godot State Chartsのインストールして有効化する
 Godotプロジェクトを作成します。SampleStateChartsという名前にしました。
 画面上部の「AssetLib」をクリックし、検索窓に「state charts」と入力すると、「Godot State Charts」が表示されるので、クリックします。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/4a6bb6c0-23a2-a5d2-28f0-7bd61a634fc7.png)
「ダウンロード」ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/c7b181d1-8546-b71b-2f86-1f1e122ed952.png)
「インストール」ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/a1256916-72ad-9c22-2c80-d89a4324c571.png)
「成功」のダイアログが表示されてインストールが完了します。
ファイルシステムを見ると、assetsフォルダの下に、godot_state_chartsがインストールされました。
その下の「godot_state_charts_examples」はサンプルプログラムがあり、より深く理解したい場合に活用できそうです。githubに簡単な説明があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/74eb03e9-36e2-ba60-81b6-72402cc7f696.png)

インストール後はプラグインを有効にする必要があります。
「プロジェクト」メニューの「プロジェクト設定...」を実行します。
「プラグイン」タブをクリックすると、Godot State Chartsが表示されるので、ステータスを有効にします（チェックする）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/8426032f-2734-b53c-a020-d578ed3dbeee.png)

# 新規２Dシーンを作成して、見張り(Watchman)と敵(Enemy)を作成する
　StateChartのサンプルとして、見張り(Watchman)と敵(Enemy)を用意し、敵をマウスで動かして、見張りの監視範囲にいるかいないかをStateChartで管理します。
　簡単のため２Dで実装しています。

　見張り(Watchman)を作成します。
　新規2Dシーンを作成します。ルートノードはNode2Dです。
　子ノードにNode2Dを追加して、名前を「Watchman」にします。見張り役です。２Dエディターウインドウで中央に移動します。
  Watchmanの子ノードにSprite2Dを追加します。インスペクターのTextureにicon.svgをドラッグします。
  Watchmanの子ノードにArea2Dを追加し、さらに子ノードにCollisionShape2Dを追加します。インスペクタのShapeはCircleShape2Dにします。見張りの監視範囲なので下図のように広めに設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/76bf66ec-0807-ab09-5871-620f572e9061.png)

　次に敵を作成します。
　Watchmanノードを複製して、名前を「Enemy」に変更します。
　移動したいので、Enemyを選択し、インスペクターのTransform/positionを(x,y)=(100px,100px)くらいにします。（Watchmanと重なっていなければ、どこでもよいです）
　Enemy/Sprite2Dを選択して、Scaleを0.6に変更して、アイコン表示を少し小さくします。
　Enemyの衝突範囲を小さくしたいのですが、Watchmanを複製したため、Watchman/CollisionShape2DとEnemy/CollisionShape2Dが同じCircleShape2Dを共有しています。個別に衝突範囲を設定したいので、Enemy/CollisionShape2Dを選択した状態で、インスペクターのShapeのCircleShape2Dをクリックして、ユニーク化します。
 ![2023-11-04 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ed187f78-a712-ce06-03dc-412619f9d285.png)
　その後に、衝突範囲を小さくします。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/e10fc84f-b538-e228-1c09-71c706733d39.png)

　敵はマウスで操作するため、Enemyにスクリプトをアタッチして下記のようにします。
　マウスの位置をそのまま、Enemyの位置に設定しています。
 ```GDScript
extends Node2D

func _process(delta):
	global_position = get_global_mouse_position()
 ```

コリジョン形状を表示して、見た目で衝突がわかるようにします。
![2023-11-04 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/b73f1b26-b0a2-94fd-a790-0fa4e1a36e04.png)

実行します。
マウスの動きに合わせて敵（Enemy）が動くことを確認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/a00b07b1-3827-b2cf-5e0e-46ea68a6ca9a.png)

# WatchmanノードにState Chartを追加する
　Watchmanノードの子ノードとして、StateChart(state_Chart.gd)を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ce7d5589-57cf-ed45-9b6c-929f0371339e.png)

　追加したStateChartを選択した状態で、画面上部の２D(もしくは３D)をクリックすると、エディターウィンドウの左側に、下図のようなStateChart用のノード追加ボタンが表示されます。名前を入力して、ボタンを押下すると簡単にStateChart用のノードを追加できます。またシーンで選択したノードに追加可能なノードのボタンのみ表示されるので、間違えにくく便利です。
 （もしくは「子ノードを追加」を実行して、検索窓に「state」もしくは「transition」と入力することでStateChart用のノードを表示することができるので、選択して追加することもできます。）
 ![2023-11-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/e1f5d2bc-85a0-3770-0a00-ed404483a4eb.png)

# StateChartで使用するノード
　先ほど子ノードを追加から追加したStateChartノードに対してGDScriptからイベントやガード条件の変数を設定し、状態の変更などは子ノードからsignalの通知により知ることができます。
  StateChartの配下には、ルートノードとしてひとつだけ、CompoundState（もしくはParallelState）を追加することができます。
　CompoundStateは通常2個以上の状態としてAtomicStateを持ち、AtomicStateがTransitionノードを持つという構造になります。

| アイコン | 名前 | 用途 |
|:-:|:-|:-|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/51727a65-5705-aece-ed90-164d53375970.png)| StateChartノード  |このノードに対してGDScriptからイベントやガード条件の変数を設定できます   |
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ad971844-5771-b7be-9357-55402a30ae94.png)  |CompoundState | 通常は2個以上の状態（AtomicState）を持ちます。初期状態を設定します   |
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/61507e1a-2023-7e36-8e70-f979a96ea10b.png) | AtomicState  | 1個の状態を表します。Transitionを子として持ちます。  |
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/9c946fdd-e5b2-22ea-5eb5-0bd94f7940d3.png) | ParallelState  | 複数のCompoundStateを持つことが多いと思います。  |
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/42b1a2b6-12a6-a2a7-d28e-dec48aec47ae.png)   |HistoryState   | 状態を記憶します  |
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/fcca0ce3-854b-09d1-b374-8bd15d16f665.png)|Transition|イベントを受けて状態遷移を定義します。Delayやガード条件も設定することができます。|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/060d6c3c-90fa-2963-d228-06630ea6f88a.png)|AnimationTreeState|本稿で未使用なので割愛|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/fe25aae4-da87-dedd-908e-bedab5068a06.png)|AnimationPlayerState|本稿で未使用なので割愛|

# 敵が接近した場合、監視中状態にする
　先ほど追加したStateChartを選択した状態で、名前「Root」でCompoundStateを追加します。
　Rootの子に、名前「Idle」と名前「Observing」のAtomicStateを追加します。
　敵が発見できていない状態がIdleで、発見するとObserving状態に遷移します。
　Idle状態からObserving状態に遷移したいので、遷移元のIdleを選択して、名前「To Observe」のTransitionを追加します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/fee72865-dba1-7e95-7585-abb17e2dbe48.png)
　２つ警告マークが表示されているので、設定します。
 
　CompoundStateに初期状態を設定する必要があります。
　Rootを選択した状態で、インスペクターのCompoundStateのInitial StateにIdleを設定します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/bee2ee8d-24f6-0a36-ee39-1e2e4fef2c54.png)

　Transitionに状態遷移の遷移先とイベント名を設定します。
　「To Observing」Transitionノードを選択した状態でインスペクターのTransionのToに遷移先としたい「Observing」状態を設定します。Eventは「enemy_entered」と手入力します。
　この設定により、GDScriptからStateChartノードのsend_eventメソッドで"enemy_entered"イベントを送ると、Toで設定した遷移先「Observing」状態に遷移するようになります。
![2023-11-05 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/e4e165a4-7ea9-7283-d38d-83d23ea1b72c.png)

 　次に、WatchmanのArea2DでEnemyを検知した場合に、状態遷移するようにします。
　Watchmanにスクリプトをアタッチします。
　Watchman/Area2Dを選択し、ノード/シグナルから「area_entered」シグナルをダブルクリックして、Watchmanのスクリプトに接続します。
　Watchmanのスクリプトを下記のようにして、Area2Dでareaを検知した時はStateChartに"enemy_entered"イベントを送信するようにします。 
```GDScript
extends Node2D

func _on_area_2d_area_entered(area):
	$StateChart.send_event("enemy_entered")
```

# StateChartデバッガを表示して、実行中に状態を確認できるようにする
　ルートノードの「Node2D」を選択して、「Instantiate Child Scnene」を実行して、「addons/godot_state_charts/utilities/state_chart_debugger.tscn」を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/d3131f45-c50b-1e4a-7a1e-8dde80dda477.png)

　２Dのエディターウインドウの左上に小さく表示されるので、大きくして右の方に配置します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/056b79b6-9098-9b73-177e-9575e84270b9.png)
　またStateChartDebuggerのインスペクターの「Initial Node to Watch」にデバッグ対象のStateChartを選択します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/edb44719-8e12-3c56-8288-8632f7b0272e.png)

# 実行して状態遷移を確認する
　実行します。
　初期状態はIdleで表示されています。
 ![2023-11-05 (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/555a8771-6bfc-a338-13ab-0e3b14794937.png)
　マウスで敵を動かして、Watchmanの衝突領域に触れると、状態が「Observing」に遷移します。
 ![2023-11-05 (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/97d4c752-29c2-9849-202f-8764cb4bea87.png)


　状態遷移が動くのを確認できました。
　まだ続きがありますが、長くなったのでここまでにします。

# github
　ここまで作ったものをgithubに登録しましたが、State Chartは別途ダウンロードが必要です。

https://github.com/footinglow/Godot4/tree/main/03_study/SampleStateCharts

実行する前に必要な手順
1. Godotにインポート
2. AssetLibからState Chartをダウンロード
3. プロジェクト設定のプラグインタブでState Chartを有効化する

　以上です。