【Godot 4.0】スマホ３Dゲームを作るための勉強　その４　GDScriptで移動・回転する。現在の状態を見る

 ゲームエンジンgodot4.0で3Dスマホゲームを作りたいと思いますが、その前にお勉強しています。
 [Godot_v4.0-rc1_win64.exe.zip](https://downloads.tuxfamily.org/godotengine/4.0/rc1/Godot_v4.0-rc1_win64.exe.zip)を使用しています。

# 目的
　Godotは専用スクリプト言語GDScript、およびC#を公式にサポートしています。
　GDScriptはPythonライクなところもあつつ、Godotエンジン用に最適化されています。

　GDScriptでノードを移動・回転させます。

# 事前準備

・新規プロジェクトを作成
・3Dシーン押下して、ルートノートとしてNode3Dを追加します。
　名称をworldに変更します。
・worldの子ノードとして、DirectionalLight3Dを追加
　インスペクタのNode3D/positon/x=-4mに変更。Rotation/X=-45°に変更。
・worldの子ノードとして、Camera3Dを追加
　インスペクタのNode3D/positon/z=3mに変更
・worldの子ノードとして、MeshInstance3Dを追加
　インスペクタのMeshInstace3D/Meshに新規BoxMeshを選択
　名称をboxに変更します。

　原点にある立方体をカメラが真正面からとらえているので、実行すると、中央に四角形が表示されます。
![Qiita - Google Chrome 2023_02_22 11_26_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/3b503d22-e0bc-090e-13b2-90acb3aa6810.png)


# 移動する
　Godotの推奨スクリプト言語のGDScriptでboxを移動します。

　シーン内のboxを右クリックして、スクリプトをアタッチを選択します。
![スクリーンショット (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/fb9fa493-bb6b-9211-0f22-83c980c3414b.png)

ダイアログで作成ボタンを押下します。
![スクリーンショット (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ed571fea-8db2-6ad3-45b6-40affadd93b5.png)

作成するとシーン内のboxの右に巻物のようなアイコンが表示されて、スクリプトがアタッチされていることがわかります。クリックするとそのスクリプトが開きます。
![C__Users_hathr_OneDrive_Pictures_Screenshots 2023_02_22 11_36_53 - コピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/3aebb904-04a9-e605-ebbe-6e9077243d4d.png)

ファイルシステムには.gdという拡張子でスクリプトが保存されます。
![C__Users_hathr_OneDrive_Pictures_Screenshots 2023_02_22 11_36_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/10a81925-d951-8385-07c5-9ed2a1287db5.png)



 作成するときにテンプレートにNode:Defaultを選択したので、下記のようになります。
``` GDScript
extends MeshInstance3D

# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass
```
* extends MeshInstance3D
  アタッチしたノードのクラスを継承してMeshInstance3Dのプロパティ、メソッドにアクセスできるようになります。
　継承元のNode3Dにもアクセスできます。
* func _ready():
　初期化時に1回だけ実行されます。
* func _process(delta):
　実行中に何度も呼ばれるメソッドです。前回呼ばれた時からの経過時間がdeltaで通知されます。単位は秒です。


　スクリプトの_ready()の内容を下記のように修正して実行しましょう。
``` GDScript
extends MeshInstance3D

# Called when the node enters the scene tree for the first time.
func _ready():
	transform.origin.x = 2.0
	transform.origin.y = 1.0
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass
```
![C__Users_hathr_Videos_Captures 2023_02_22 11_39_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/561189b9-f160-24ff-204c-5eff7d717ea2.png)



 Node3Dを継承しているノード（子ノードを追加するダイアログで、Node3Dにぶら下がっているノード）はインスペクタ内に表示されている情報を設定/読み出しすることができます。
　インスペクタ内に表示されているNode3D/Transform/positionは、GDScriptではtransform.originでアクセスすることができます。
　（細かいことといえば、アタッチしているノードがNode3Dを継承しているノードで、GDScriptでextendsでNode3Dを継承するクラスを指定している場合です）

# 回転する
　GDScriptでboxを回転しましょう。
　X軸周りに手前に30°回転します。

　シーン内のboxノードの右側にある巻物アイコンをクリックするとGDScriptを開くことができます。
　下記のように変更しましょう。

``` GDScript
extends MeshInstance3D

# Called when the node enters the scene tree for the first time.
func _ready():
	rotate_x(deg_to_rad(30))

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass
```
* rotate_x(deg_to_rad(30))
 X軸周りに30度回転するように設定しました。
 X軸周りに回転する場合は、rotate_Xメソッドを使用します。引数はラジアンなので、deg_to_radメソッドで角度をラジアンに変換します。

![C__Users_hathr_Videos_Captures 2023_02_22 11_45_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/8606e2de-d197-c08b-2603-a144e359162e.png)

　X軸、y軸、z軸周りに回転する場合は、それぞれrotate_x、rotate_ｙ、rotate_z関数を使用します。

# 回転しつづける
　一定のスピードで回転するようにしましょう。
　先ほどは1回設定すればよかったので、初期化時に1回だけ起動される_readyメソッドの中で角度を設定しましたが、時間経過とともに変化させたいので、周期的に何度も呼ばれる_processメソッドの中で角度を変化するようにします。

``` GDScript
extends MeshInstance3D

var m_d_speed_degps = 30		# degree/sec

func _ready():
	pass

func _process(delta):
	rotate_x(deg_to_rad(m_d_speed_degps * delta))
```

* var m_d_speed_degps = 30		# degree/sec
 回転の速度をメンバ変数として定義しました。1秒間に30°回転するという意味です。12秒で360度1周する計算です。
　m_はメンバ変数、d_はdoubleです。あと変数名の接尾辞に単位をつけています。お好みで。
* rotate_x(deg_to_rad(m_d_speed_degps) * delta)
　1秒間に回転する量に、経過時間をかけています。
　例えば_process関数が0.1秒に1回呼ばれているとすると、delta=0.1になります。
　そうするとm_d_speed_degps * delta = 30° × 0.1 = 3°となり、1回_processが呼ばれるたびに3°回転することになります。10回呼ばれると1秒になって30°回転します。

　実行しましょう。
　12秒で1回転します。

# 回転速度をインスペクタで変更する
　回転速度のようなパラメータをインスペクタで設定できるようにします。
　先ほど追加した、メンバ変数の前に「@export」を追加します。

::: note
 Godot 4.0ではexportに@が必要です
:::

``` GDScript
extends MeshInstance3D

@export var m_d_speed_degps = 30		# degree/sec

func _ready():
	pass

func _process(delta):
	rotate_x(deg_to_rad(m_d_speed_degps * delta))
```

　スクリプトを保存して、シーンのboxを選択すると、インスペクタにm_d_speed_degpsが表示されます。（自動でキャメルケースになるようです）
![C__Users_hathr_Videos_Captures 2023_02_23 0_15_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/368f7bda-a2b0-2360-5b2a-d17a440ff4f5.png)

　インスペクタ内のM D Speed Degpsの30を300に変更して実行しましょう。先ほどと比べて回転が速くなります。
# 実行中に回転速度をインスペクタで変更する
　インスペクタの値は実行中に変更することができます。
　実行中は、左側のシーンにリモート、ローカルと表示されます。リモートをクリックすると実行中のシーンが表示されます。閉じているので開いて、boxを選択してください。

　リモートのboxを選択すると、右側のインスペクタの表示もリモートのノードの現在の情報が表示されます。
　M D Speed Degpsを更新（３０や３００など）すると回転速度がリアルタイムに変化します。
![スクリーンショット (13).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/6f22c21a-d679-06c7-5e48-009d2702b21b.png)

# 移動し続ける
　次に移動しつづけましょう。
　最初にtransform.origin.xというようにxやyを直接操作しましたが、いろいろな方向に指定した速度で動かしたい場合はあまり良い方法ではありません。Vector3を使用しましょう。
　Vector3はx,y,zをもつクラスで位置、向きを表すことができます。transform.originもVector3なので、そのまま足し算できます。

　メンバ変数で方向と速度を定義しました。

``` GDScript
extends MeshInstance3D
 
@export var m_v_dir = Vector3(1.0, 0.5, 0.0)
@export var m_d_speed_mps = 0.5

func _ready():
	pass

func _process(delta):
	transform.origin += m_v_dir.normalized() * m_d_speed_mps * delta 
```

* @export var m_v_dir = Vector3(1.0, 0.5, 0.0)
　方向を定義します。単位時間（一定時間）移動するx,y,zの割合として使用します。
　x=1.0m移動する間に、y=0.5m移動するという割合です。
　割合なので、x=10.0m,y=5.0mとしても、x=1000.0m,y=500.0mとしても意味、結果は同じになるように使用します。

* @export var m_d_speed_mps = 0.5
　速度を定義します。
　方向によらず、1秒間に0.5m移動します。
* m_v_dir.normalized()
　Vector3のメンバ関数normalized()は方向はそのままで長さを1mにします。
　例えば、x=1m,y=0m,z=0mの場合は長さが1mですが、x=1m,y=1m,z=0mの場合、長さが1.4mくらいになります。
　m_v_dir = Vector3(1.0, 0.5, 0.0)の長さは1.1180340051651mです。
　m_v_dir.normalized()とすると、(0.894427, 0.447214, 0)に変換されて、長さが1mになります。
* m_v_dir.normalized() * m_d_speed_mps
　速度を掛け算して、1秒間あたりに移動する距離(m)にします。
* m_v_dir.normalized() * m_d_speed_mps * delta
　deltaを掛け算して、前回からの経過時間分だけ移動します。 

# 実行中に位置をインスペクタで確認・変更する
　先ほどと同じようにシーンのリモートをクリックして、boxを選択し、インスペクタを見てみましょう。
　実行画面からは立方体がすでにいなくなっていると思いますが、インスペクタのNode3D/positionを見ると現在の位置を確認することができます。
　x,yの値がリアルタイムに更新されて移動中であることがわかります。
![スクリーンショット (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/f0e56e65-9e65-e2b2-8291-d542c2d82db5.png)
　positionのx,yを0にすると立方体が再び実行画面に戻ってきます。


以上