GodotEngineで画面やプロセスに関するいくつかのTips

# はじめに

今回は以前記事にしたカメラ映像のネタの続きに関する実装方法やその他ちょっとだけプロセスに関する機能を紹介しようと思う。


# 画面に関するTips

## 複数のカメラ映像を重ねた後の画面サイズの調整について

以前、GodotでUnityのカメラと同じく優先度をつけて映像を重ねたい場合のテクニックを紹介した。

https://qiita.com/lumis/items/4fcccc4e35a96922b6e8


このテクニックの最後でカメラ映像がウィンドウサイズと合わずに隙間ができることに触れた。
本来のデフォルトのビューポートとカメラ映像だと、ウィンドウサイズを変えるとちゃんとカメラ映像とビューポートのサイズは追随する。

さすがにこのような表示のままではゲームやアプリとして活用しづらいかもしれない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38416/7022f079-04aa-074d-aaff-7232e974e743.png)

そこで、SubViewportContainerとSubViewportを使った場合でもちゃんとウィンドウサイズに追随するようにしてみた。

**手順**

たとえば次のように、SubViewportとCamer3Dを複数設置していたとする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38416/1c525751-b0ce-1783-c73b-96e9b6c09337.png)


1. ルートウィンドウのサイズ変更イベントを用意する
2. _ready処理の中でイベントを接続する
 
適当なノードにスクリプトをアタッチし、ルートつまりWindowに対して `size_changed` のイベントを接続する。

```gdscript:test.gd
func _ready():
    #---root(Windowsのインスタンス)の size_changedシグナルにリサイズイベントを接続する
	get_tree().root.connect("size_changed", _on_window_resize)

## リサイズイベント
func _on_window_resize():
    #---デフォルトのビューポートのサイズを取得
	#var viewport_size = get_viewport().get_visible_rect().size
    var winsize = get_tree().root.size
	print(viewport_size, winsize)
	
	#---UIという名前のノードだとする
    #   念の為SubViewportContainerの親のControlノードのサイズも変更しておく
	self.size = Vector2(winsize.x, winsize.y)

	#---SubViewportContainer
    var maincamera_container: SubViewportContainer = get_node("MainCameraContainer")
    maincamera_container.size = Vector2(winsize.x, winsize.y)
    #---各SubViewport
	(maincamera_container.get_node("FrontMainViewport") as SubViewport).size = Vector2(winsize.x, winsize.y)
	(maincamera_container.get_node("IKViewport") as SubViewport).size = Vector2(winsize.x, winsize.y)
 
```

ポイントは、ルート(Window)のビューポートのサイズ `size` を取得することだ。
他にも `get_visible_rect()`でも取得できるのだが、こちらは描画されているウィンドウつまり四角形の大きさを取得するため厳密には画面解像度を含めたサイズではない。こちらを使うとSubViewportの映像が想定通りの解像度にならないことがあったので、使うなら `size` だ。

この `size` を、SubViewportContainer自体と、子のSubViewport達の `size` プロパティにも代入してあげる。
これでウィンドウのサイズを変更したときでも各SubViewportの映像もきちんとサイズが追随して変更される。

![無題の動画.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38416/c8233053-b7ff-f05d-4660-f2dc674cdd8a.gif)


## 複数ビューポートを使っている場合のスクリーンショットの取得方法

