mcpField
G
godot-ui

godot-ui

github.com/zateデザイン・UI202.7k 回インストール50 週間前に更新
リポジトリ

godot-ui について

Godot でゲーム UI を作る開発者向けの UI/UX スキルです。Control ノードを使ったレイアウトやテーマ、操作パターンを扱い、メニューや HUD などの実践例に加えて、アクセシビリティと性能も解説します。

主な機能

  1. 01Control ノードのレイアウトとアンカー
  2. 02テーマのカスタマイズと再利用可能なリソース
  3. 03メニュー、HUD、インベントリ、会話のパターン
  4. 04キーボードとゲームパッドのナビゲーション
  5. 05UI アニメーションと性能の指針
  6. 06テキスト拡大とアクセシビリティ

活用シーン

  1. 01レスポンシブな設定・ポーズメニューの作成
  2. 02インベントリや会話 UI の実装
  3. 03キーボードとゲームパッドのフォーカス設定
  4. 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 パフォーマンスの最適化

ベストプラクティス:

  1. 可能な場合は z_index の代わりに CanvasLayers を使って奥行きを管理します
  2. ScrollContainers 内のコンテンツをクリップするには、clip_contents = true を設定します
  3. RichTextLabel の複雑さを抑えます。BBCode の解析には時間がかかる場合があります
  4. UI 要素をプールします。ノードを作成・破棄する代わりに再利用します
  5. UI スプライトにはTextureAtlas を使い、ドローコールを減らします
  6. 類似した要素を同じ親の下にまとめます
  7. UI が非表示のときは処理を無効にします:process_mode = PROCESS_MODE_DISABLED
  8. 画面外の要素が描画されないよう、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 要素を作成するときは、次の手順に従ってください。

  1. 新しい UI シーンファイルを作成するには、mcp__godot__create_scene を使います
  2. Control ノードの階層を構築するには、mcp__godot__add_node を使います
  3. UI の構造を作成した後、保存するには**mcp__godot__save_scene を使います**
  4. UI ロジックに関連する GDScript ファイルを作成するには、Edit/Write ツールを使います
  5. 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 の設定を使ってください

コメント