最終確認日
Godotでモバイル向けのセーフエリア対応
要点
DisplayServer.get_display_safe_area()
を使うviewport
に基づいて画面ピクセル ÷ viewport サイズ
でスケーリングする。DisplayServer.screen_get_scale()
は使わない
var screen_size := DisplayServer.screen_get_size()
var safe_area := DisplayServer.get_display_safe_area()
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
var screen_size_in_ui := Vector2(
screen_size.x / scale_x,
screen_size.y / scale_y
)
# 実ピクセル -> UI座標へ
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)
)
# セーフエリア上部の余白
var safe_area_top_inset := safe_area_in_ui.position.y
# セーフエリア下部の余白
var safe_area_bottom_inset := screen_size_in_ui.y - safe_area_in_ui.end.y
# セーフエリア左部分の余白
var safe_area_left_inset := safe_area_in_ui.position.x
# セーフエリア右部分の余白
var safe_area_right_inset := screen_size_in_ui.x - safe_area_in_ui.end.x
画面サイズの取得方法についてはGodotでモバイル向けの画面サイズ取得にて。
調査
Swiftの場合
まずは目標の値を知る。iPhone 13 mini 実機で、Swiftで調査する。
import SwiftUI
struct ContentView: View {
private var safeAreaInsets: UIEdgeInsets {
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
return windowScene?.windows.first?.safeAreaInsets ?? .zero
}
var body: some View {
ZStack {
GeometryReader { geometry in
Color.yellow
VStack {
Text("画面サイズ")
Text("幅:\(UIScreen.main.bounds.width) 高さ:\(UIScreen.main.bounds.height)")
Text("SafeArea")
Text("上:\(safeAreaInsets.top)")
Text("下:\(safeAreaInsets.bottom)")
Text("左:\(safeAreaInsets.left)")
Text("右:\(safeAreaInsets.right)")
Text("SafeAresサイズ(黄色部分)")
Text("幅:\(geometry.size.width) 高さ:\(geometry.size.height)")
}
}
}
.background(Color.blue)
}
}
portrait (縦持ち) | landscape (横持ち) |
---|---|
![]() |
![]() |
portrait (縦持ち)の場合 |
- 画面サイズは
(width: 375, height: 812)
- SafeArea insets は
(top: 50, left: 0, bottom: 34, right: 0)
- SafeArea size は
(width: 375, height: 728)
landscape (横持ち)の場合
- 画面サイズは
(width: 812, height: 375)
- SafeArea insets は
(top: 0, left: 50, bottom: 21, right: 50)
- SafeArea size は
(width: 712, height: 354)
Godotで確認してみる
目標値はSwiftで確認した値。
func _ready():
var screen_size = DisplayServer.screen_get_size()
var safe_area = DisplayServer.get_display_safe_area()
var screen_scale = DisplayServer.screen_get_scale()
var screen_size_in_ui = screen_size / screen_scale
var safe_area_in_ui = Rect2(
safe_area.position / screen_scale,
safe_area.size / screen_scale
)
# セーフエリア上部の余白
var safe_area_top_inset = safe_area_in_ui.position.y
# セーフエリア左部分の余白
var safe_area_left_inset = safe_area_in_ui.position.x
# セーフエリア下部の余白
var safe_area_bottom_inset = screen_size_in_ui.y - safe_area_in_ui.end.y
# セーフエリア右部分の余白
var safe_area_right_inset = screen_size_in_ui.x - safe_area_in_ui.end.x
print("Screen Size: ", screen_size)
print("Screen Size(ui): ", screen_size_in_ui)
print("Screen Scale: ", screen_scale)
print("Safe Area position(display): ", safe_area.position)
print("Safe Area size(display): ", safe_area.size)
print("Safe Area position(ui): ", safe_area_in_ui.position)
print("Safe Area size(ui): ", safe_area_in_ui.size)
print("Safe Area end: ", safe_area_in_ui.end)
print("Safe Area top inset: ", safe_area_top_inset)
print("Safe Area left inset: ", safe_area_left_inset)
print("Safe Area bottom inset: ", safe_area_bottom_inset)
print("Safe Area right inset: ", safe_area_right_inset)
出力
Screen Size: (1125, 2436)
Screen Size(ui): (375.0, 812.0)
Screen Scale: 3.0
Safe Area position(display): (0, 150)
Safe Area size(display): (1125, 2184)
Safe Area position(ui): (0.0, 50.0)
Safe Area size(ui): (375.0, 728.0)
Safe Area end: (375.0, 778.0)
Safe Area top inset: 50.0
Safe Area left inset: 0.0
Safe Area bottom inset: 34.0
Safe Area right inset: 0.0
一致することがわかる。
ただし、この値をそのままUIに反映させてみるとうまく表示されないことがわかる。
@onready var screen_size_rect = $ScreenSize
@onready var safe_area_rect = $SafeArea
func _ready():
var screen_size = DisplayServer.screen_get_size()
var safe_area = DisplayServer.get_display_safe_area()
var screen_scale = DisplayServer.screen_get_scale()
var screen_size_in_ui = screen_size / screen_scale
var safe_area_in_ui = Rect2(
safe_area.position / screen_scale,
safe_area.size / screen_scale
)
screen_size_rect.position = Vector2.ZERO
screen_size_rect.size = screen_size_in_ui
safe_area_rect.position = safe_area_in_ui.position
safe_area_rect.size = safe_area_in_ui.size
viewport に合わせて再スケールする
DisplayServer.screen_get_scale()
を使わずに、screen_size.x / viewport_size.x
のように、画面ピクセル ÷ viewport サイズ
にすることで、正しい位置に配置することができる。
extends Control
@onready var screen_size_rect = $ScreenSize
@onready var safe_area_rect = $SafeArea
func _ready():
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
var screen_size_in_ui := Vector2(
screen_size.x / scale_x,
screen_size.y / scale_y
)
# 実ピクセル -> UI座標へ
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)
)
# セーフエリア上部の余白
var safe_area_top_inset = safe_area_in_ui.position.y
# セーフエリア下部の余白
var safe_area_bottom_inset = screen_size_in_ui.y - safe_area_in_ui.end.y
# セーフエリア左部分の余白
var safe_area_left_inset = safe_area_in_ui.position.x
# セーフエリア右部分の余白
var safe_area_right_inset = screen_size_in_ui.x - safe_area_in_ui.end.x
# UIに反映
screen_size_rect.position = Vector2.ZERO
screen_size_rect.size = screen_size_in_ui
safe_area_rect.position = safe_area_in_ui.position
safe_area_rect.size = safe_area_in_ui.size
print("Screen Size: ", screen_size)
print("Screen Size(ui): ", screen_size_in_ui)
print("Screen Scale: ", screen_scale)
print("Scale x: ", scale_x)
print("Scale y: ", scale_y)
print("Safe Area position(display): ", safe_area.position)
print("Safe Area size(display): ", safe_area.size)
print("Safe Area position(ui): ", safe_area_in_ui.position)
print("Safe Area size(ui): ", safe_area_in_ui.size)
print("Safe Area end: ", safe_area_in_ui.end)
print("Safe Area top inset: ", safe_area_top_inset)
print("Safe Area bottom inset: ", safe_area_bottom_inset)
print("Safe Area left inset: ", safe_area_left_inset)
print("Safe Area right inset: ", safe_area_right_inset)
これで実行すると、正しい位置とサイズで取得ができていることがわかる。
値はネイティブで実行した場合と異なる点に注意。
Screen Size: (1125, 2436)
Screen Size(ui): (540.0, 1169.0)
Screen Scale: 3.0
Scale x: 2.08333333333333
Scale y: 2.08383233532934
Safe Area position(display): (0, 150)
Safe Area size(display): (1125, 2184)
Safe Area position(ui): (0.0, 71.98276)
Safe Area size(ui): (540.0, 1048.069)
Safe Area end: (540.0, 1120.052)
Safe Area top inset: 71.9827575683594
Safe Area bottom inset: 48.9482421875
Safe Area left inset: 0.0
Safe Area right inset: 0.0
Autoloadで使いやすくする
Control
ノードを作る
Autoloadで SafeAreaGuide
を登録する
スクリプトを書く
safe_area_guide.gd
extends Control
var safe_area_insets: SafeAreaInsets
func _ready():
if not (OS.get_name() == "iOS" or OS.get_name() == "Android"):
# モバイル端末でのみセーフエリアを考慮する.
safe_area_insets = SafeAreaInsets.new(0, 0, 0, 0)
return
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
var screen_size_in_ui := Vector2(
screen_size.x / scale_x,
screen_size.y / scale_y
)
# 実ピクセル -> UI座標へ
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)
)
# セーフエリア上部の余白
var safe_area_top_inset := safe_area_in_ui.position.y
# セーフエリア下部の余白
var safe_area_bottom_inset := screen_size_in_ui.y - safe_area_in_ui.end.y
# セーフエリア左部分の余白
var safe_area_left_inset := safe_area_in_ui.position.x
# セーフエリア右部分の余白
var safe_area_right_inset := screen_size_in_ui.x - safe_area_in_ui.end.x
safe_area_insets = SafeAreaInsets.new(
safe_area_top_inset,
safe_area_left_inset,
safe_area_bottom_inset,
safe_area_right_inset
)
SafeAreaInsets
というクラスも作った。(safe_area_insets.gd
)
safe_area_insets.gd
extends RefCounted
class_name SafeAreaInsets
var top: float = 0
var left: float = 0
var bottom: float = 0
var right: float = 0
func _init(top: float, left: float, bottom: float, right: float):
self.top = top
self.left = left
self.bottom = bottom
self.right = right
つかう
SafeAreaGuide.safe_area_insets
からアクセスする。
# 上部の余白
SafeAreaGuide.safe_area_insets.top
関連ノート
- Godotでモバイル向けの画面サイズ取得
- GodotでiOSのキーボードに追従するツールバーを実装する
- Godotでモバイルゲームを作るメモ(Udemy)セクション4

公開日
更新日