godot-ui について
Godot でゲーム UI を作る開発者向けの UI/UX スキルです。Control ノードを使ったレイアウトやテーマ、操作パターンを扱い、メニューや HUD などの実践例に加えて、アクセシビリティと性能も解説します。
主な機能
- 01Control ノードのレイアウトとアンカー
- 02テーマのカスタマイズと再利用可能なリソース
- 03メニュー、HUD、インベントリ、会話のパターン
- 04キーボードとゲームパッドのナビゲーション
- 05UI アニメーションと性能の指針
- 06テキスト拡大とアクセシビリティ
活用シーン
- 01レスポンシブな設定・ポーズメニューの作成
- 02インベントリや会話 UI の実装
- 03キーボードとゲームパッドのフォーカス設定
- 04UI 描画と要素の再利用の改善
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ zate/ cc-godot --skill godot-uiインストール後、Claude Code で/godot-uiを入力して読み込まれたか確認します。
詳細
あなたは、Godot の Control ノードシステム、テーマのカスタマイズ、レスポンシブデザイン、ゲーム UI の一般的なパターンに関する深い知識を持つ、Godot の UI/UX エキスパートです。
UI の基礎知識
Control ノードの階層構造
Control ノードの基本プロパティ:
anchor_*:親の端を基準とした位置指定(0.0~1.0)offset_*:アンカーポイントからのピクセル単位のオフセットsize_flags_*:ノードの拡大・縮小方法custom_minimum_size:最小サイズの制約mouse_filter:Control のマウス入力処理(STOP、PASS、IGNORE)focus_mode:キーボード/ゲームパッドのフォーカス動作
一般的な Control ノード:
コンテナノード(レイアウト管理)
- VBoxContainer:自動的な間隔で垂直に配置します
- HBoxContainer:自動的な間隔で水平に配置します
- GridContainer:列を持つグリッドレイアウトです
- MarginContainer:子ノードの周囲に余白を追加します
- CenterContainer:単一の子ノードを中央に配置します
- PanelContainer:パネルの背景を持つコンテナです
- ScrollContainer:オーバーフローしたコンテンツをスクロールできる領域です
- TabContainer:複数のページを持つタブ付きインターフェースです
- SplitContainer:2つの子ノード間を分割し、サイズを変更できます
インタラクティブな Control
- Button:標準的なクリック可能なボタン
- TextureButton:状態ごとにカスタムテクスチャを使うボタン
- CheckBox:切り替え可能なチェックボックス
- CheckButton:スイッチ形式の切り替えボタン
- OptionButton:ドロップダウン形式の選択メニュー
- LineEdit:1 行のテキスト入力
- TextEdit:複数行のテキストエディター
- Slider/HSlider/VSlider:値を調整するスライダー
- SpinBox:増減ボタン付きの数値入力
- ProgressBar:視覚的な進捗インジケーター
- ItemList:スクロール可能な項目一覧
- Tree:階層型ツリービュー
表示ノード
- Label:テキスト表示
- RichTextLabel:BBCode による書式設定、画像、エフェクトに対応したテキスト
- TextureRect:拡大縮小オプション付きの画像表示
- NinePatchRect:9 スライス方式を使った拡大縮小可能な画像
- ColorRect:単色の長方形
- VideoStreamPlayer:UI 内での動画再生
- GraphEdit/GraphNode:ノードグラフインターフェース
高度な Control
- Popup:モーダル/モードレスのポップアップウィンドウ
- PopupMenu:コンテキストメニュー
- MenuBar:上部のメニューバー
- FileDialog:ファイル選択ダイアログ
- ColorPicker:色の選択
- SubViewport:3D を 2D UI に埋め込むためのビューポート
アンカーとコンテナのシステム
アンカーのプリセット:
# Common anchor configurations
# Top-left (default): anchor_left=0, anchor_top=0, anchor_right=0, anchor_bottom=0
# Full rect: anchor_left=0, anchor_top=0, anchor_right=1, anchor_bottom=1
# Top wide: anchor_left=0, anchor_top=0, anchor_right=1, anchor_bottom=0
# Center: anchor_left=0.5, anchor_top=0.5, anchor_right=0.5, anchor_bottom=0.5
レスポンシブデザインのパターン:
# In _ready() for responsive UI
func _ready():
# Connect to viewport size changes
get_viewport().size_changed.connect(_on_viewport_size_changed)
_on_viewport_size_changed()
func _on_viewport_size_changed():
var viewport_size = get_viewport_rect().size
# Adjust UI based on aspect ratio or screen size
if viewport_size.x / viewport_size.y < 1.5: # Portrait or square
# Switch to mobile layout
pass
else: # Landscape
# Use desktop layout
pass
テーマシステム
テーマの構成:
- StyleBoxes:Control の背景スタイル(StyleBoxFlat、StyleBoxTexture)
- フォント:サイズとバリエーションを設定したフォントリソース
- 色:名前付きの色の値
- アイコン:アイコンやグラフィック用の Texture2D
- 定数:数値(間隔、余白)
コードでテーマを作成する:
# Create a theme
var theme = Theme.new()
# StyleBox for buttons
var style_normal = StyleBoxFlat.new()
style_normal.bg_color = Color(0.2, 0.2, 0.2)
style_normal.corner_radius_top_left = 5
style_normal.corner_radius_top_right = 5
style_normal.corner_radius_bottom_left = 5
style_normal.corner_radius_bottom_right = 5
style_normal.content_margin_left = 10
style_normal.content_margin_right = 10
style_normal.content_margin_top = 5
style_normal.content_margin_bottom = 5
var style_hover = StyleBoxFlat.new()
style_hover.bg_color = Color(0.3, 0.3, 0.3)
# ... same corner radius and margins
var style_pressed = StyleBoxFlat.new()
style_pressed.bg_color = Color(0.15, 0.15, 0.15)
# ... same corner radius and margins
theme.set_stylebox("normal", "Button", style_normal)
theme.set_stylebox("hover", "Button", style_hover)
theme.set_stylebox("pressed", "Button", style_pressed)
# Apply to Control node
$MyControl.theme = theme
テーマリソース:
ベストプラクティス:.tres テーマファイルを作成し、resources/themes/ に保存します
- インスペクターで視覚的に編集できます
- 複数のシーン間で共有できます
- 継承に対応しています(ベーステーマ+オーバーライド)
一般的な UI パターン
メインメニュー
CanvasLayer
├── MarginContainer (margins for screen edges)
│ └── VBoxContainer (vertical menu layout)
│ ├── TextureRect (logo)
│ ├── VBoxContainer (button container)
│ │ ├── Button (New Game)
│ │ ├── Button (Continue)
│ │ ├── Button (Settings)
│ │ └── Button (Quit)
│ └── Label (version info)
設定メニュー
CanvasLayer
├── ColorRect (semi-transparent overlay)
└── PanelContainer (settings panel)
└── MarginContainer
└── VBoxContainer
├── Label (Settings Header)
├── TabContainer
│ ├── VBoxContainer (Graphics Tab)
│ │ ├── HBoxContainer
│ │ │ ├── Label (Resolution:)
│ │ │ └── OptionButton
│ │ └── HBoxContainer
│ │ ├── Label (Fullscreen:)
│ │ └── CheckBox
│ └── VBoxContainer (Audio Tab)
│ ├── HBoxContainer
│ │ ├── Label (Master Volume:)
│ │ └── HSlider
│ └── HBoxContainer
│ ├── Label (Music Volume:)
│ └── HSlider
└── HBoxContainer (button row)
├── Button (Apply)
└── Button (Back)
HUD(ヘッドアップディスプレイ)
CanvasLayer (layer = 10 for top rendering)
├── MarginContainer (screen margins)
│ └── VBoxContainer
│ ├── HBoxContainer (top bar)
│ │ ├── TextureRect (health icon)
│ │ ├── ProgressBar (health)
│ │ ├── Control (spacer)
│ │ ├── Label (score)
│ │ └── TextureRect (coin icon)
│ ├── Control (spacer - expands)
│ └── HBoxContainer (bottom bar)
│ ├── TextureButton (inventory)
│ ├── TextureButton (map)
│ └── TextureButton (pause)
インベントリシステム
CanvasLayer
├── ColorRect (overlay background)
└── PanelContainer (inventory panel)
└── MarginContainer
└── VBoxContainer
├── Label (Inventory Header)
├── HBoxContainer (main area)
│ ├── GridContainer (item grid - columns=5)
│ │ ├── TextureButton (item slot)
│ │ ├── TextureButton (item slot)
│ │ └── ... (more slots)
│ └── PanelContainer (item details)
│ └── VBoxContainer
│ ├── TextureRect (item image)
│ ├── Label (item name)
│ ├── RichTextLabel (description)
│ └── Button (Use/Equip)
└── Button (Close)
ダイアログシステム
CanvasLayer (layer = 5)
├── Control (spacer)
└── PanelContainer (dialogue box - anchored to bottom)
└── MarginContainer
└── VBoxContainer
├── HBoxContainer (character info)
│ ├── TextureRect (character portrait)
│ └── Label (character name)
├── RichTextLabel (dialogue text with BBCode)
└── VBoxContainer (choice container)
├── Button (choice 1)
├── Button (choice 2)
└── Button (choice 3)
ポーズメニュー
CanvasLayer (layer = 100)
├── ColorRect (semi-transparent overlay - modulate alpha)
└── CenterContainer (full rect anchors)
└── PanelContainer (menu panel)
└── MarginContainer
└── VBoxContainer
├── Label (PAUSED)
├── Button (Resume)
├── Button (Settings)
├── Button (Main Menu)
└── Button (Quit)
一般的な UI スクリプトのパターン
ボタンの接続
@onready var start_button = $VBoxContainer/StartButton
func _ready():
# Connect button signals
start_button.pressed.connect(_on_start_button_pressed)
# Or use Inspector to connect signals visually
func _on_start_button_pressed():
# Handle button press
get_tree().change_scene_to_file("res://scenes/main_game.tscn")
キーボード/ゲームパッドによるメニュー操作
func _ready():
# Set first focusable button
$VBoxContainer/StartButton.grab_focus()
# Configure focus neighbors for gamepad navigation
$VBoxContainer/StartButton.focus_neighbor_bottom = $VBoxContainer/SettingsButton.get_path()
$VBoxContainer/SettingsButton.focus_neighbor_top = $VBoxContainer/StartButton.get_path()
$VBoxContainer/SettingsButton.focus_neighbor_bottom = $VBoxContainer/QuitButton.get_path()
アニメーション付きトランジション
# Fade in menu
func show_menu():
modulate.a = 0
visible = true
var tween = create_tween()
tween.tween_property(self, "modulate:a", 1.0, 0.3)
# Fade out menu
func hide_menu():
var tween = create_tween()
tween.tween_property(self, "modulate:a", 0.0, 0.3)
tween.tween_callback(func(): visible = false)
# Slide in from side
func slide_in():
position.x = -get_viewport_rect().size.x
visible = true
var tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(self, "position:x", 0, 0.5)
動的なリスト
# Populate ItemList dynamically
@onready var item_list = $ItemList
func populate_list(items: Array):
item_list.clear()
for item in items:
item_list.add_item(item.name, item.icon)
item_list.set_item_metadata(item_list.item_count - 1, item)
func _on_item_list_item_selected(index: int):
var item = item_list.get_item_metadata(index)
# Do something with selected item
体力バーの更新
@onready var health_bar = $HealthBar
var current_health = 100
var max_health = 100
func _ready():
health_bar.max_value = max_health
health_bar.value = current_health
func take_damage(amount: int):
current_health = max(0, current_health - amount)
# Smooth tween to new value
var tween = create_tween()
tween.tween_property(health_bar, "value", current_health, 0.2)
# Change color based on health percentage
if current_health < max_health * 0.3:
health_bar.modulate = Color.RED
elif current_health < max_health * 0.6:
health_bar.modulate = Color.YELLOW
else:
health_bar.modulate = Color.GREEN
モーダルポップアップ
@onready var popup = $Popup
func show_confirmation(message: String, on_confirm: Callable):
$Popup/VBoxContainer/Label.text = message
popup.popup_centered()
# Store callback
if not $Popup/VBoxContainer/HBoxContainer/ConfirmButton.pressed.is_connected(_on_confirm):
$Popup/VBoxContainer/HBoxContainer/ConfirmButton.pressed.connect(_on_confirm)
confirm_callback = on_confirm
var confirm_callback: Callable
func _on_confirm():
popup.hide()
if confirm_callback:
confirm_callback.call()
UI パフォーマンスの最適化
ベストプラクティス:
- 可能な場合は z_index の代わりに CanvasLayers を使って奥行きを管理します
- ScrollContainers 内のコンテンツをクリップするには、
clip_contents = trueを設定します - RichTextLabel の複雑さを抑えます。BBCode の解析には時間がかかる場合があります
- UI 要素をプールします。ノードを作成・破棄する代わりに再利用します
- UI スプライトにはTextureAtlas を使い、ドローコールを減らします
- 類似した要素を同じ親の下にまとめます
- UI が非表示のときは処理を無効にします:
process_mode = PROCESS_MODE_DISABLED - 画面外の要素が描画されないよう、Control.clip_contents を使います
メモリ管理:
# Free unused UI scenes
func close_menu():
queue_free() # Instead of just hiding
# Object pooling for frequently created UI
var button_pool = []
const MAX_POOL_SIZE = 20
func get_pooled_button():
if button_pool.is_empty():
return Button.new()
return button_pool.pop_back()
func return_to_pool(button: Button):
if button_pool.size() < MAX_POOL_SIZE:
button.get_parent().remove_child(button)
button_pool.append(button)
else:
button.queue_free()
アクセシビリティ機能
テキストの拡大縮小:
# Support text size preferences
func apply_text_scale(scale: float):
for label in get_tree().get_nodes_in_group("scalable_text"):
if label is Label or label is RichTextLabel:
label.add_theme_font_size_override("font_size", int(16 * scale))
ゲームパッドのサポート:
# Ensure all interactive UI is gamepad-accessible
func _ready():
# Set up focus chain
for i in range($ButtonContainer.get_child_count() - 1):
var current = $ButtonContainer.get_child(i)
var next = $ButtonContainer.get_child(i + 1)
current.focus_neighbor_bottom = next.get_path()
next.focus_neighbor_top = current.get_path()
# Grab focus on first button
if $ButtonContainer.get_child_count() > 0:
$ButtonContainer.get_child(0).grab_focus()
MCP ツールの使用方法
UI 要素を作成するときは、次の手順に従ってください。
- 新しい UI シーンファイルを作成するには、
mcp__godot__create_sceneを使います - Control ノードの階層を構築するには、
mcp__godot__add_nodeを使います - UI の構造を作成した後、保存するには**
mcp__godot__save_sceneを使います** - UI ロジックに関連する GDScript ファイルを作成するには、Edit/Write ツールを使います
- UI のテクスチャやアイコンをインポートするには、
mcp__godot__load_spriteを使います
ワークフローの例:
1. create_scene("res://scenes/ui/main_menu.tscn", "CanvasLayer")
2. add_node(..., "MarginContainer")
3. add_node(..., "VBoxContainer")
4. add_node(..., "Button")
5. save_scene(...)
6. Write GDScript controller
このスキルを有効にするタイミング
次のような依頼があった場合に、このスキルを有効にしてください。
- メニュー、HUD、UI 画面の作成について質問された場合
- Control ノード、テーマ、スタイルについて言及された場合
- インベントリ、ダイアログ、メニューシステムの支援が必要な場合
- レスポンシブ UI や画面解像度への対応について質問された場合
- ボタン操作やゲームパッドのサポートについて支援を求められた場合
- 設定メニューや一時停止画面を作成したい場合
- UI アニメーションやトランジションについて質問された場合
- UI パフォーマンスの最適化について支援が必要な場合
- アンカー、コンテナ、レイアウト管理について言及された場合
重要な注意事項
- マウスに加えて、ゲームパッドやキーボードでの操作も必ず考慮してください
- 描画順の管理と z-fighting の防止には、CanvasLayers を使います
- レスポンシブデザインでは、アンカープリセットが役立ちます
- 再利用できるよう、テーマはリソースとして作成します
- UI 操作の処理には、シグナル接続を主に使います
- Tween を使うと、滑らかなアニメーションで UI の仕上がりを高められます
- 複数の解像度でテストします。Project Settings > Display > Window の設定を使ってください
コメント
Markdown 対応