はじめに
今回は以前記事にしたカメラ映像のネタの続きに関する実装方法やその他ちょっとだけプロセスに関する機能を紹介しようと思う。
画面に関するTips
複数のカメラ映像を重ねた後の画面サイズの調整について
以前、GodotでUnityのカメラと同じく優先度をつけて映像を重ねたい場合のテクニックを紹介した。
このテクニックの最後でカメラ映像がウィンドウサイズと合わずに隙間ができることに触れた。
本来のデフォルトのビューポートとカメラ映像だと、ウィンドウサイズを変えるとちゃんとカメラ映像とビューポートのサイズは追随する。
さすがにこのような表示のままではゲームやアプリとして活用しづらいかもしれない。
そこで、SubViewportContainerとSubViewportを使った場合でもちゃんとウィンドウサイズに追随するようにしてみた。
手順
たとえば次のように、SubViewportとCamer3Dを複数設置していたとする。
- ルートウィンドウのサイズ変更イベントを用意する
- _ready処理の中でイベントを接続する
適当なノードにスクリプトをアタッチし、ルートつまりWindowに対して size_changed
のイベントを接続する。
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の映像もきちんとサイズが追随して変更される。
複数ビューポートを使っている場合のスクリーンショットの取得方法
ゲーム画面の現在の映像をスクリーンショットに撮るのは、公式マニュアル にもあるように、次の一文でよい。
get_viewport().get_texture().get_image().save_jpg("user://testscreenshot00.jpg")
ただ、SubViewportContainerとSubViewportを使っていたり、任意の解像度でスクリーンショットを取得したい場合はこれだけでは足りない。
次のようにする。
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側に渡してやれば保存できると思われる。
公式マニュアルはこちら。
-
ViewportTexture
※実際に使用するメソッドの大半はTexture2Dにある。 - Texture2D
- Image
整理するとこうだ。
カメラ→テクスチャ→画像として保存
ではなく、
ビューポート(with カメラ)→テクスチャ→画像として保存
ということになる。
複数のSubViewportを使っていると、個々のSubViewportをそれぞれ別目的で活用しやすい。
テクスチャをこのようにスクリーンショットとして保存できたり、 Unityでいうところのレンダーテクスチャ的に他のノードへと連携して活用したりとできる。
その他Tips
ノードを完全に無効化する
ノードをそもそも動かさないようにするためには、process_mode
を変更する。
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
公式マニュアルはこちら。
enum ProcessMode
Unityでいうところの enabled やSetActiveに相当すると思われる。
終わりに
以上、画面に関する操作と、プロセスに関するTipsを紹介した。
Unityだとこうすればよかったけど、Godotではどうするの?というTipsを今後も発見や実践したら紹介していきたい。
今回のネタも、Godotだとビューポートの使い方さえ覚えておけばすぐできる(少なくともSubViewportを使わなければたった一文でできる)ので、個人的にはかなりわかりやすくて使いやすい。