Godot 4 におけるコントロールレイアウト
GodotのUIが壊れる原因のほとんどは、次の3つの間違いから来ています。コンテナの子要素を手動で配置する、アンカーを設定してもオフセットをリセットしない、ストレッチ設定をデフォルトのままにする。コンテナでレイアウトし、アンカーは各UIのルートにのみ設定し、最小サイズですべてを制御しましょう。
コンテナは子要素を所有する
Container(VBoxContainer、HBoxContainer、GridContainer、MarginContainer、PanelContainer、CenterContainer、ScrollContainer、FlowContainer)の子要素は、自身のposition、size、アンカーを保持できません。コンテナは次のソートでそれらを上書きします。子要素のスペースの配分は以下で制御します。
size_flags_horizontal/size_flags_vertical:Control.SIZE_FILL(デフォルト)、余ったスペースを取るSIZE_EXPAND_FILL、小さく整列したままにするSIZE_SHRINK_CENTER/SIZE_SHRINK_END。size_flags_stretch_ratio: 展開する兄弟間での余ったスペースの分配率。custom_minimum_size: 子要素が受け入れる最小サイズ。コンテナは子要素をその結合最小サイズより小さく縮めることはありません。これにより、テキストやボタンが切り取られるのを防ぎます。
コンテナはレイアウトに応じて選択します。行または列(VBoxContainer、HBoxContainer)、グリッド(columnsを指定したGridContainer)、パディング(MarginContainer、テーマ定数margin_left、margin_top、margin_right、margin_bottomで設定)、背景(PanelContainer、スタイルボックスpanel)、長いコンテンツ(SIZE_EXPAND_FILLを持つ1つの子要素を持つScrollContainer)、折り返しアイテム(HFlowContainer)。
アンカーはルートのみに
アンカーは、コントロールをその親の矩形に対して相対的に配置し、最も外側のUIノードに使用するものです。コードからは、set_anchors_and_offsets_preset()を使用します。set_anchors_preset()はアンカーのみを移動し、古いオフセットを保持するため、コードで作成されたコントロールはサイズ(0, 0)のままになります。
プリセット: PRESET_FULL_RECT、PRESET_TOP_LEFT、PRESET_TOP_RIGHT、PRESET_BOTTOM_LEFT、PRESET_BOTTOM_RIGHT、PRESET_CENTER、PRESET_TOP_WIDE、PRESET_BOTTOM_WIDE、PRESET_LEFT_WIDE、PRESET_RIGHT_WIDE、PRESET_HCENTER_WIDE、PRESET_VCENTER_WIDE、およびPRESET_CENTER_TOPなどの中央端プリセット。
CanvasLayer上のHUD
Node2Dの下にあるControlはワールド空間に描画され、Camera2Dとともに移動します。HUDをCanvasLayerの下に置くと、画面に固定されます。layerは複数のキャンバスレイヤーを順序付けます(高い値ほど手前に描画されます)。
extends CanvasLayer
var score_label: Label
func _ready() -> void:
var margin := MarginContainer.new()
margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
for side in ["margin_left", "margin_top", "margin_right", "margin_bottom"]:
margin.add_theme_constant_override(side, 16)
add_child(margin)
var column := VBoxContainer.new()
margin.add_child(column)
var top_bar := HBoxContainer.new()
column.add_child(top_bar)
score_label = Label.new()
score_label.text = "Score: 0"
score_label.add_theme_font_size_override("font_size", 24)
score_label.add_theme_color_override("font_color", Color.GOLD)
top_bar.add_child(score_label)
var spacer := Control.new()
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE
top_bar.add_child(spacer)
var pause_button := Button.new()
pause_button.text = "Pause"
pause_button.custom_minimum_size = Vector2(96, 40)
top_bar.add_child(pause_button)
func set_score(value: int) -> void:
score_label.text = "Score: %d" % value
エディタで構築する場合、同じツリーは次のようになります: CanvasLayer > MarginContainer (Layout: Full Rect) > VBoxContainer > HBoxContainer > Label, Control (Expand), Button。
クリッピングしないテキスト
autowrap_modeが設定されたLabelには、どこかから幅が必要です。デフォルトのフラグを持つHBoxContainer内では、幅が1ピクセルに縮み、非常に高くなります。SIZE_EXPAND_FILLまたはcustom_minimum_size.xを設定してください。- 固定スペース内の1行テキストの場合は、
text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS(またはclip_text = true)を設定します。 - コントロールのスケーリングよりも
add_theme_font_size_override("font_size", n)を推奨します。scaleはコンテナが予約するサイズを変更しません。
extends VBoxContainer
func add_message(text: String) -> void:
var label := Label.new()
label.text = text
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
add_child(label)
func add_title(text: String) -> void:
var label := Label.new()
label.text = text
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
label.custom_minimum_size = Vector2(0, 32)
add_child(label)
ゲームに届かないクリック
mouse_filterは、コントロールがマウス入力を消費するかどうかを決定します。デフォルトは異なります。通常のControl、ColorRect、PanelはMOUSE_FILTER_STOP、コンテナはMOUSE_FILTER_PASS、LabelはMOUSE_FILTER_IGNOREを使用します。STOPが設定されたフルスクリーンのControlまたはColorRectは、すべてのクリックを飲み込み、ゲーム内の_unhandled_input()はそれを見ることができません。オーバーレイ、スペーサー、装飾的なRectにはmouse_filter = Control.MOUSE_FILTER_IGNOREを設定してください。
テーマオーバーライド(Godot 4名)
| Godot 3 | Godot 4 |
|---|---|
add_color_override("font_color", c) | add_theme_color_override("font_color", c) |
add_font_override("font", f) | add_theme_font_override("font", f) |
| (フォントサイズはDynamicFontにありました) | add_theme_font_size_override("font_size", 24) |
add_stylebox_override("normal", sb) | add_theme_stylebox_override("normal", sb) |
add_constant_override("separation", 8) | add_theme_constant_override("separation", 8) |
add_icon_override("icon", tex) | add_theme_icon_override("icon", tex) |
.tscn内のcustom_colors/font_color | theme_override_colors/font_color |
対応するremove_theme_*_override(name)でオーバーライドを削除します。一貫した外観にするには、各ノードを個別にオーバーライドする代わりに、ルートコントロールに1つのThemeリソースを割り当てます。子要素はそれを継承します。
解像度とストレッチ
これらはプロジェクト設定 > 表示 > ウィンドウで設定します(project.godotに保存されます)。
display/window/size/viewport_widthおよびviewport_height: 設計する基本解像度(デフォルト1152 x 648)。display/window/stretch/mode: ほとんどのゲームではcanvas_items(UIと2Dがスムーズにスケール)、ピクセルアートではviewport、ツールではdisabled。Godot 3の2dモードはGodot 4ではcanvas_itemsです。デフォルトはdisabled。Godot 4.7以降で作成されたプロジェクトはcanvas_itemsで始まります。display/window/stretch/aspect:expandはアンカー付きUIがより広いまたは高い画面で余分なスペースを使用できるようにします。keepは黒帯を追加します。デフォルトはkeep。Godot 4.7以降で作成されたプロジェクトはexpandで始まります。display/window/stretch/scale_mode: ピクセルアートを鮮明にするにはinteger。
実行時には同じ設定がウィンドウにあります: get_window().content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS および get_window().content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND。
UIが切れたり配置がおかしい場合のチェックリスト
- コンテナの子要素に手動で
positionやsizeが設定されていませんか?サイズフラグとcustom_minimum_sizeを使用してください。 - ルートが
set_anchors_preset()だけでアンカー設定されていませんか?set_anchors_and_offsets_preset()を使用してください。 - 折り返し
Labelに幅が設定されていますか? - HUDは
Node2DではなくCanvasLayerの下にありますか? - ウィンドウサイズが変更されているのに
display/window/stretch/modeがまだdisabledになっていませんか? - 不可視のフルスクリーンコントロールに
MOUSE_FILTER_STOPが設定されていて、上に重なっていませんか?