ゲーム画面の現在の映像をスクリーンショットに撮るのは、[公式マニュアル](https://docs.godotengine.org/ja/4.x/tutorials/rendering/viewports.html#capture) にもあるように、次の一文でよい。

```gdscript
get_viewport().get_texture().get_image().save_jpg("user://testscreenshot00.jpg")
```

ただ、SubViewportContainerとSubViewportを使っていたり、任意の解像度でスクリーンショットを取得したい場合はこれだけでは足りない。

次のようにする。

```gdscript
func capture_screen():
    var FULLRESOLUTION = Vector2i(1920,1080)
	#---通常のスクリーンショットの撮り方
	get_viewport().get_texture().get_image().save_jpg("user://testscreenshot00.jpg")

	#---サブビューポートが複数あり、それぞれのスクリーンショットの撮り方
    #一時的に解像度も変更する（サブビューポート単位で可）
	(maincamera_container.get_node("FrontMainViewport") as SubViewport).size = FULLRESOLUTION
	(maincamera_container.get_node("IKViewport") as SubViewport).size = FULLRESOLUTION
    #---画面解像度変更を待たないといけない
	await get_tree().create_timer(0.1).timeout
    #---特定のSubViewportのスクリーンショットを取得して保存
	(maincamera_container.get_node("FrontMainViewport") as SubViewport).get_texture().get_image().save_jpg("user://testscreenshot01.jpg")
	(maincamera_container.get_node("IKViewport") as SubViewport).get_texture().get_image().save_jpg("user://testscreenshot02.jpg")
	print_debug("save screenshot!")
    #---念の為待つ
	await get_tree().create_timer(0.1).timeout
    #---解像度を戻す（rootのサイズを代入）
	(maincamera_container.get_node("FrontMainViewport") as SubViewport).size = get_tree().root.size

```

Godotでは、Viewport/SubViewportが画面操作の起点となる。現在の映像をテクスチャ(ViewportTexture)として取得するメソッドが `get_texture()` として備わっているので非常に扱いやすい。

サブビューポートをそのまま参照してもよいが、ゲームやツールの機能として任意の解像度でスクリーンショットを取得したい場合もあるだろう。
そういうときは、取得対象の映像が出力されるサブビューポートのサイズを変更してあげればよい。
ただし、すぐ画像として保存することはできないので、 `create_timer` でわずかにウェイトを入れよう。

サブビューポートから取得したViewportTextureから、Imageを取得する。
そのImageの `save_jpg` メソッドを呼び出せば、ローカルディスクに保存できる。

また、Webビルドなどでスクリーンショットを保存したい場合には、 `save_jpg_to_buffer` メソッドを使ってバイト配列をjavascript側に渡してやれば保存できると思われる。

:::note info
公式マニュアルはこちら。
* [ViewportTexture](https://docs.godotengine.org/ja/4.x/classes/class_viewporttexture.html#class-viewporttexture)
※実際に使用するメソッドの大半はTexture2Dにある。
* [Texture2D](https://docs.godotengine.org/ja/4.x/classes/class_texture2d.html#class-texture2d)
* [Image](https://docs.godotengine.org/ja/4.x/classes/class_image.html#class-image)
:::

整理するとこうだ。
カメラ→テクスチャ→画像として保存
ではなく、
ビューポート(with カメラ)→テクスチャ→画像として保存
ということになる。

複数のSubViewportを使っていると、個々のSubViewportをそれぞれ別目的で活用しやすい。
テクスチャをこのようにスクリーンショットとして保存できたり、 Unityでいうところのレンダーテクスチャ的に他のノードへと連携して活用したりとできる。


# その他Tips

## ノードを完全に無効化する

ノードをそもそも動かさないようにするためには、`process_mode` を変更する。

```gdscript
var charabody3d = get_node("CharacterBody3D") #名前が型と同じ例
#---無効化（一切動かない）
charabody3d.process_mode = Node.PROCESS_MODE_DISABLED
#---有効化
charabody3d.process_mode = Node.PROCESS_MODE_INHERIT
#---親と関係なく常に有効化であれば下記でも良い。
charabody3d.process_mode = Node.PROCESS_MODE_ALWAYS
```

:::note info
公式マニュアルはこちら。
[enum ProcessMode](https://docs.godotengine.org/ja/4.x/classes/class_node.html#enum-node-processmode)
:::

:::note info
Unityでいうところの enabled やSetActiveに相当すると思われる。
:::


 # 終わりに

 以上、画面に関する操作と、プロセスに関するTipsを紹介した。
 
 Unityだとこうすればよかったけど、Godotではどうするの？というTipsを今後も発見や実践したら紹介していきたい。
 
 今回のネタも、Godotだとビューポートの使い方さえ覚えておけばすぐできる（少なくともSubViewportを使わなければたった一文でできる）ので、個人的にはかなりわかりやすくて使いやすい。