Godot 4에서의 컨트롤 레이아웃
깨진 Godot UI의 대부분은 세 가지 실수에서 비롯됩니다: 컨테이너의 자식 위치를 수동으로 설정하거나, 오프셋을 초기화하지 않고 앵커를 설정하거나, 스트레치 설정을 기본값으로 남겨두는 것입니다. 컨테이너로 레이아웃하고, 각 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), 그리드 (GridContainer with columns), 패딩 (MarginContainer, margin_left, margin_top, margin_right, margin_bottom 테마 상수로 설정), 배경 (PanelContainer, panel 스타일 박스), 긴 콘텐츠 (SIZE_EXPAND_FILL인 자식이 하나 있는 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를 지정하십시오.- 고정된 공간의 한 줄 텍스트의 경우
text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS(또는clip_text = true)를 설정하십시오. - 컨트롤을
scale하는 것보다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()은 이를 볼 수 없습니다. 오버레이, 스페이서 및 장식용 사각형에는 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)을 사용하여 오버라이드를 제거하십시오. 일관된 모양을 위해 각 노드를 개별적으로 오버라이드하는 대신 루트 컨트롤에 하나의 Theme 리소스를 할당하십시오. 자식이 이를 상속합니다.
해상도 및 스트레치
Project Settings > Display > Window에서 설정하십시오 (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이 설정된 보이지 않는 전체 화면 컨트롤이 위에 있습니까?