Godotでモバイル向けの画面サイズ取得
要点
Screen座標系
DisplayServer から取得した値は端末に依存するScreen座標系(物理ピクセル)である。
- DisplayServer.screen_get_size()で実際のスクリーンの解像度を取得できる
- DisplayServer.screen_get_scale()で端末の画面スケールを取得できる
Viewport座標系
viewport から取得した値は Project Settings の Viewport Width と Viewport Height や Stretch > Aspect の設定に依存するViewport座標系(論理ピクセル)である。
- get_viewport_rect().sizeでは、- Stretch>- Aspectに基づいて引き伸ばし等を行った後の描画可能なサイズを返す。
- get_viewport().content_scale_sizeでは- Viewport Widthと- Viewport Heightに設定した値をそのまま返す。
- get_viewport().get_visible_rect().sizeでは、- Stretch>- Aspectに基づいて引き伸ばし等を行った後の描画可能なサイズを返す。- get_viewport_rect().sizeとの違いは調査したがわからず。- Controlから使える。
 
各座標系
画面タップ時
Viewport座標系 (論理ピクセル)
func _input(event):
    # 入力がタッチかどうかを確認
    if event is InputEventScreenTouch:
        print("タップ!:", event.position)出力
# iPhone 13 mini 
# 左上ギリギリを狙う
タップ!:(13.44, 38.87069)
# 右上ギリギリを狙う
タップ!:(516.96, 30.23276)
# 右下ギリギリを狙う
タップ!:(504.0, 1140.207)
# 左下ギリギリを狙う
タップ!:(36.48, 1138.767)- 角がまるいので (0,0) も (540, 1169) も狙えないが、 取得できる positionにはscreen_scaleは関係がない。
- get_viewport().content_scale_sizeで取得できる範囲である。
- event.position.x * get_viewport().get_screen_transform().x.xという書き方をGodotでモバイル向けにジョイスティックを実装するで見かけたが、CanvasLayer の物に対しては必要がなさそう。(単に- event_position.xを使えばおk)
画面タップできる範囲は get_viewport().content_scale_size である。
セーフエリア
スクリーン座標系 (物理ピクセル)
var screen_size := DisplayServer.screen_get_size()
var safe_area := DisplayServer.get_display_safe_area()
var screen_scale := DisplayServer.screen_get_scale()
var viewport_size := get_viewport().get_visible_rect().size
var scale_x := screen_size.x / viewport_size.x
var scale_y := screen_size.y / viewport_size.y
# スクリーン座標系 -> Viewport座標系へ
var safe_area_in_ui := Rect2(
    Vector2(safe_area.position.x / scale_x, safe_area.position.y / scale_y),
    Vector2(safe_area.size.x / scale_x, safe_area.size.y / scale_y)
)詳しくは Godotでモバイル向けのセーフエリア対応 にて。
キーボードサイズ
スクリーン座標系 (物理ピクセル)
var screen_scale = DisplayServer.screen_get_scale()
var keyboard_height = DisplayServer.virtual_keyboard_get_height()
# iOSネイティブで取得できる値
var keyboard_height_in_ui = keyboard_height / screen_scale
# Godot で実際に使う値
# Viewport座標系とスクリーン座標系のスケールを計算
var screen_size: Vector2 = DisplayServer.screen_get_size()
var viewport_size: Vector2 = get_viewport().get_visible_rect().size
var scale_y: float = screen_size.y / viewport_size.y
var keyboard_height_in_viewport: float = keyboard_height / scale_y
print("screen_scale: ", screen_scale)
print("keyboard_height: ", keyboard_height)
print("keyboard_height_in_ui: ", keyboard_height_in_ui)
print("keyboard_height_in_viewport:",  )出力
# iPhone 13 mini
screen_scale:                3.0
keyboard_height:             1008
keyboard_height_in_ui:       336.0    // この値は iOS と一致する
keyboard_height_in_viewport: 483keyboard_height にはセーフエリアのbottom_insets も含まれているため、キーボードの上端のy座標を取得したい場合には、単にviewport.size.y - keyboard_height で取得できる。
詳しくはGodotでモバイル端末のキーボードの高さの取得する にて。
キーボード関連ノート
- GodotでiOSのキーボードに追従するツールバーを実装する
- Godot-iOSキーボードの高さの変化を通知するネイティブプラグインを作る
調査
画面サイズ取得の各関数
print("DisplayServer.screen_get_size(): ", DisplayServer.screen_get_size())
print("get_viewport_rect().size: ", get_viewport_rect().size)
print("get_viewport().content_scale_size: ", get_viewport().content_scale_size)
print("get_viewport().get_visible_rect().size: ", get_viewport().get_visible_rect().size)
print("DisplayServer.screen_get_scale(): ", DisplayServer.screen_get_scale())
var mode = ProjectSettings.get_setting("display/window/stretch/mode")
var aspect = ProjectSettings.get_setting("display/window/stretch/aspect")
print("Stretch mode: ", mode)
print("Aspect mode: ", aspect)調査1
- Stretch: canvas_items(- viewportでも同様な結果)
- Aspect: keep_width
- Viewport Width: 10- Viewport Height: 960
# Mac上
DisplayServer.screen_get_size():         (3024, 1964)
get_viewport_rect().size:                (10.0, 960.0)
get_viewport().content_scale_size:       (10, 960)
get_viewport().get_visible_rect().size:  (10.0, 960.0)
DisplayServer.screen_get_scale():        2.0
Stretch mode:                            canvas_items
Aspect mode:                             keep_width
# iPhone 13 mini 実機
DisplayServer.screen_get_size():         (1125, 2436)
get_viewport_rect().size:                (10.0, 960.0)
get_viewport().content_scale_size:       (10, 960)
get_viewport().get_visible_rect().size:  (10.0, 960.0)
DisplayServer.screen_get_scale():        3.0
Stretch mode:                            canvas_items
Aspect mode:                             keep_width調査2
- Stretch: canvas_items(- viewportでも同様な結果)
- Aspect: expand
- Viewport Width: 10- Viewport Height: 960
# Mac
DisplayServer.screen_get_size():         (3024, 1964)
get_viewport_rect().size:                (64.0, 960.0)
get_viewport().content_scale_size:       (10, 960)
get_viewport().get_visible_rect().size:  (64.0, 960.0)
DisplayServer.screen_get_scale():        2.0
Stretch mode:                            canvas_items
Aspect mode:                             expand
# iPhone 13 mini 実機
DisplayServer.screen_get_size():         (1125, 2436)
get_viewport_rect().size:                (443.0, 960.0)
get_viewport().content_scale_size:       (10, 960)
get_viewport().get_visible_rect().size:  (443.0, 960.0)
DisplayServer.screen_get_scale():        3.0
Stretch mode:                            canvas_items
Aspect mode:                             expand調査3
- Stretch: canvas_items(- viewportでも同様な結果)
- Aspect: keep_height
- Viewport Width: 10- Viewport Height: 960
# Mac
DisplayServer.screen_get_size():         (3024, 1964)
get_viewport_rect().size:                (64.0, 960.0)
get_viewport().content_scale_size:       (10, 960)
get_viewport().get_visible_rect().size:  (64.0, 960.0)
DisplayServer.screen_get_scale():        2.0
Stretch mode:                            canvas_items
Aspect mode:                             keep_height
# iPhone 13 mini 実機
DisplayServer.screen_get_size():         (1125, 2436)
get_viewport_rect().size:                (443.0, 960.0)
get_viewport().content_scale_size:       (10, 960)
get_viewport().get_visible_rect().size:  (443.0, 960.0)
DisplayServer.screen_get_scale():        3.0
Stretch mode:                            canvas_items
Aspect mode:                             keep_height- DisplayServer.screen_get_size()はゲームを表示しているスクリーンの解像度
- get_viewport().content_scale_sizeは Project Settings で設定した値
- get_viewport_rect().sizeは、Project Settings で- Stretch>- Aspectに基づいて計算された描画可能なサイズ
- get_viewport().get_visible_rect().sizeは- get_viewport_rect().sizeと同様- 違いを探す
 
get_viewport().get_visible_rect().size と get_viewport_rect().size の違い
- 
- Returns the viewport's boundaries as a Rect2.
 
- get_viewport().get_visible_rect().size - Returns the visible rectangle in global screen coordinates.
 
- SubViewportなどを使って実験してみたけれど、異なる値を返すパターンに遭遇しない...
違いがわからんけど、SubViewport を使うと異なる Viewport を作ることができることがわかった。このとき、Subviewport を (300, 300) に設定すると、これらの関数も (300, 300) を返すようになる。
わかりません!!!!
調査まとめ
モバイルのような全画面で端末のセーフエリアなどを気にする場合は DisplayServer.screen_get_size() と DisplayServer.screen_get_scale() を用いて計算するのが良さそう。
反対にWebなどでゲームを埋め込んだ場合には DisplayServer.screen_get_size() は使うと危ないかもしれない。
ドキュメント
関連ノート
- Godotでモバイルゲームを作るメモ(Udemy)セクション2
- Godotでモバイル向けの環境設定をする
- Godotでモバイル向けのセーフエリア対応
- GodotでiOSのキーボードに追従するツールバーを実装する
- 物理ピクセル(physical pixel)
- 論理ピクセル(logical pixel)
