23 KiB
allowed_tools
| allowed_tools | |||||
|---|---|---|---|---|---|
|
Create a quick UI template for common game UI screens.
Process
- Ask the user what type of UI they want to create:
Use AskUserQuestion with the following options:
Question: "What type of UI screen would you like to create?" Header: "UI Type" Multi-select: false Options:
- Main Menu: Title screen with New Game, Continue, Settings, Quit
- Pause Menu: In-game pause screen with Resume, Settings, Main Menu, Quit
- Settings Menu: Graphics, audio, and gameplay settings with tabs
- Game HUD: Health bar, score, and interactive buttons
- Inventory: Grid-based item management system
- Dialogue Box: Character dialogue with portrait and choices
- Confirmation Dialog: Yes/No popup dialog
Question 2: "Where should the UI scene be created?" Header: "Location" Multi-select: false Options:
- scenes/ui/menus/: For menu screens
- scenes/ui/hud/: For in-game HUD elements
- scenes/ui/dialogs/: For popup dialogs
- Custom path: I'll specify the path
-
If Custom path selected, ask for the specific path
-
Create the appropriate template based on selection
Main Menu Template
Create scene with:
CanvasLayer (root)
├── ColorRect (background - full rect anchors)
├── MarginContainer (full rect, margins: 40px all sides)
│ └── VBoxContainer (alignment: center)
│ ├── Control (spacer - custom_minimum_size.y = 100)
│ ├── Label (title - custom font size 48, center aligned)
│ ├── Control (spacer - custom_minimum_size.y = 50)
│ ├── VBoxContainer (button_container - separation: 10)
│ │ ├── Button (new_game_btn - text: "New Game", custom_minimum_size.x = 200)
│ │ ├── Button (continue_btn - text: "Continue", custom_minimum_size.x = 200)
│ │ ├── Button (settings_btn - text: "Settings", custom_minimum_size.x = 200)
│ │ └── Button (quit_btn - text: "Quit", custom_minimum_size.x = 200)
│ └── Control (spacer with size_flags_vertical = 3)
Create accompanying script:
extends CanvasLayer
@onready var new_game_btn = $MarginContainer/VBoxContainer/VBoxContainer/new_game_btn
@onready var continue_btn = $MarginContainer/VBoxContainer/VBoxContainer/continue_btn
@onready var settings_btn = $MarginContainer/VBoxContainer/VBoxContainer/settings_btn
@onready var quit_btn = $MarginContainer/VBoxContainer/VBoxContainer/quit_btn
func _ready():
# Connect button signals
new_game_btn.pressed.connect(_on_new_game_pressed)
continue_btn.pressed.connect(_on_continue_pressed)
settings_btn.pressed.connect(_on_settings_pressed)
quit_btn.pressed.connect(_on_quit_pressed)
# Set focus for gamepad support
new_game_btn.grab_focus()
# Check if save exists for continue button
continue_btn.disabled = not _has_save_file()
# Fade in animation
modulate.a = 0
var tween = create_tween()
tween.tween_property(self, "modulate:a", 1.0, 0.5)
func _has_save_file() -> bool:
# TODO: Implement save file checking
return FileAccess.file_exists("user://savegame.save")
func _on_new_game_pressed():
# TODO: Implement new game logic
get_tree().change_scene_to_file("res://scenes/main_game.tscn")
func _on_continue_pressed():
# TODO: Implement load game logic
pass
func _on_settings_pressed():
# TODO: Open settings menu
get_tree().change_scene_to_file("res://scenes/ui/menus/settings_menu.tscn")
func _on_quit_pressed():
get_tree().quit()
Pause Menu Template
Create scene with:
CanvasLayer (root - layer: 100)
├── ColorRect (overlay - full rect, color: #00000080, mouse_filter: STOP)
├── CenterContainer (full rect anchors)
│ └── PanelContainer (custom_minimum_size: 400x500)
│ └── MarginContainer (margins: 20px all sides)
│ └── VBoxContainer (separation: 15)
│ ├── Label (title - text: "PAUSED", align: center, font size: 36)
│ ├── Control (spacer - custom_minimum_size.y = 20)
│ ├── Button (resume_btn - text: "Resume")
│ ├── Button (settings_btn - text: "Settings")
│ ├── Button (main_menu_btn - text: "Main Menu")
│ └── Button (quit_btn - text: "Quit")
Create accompanying script:
extends CanvasLayer
@onready var resume_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/resume_btn
@onready var settings_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/settings_btn
@onready var main_menu_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/main_menu_btn
@onready var quit_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/quit_btn
func _ready():
# Connect signals
resume_btn.pressed.connect(_on_resume_pressed)
settings_btn.pressed.connect(_on_settings_pressed)
main_menu_btn.pressed.connect(_on_main_menu_pressed)
quit_btn.pressed.connect(_on_quit_pressed)
# Pause the game
get_tree().paused = true
# Set focus
resume_btn.grab_focus()
# Pop-up animation
$CenterContainer.scale = Vector2(0.8, 0.8)
$CenterContainer.modulate.a = 0
var tween = create_tween()
tween.tween_property($CenterContainer, "scale", Vector2.ONE, 0.3).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
tween.parallel().tween_property($CenterContainer, "modulate:a", 1.0, 0.3)
func _on_resume_pressed():
_close_menu()
func _on_settings_pressed():
# TODO: Open settings submenu
pass
func _on_main_menu_pressed():
get_tree().paused = false
get_tree().change_scene_to_file("res://scenes/ui/menus/main_menu.tscn")
func _on_quit_pressed():
get_tree().quit()
func _close_menu():
get_tree().paused = false
queue_free()
func _input(event):
if event.is_action_pressed("ui_cancel"):
_close_menu()
Settings Menu Template
Create scene with:
CanvasLayer (root)
├── ColorRect (background - full rect)
├── MarginContainer (full rect, margins: 40px all sides)
│ └── VBoxContainer (separation: 20)
│ ├── Label (title - text: "Settings", font size: 36)
│ ├── TabContainer (size_flags_vertical: 3)
│ │ ├── VBoxContainer (name: "Graphics", separation: 10)
│ │ │ ├── HBoxContainer
│ │ │ │ ├── Label (text: "Resolution:")
│ │ │ │ ├── Control (size_flags_horizontal: 3)
│ │ │ │ └── OptionButton (resolution_option)
│ │ │ ├── HBoxContainer
│ │ │ │ ├── Label (text: "Fullscreen:")
│ │ │ │ ├── Control (size_flags_horizontal: 3)
│ │ │ │ └── CheckBox (fullscreen_check)
│ │ │ └── HBoxContainer
│ │ │ ├── Label (text: "VSync:")
│ │ │ ├── Control (size_flags_horizontal: 3)
│ │ │ └── CheckBox (vsync_check)
│ │ └── VBoxContainer (name: "Audio", separation: 10)
│ │ ├── HBoxContainer
│ │ │ ├── Label (text: "Master Volume:")
│ │ │ └── HSlider (master_slider - min: 0, max: 100, value: 100)
│ │ ├── HBoxContainer
│ │ │ ├── Label (text: "Music Volume:")
│ │ │ └── HSlider (music_slider - min: 0, max: 100, value: 100)
│ │ └── HBoxContainer
│ │ ├── Label (text: "SFX Volume:")
│ │ └── HSlider (sfx_slider - min: 0, max: 100, value: 100)
│ └── HBoxContainer (separation: 10)
│ ├── Control (size_flags_horizontal: 3)
│ ├── Button (apply_btn - text: "Apply")
│ └── Button (back_btn - text: "Back")
Create accompanying script:
extends CanvasLayer
# Graphics tab
@onready var resolution_option = $MarginContainer/VBoxContainer/TabContainer/Graphics/HBoxContainer/resolution_option
@onready var fullscreen_check = $MarginContainer/VBoxContainer/TabContainer/Graphics/HBoxContainer2/fullscreen_check
@onready var vsync_check = $MarginContainer/VBoxContainer/TabContainer/Graphics/HBoxContainer3/vsync_check
# Audio tab
@onready var master_slider = $MarginContainer/VBoxContainer/TabContainer/Audio/HBoxContainer/master_slider
@onready var music_slider = $MarginContainer/VBoxContainer/TabContainer/Audio/HBoxContainer2/music_slider
@onready var sfx_slider = $MarginContainer/VBoxContainer/TabContainer/Audio/HBoxContainer3/sfx_slider
# Buttons
@onready var apply_btn = $MarginContainer/VBoxContainer/HBoxContainer/apply_btn
@onready var back_btn = $MarginContainer/VBoxContainer/HBoxContainer/back_btn
func _ready():
# Populate resolution options
resolution_option.add_item("1920x1080")
resolution_option.add_item("1280x720")
resolution_option.add_item("1024x768")
# Load current settings
_load_settings()
# Connect signals
apply_btn.pressed.connect(_on_apply_pressed)
back_btn.pressed.connect(_on_back_pressed)
master_slider.value_changed.connect(_on_master_volume_changed)
music_slider.value_changed.connect(_on_music_volume_changed)
sfx_slider.value_changed.connect(_on_sfx_volume_changed)
func _load_settings():
# TODO: Load from config file
fullscreen_check.button_pressed = (DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN)
vsync_check.button_pressed = (DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED)
func _on_apply_pressed():
_save_settings()
func _save_settings():
# Graphics settings
if fullscreen_check.button_pressed:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
if vsync_check.button_pressed:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
else:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
# TODO: Save to config file
# TODO: Apply resolution
func _on_back_pressed():
get_tree().change_scene_to_file("res://scenes/ui/menus/main_menu.tscn")
func _on_master_volume_changed(value: float):
# TODO: Apply to audio bus
AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear_to_db(value / 100.0))
func _on_music_volume_changed(value: float):
# TODO: Apply to audio bus
pass
func _on_sfx_volume_changed(value: float):
# TODO: Apply to audio bus
pass
Game HUD Template
Create scene with:
CanvasLayer (root - layer: 10)
├── MarginContainer (full rect, margins: 20px all sides)
│ └── VBoxContainer
│ ├── HBoxContainer (top_bar - separation: 10)
│ │ ├── TextureRect (health_icon - expand_mode: keep_size, custom_minimum_size: 32x32)
│ │ ├── ProgressBar (health_bar - custom_minimum_size: 200x24)
│ │ ├── Control (spacer - size_flags_horizontal: 3)
│ │ ├── Label (score_label - text: "Score: 0")
│ │ └── TextureRect (coin_icon - expand_mode: keep_size, custom_minimum_size: 24x24)
│ ├── Control (middle_spacer - size_flags_vertical: 3)
│ └── HBoxContainer (bottom_bar - separation: 10)
│ ├── Control (spacer - size_flags_horizontal: 3)
│ ├── TextureButton (inventory_btn - custom_minimum_size: 48x48)
│ ├── TextureButton (map_btn - custom_minimum_size: 48x48)
│ └── TextureButton (pause_btn - custom_minimum_size: 48x48)
Create accompanying script:
extends CanvasLayer
@onready var health_bar = $MarginContainer/VBoxContainer/HBoxContainer/health_bar
@onready var score_label = $MarginContainer/VBoxContainer/HBoxContainer/score_label
@onready var inventory_btn = $MarginContainer/VBoxContainer/HBoxContainer2/inventory_btn
@onready var map_btn = $MarginContainer/VBoxContainer/HBoxContainer2/map_btn
@onready var pause_btn = $MarginContainer/VBoxContainer/HBoxContainer2/pause_btn
var current_score: int = 0
func _ready():
# Connect button signals
inventory_btn.pressed.connect(_on_inventory_pressed)
map_btn.pressed.connect(_on_map_pressed)
pause_btn.pressed.connect(_on_pause_pressed)
# Initialize health bar
health_bar.max_value = 100
health_bar.value = 100
func set_health(value: float):
var tween = create_tween()
tween.tween_property(health_bar, "value", value, 0.3)
# Change color based on health
if value < 30:
health_bar.modulate = Color.RED
elif value < 60:
health_bar.modulate = Color.YELLOW
else:
health_bar.modulate = Color.GREEN
func add_score(amount: int):
current_score += amount
score_label.text = "Score: %d" % current_score
# Bounce animation
var tween = create_tween()
tween.tween_property(score_label, "scale", Vector2(1.2, 1.2), 0.1)
tween.tween_property(score_label, "scale", Vector2.ONE, 0.1)
func _on_inventory_pressed():
# TODO: Open inventory
pass
func _on_map_pressed():
# TODO: Open map
pass
func _on_pause_pressed():
# Open pause menu
var pause_scene = load("res://scenes/ui/menus/pause_menu.tscn")
get_tree().root.add_child(pause_scene.instantiate())
Inventory Template
Create scene with:
CanvasLayer (root - layer: 50)
├── ColorRect (overlay - full rect, color: #00000080)
├── CenterContainer (full rect)
│ └── PanelContainer (custom_minimum_size: 800x600)
│ └── MarginContainer (margins: 20px all sides)
│ └── VBoxContainer (separation: 15)
│ ├── Label (title - text: "Inventory", font size: 32)
│ ├── HSeparator
│ ├── HBoxContainer (size_flags_vertical: 3, separation: 15)
│ │ ├── ScrollContainer (size_flags_horizontal: 3)
│ │ │ └── GridContainer (item_grid - columns: 5, separation: 10)
│ │ └── PanelContainer (item_details - custom_minimum_size.x: 250)
│ │ └── MarginContainer (margins: 10px all sides)
│ │ └── VBoxContainer
│ │ ├── TextureRect (item_image - expand_mode: keep_aspect_centered, custom_minimum_size: 200x200)
│ │ ├── Label (item_name - text: "Select an item", align: center, font size: 18)
│ │ ├── RichTextLabel (item_description - text: "No item selected", bbcode_enabled: true, size_flags_vertical: 3)
│ │ └── Button (use_btn - text: "Use", disabled: true)
│ └── Button (close_btn - text: "Close")
Create accompanying script:
extends CanvasLayer
@onready var item_grid = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/item_grid
@onready var item_image = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/item_image
@onready var item_name = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/item_name
@onready var item_description = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/item_description
@onready var use_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/use_btn
@onready var close_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/close_btn
var selected_item = null
const SLOT_SCENE = preload("res://scenes/ui/hud/hud_components/inventory_slot.tscn")
func _ready():
# Connect signals
use_btn.pressed.connect(_on_use_pressed)
close_btn.pressed.connect(_on_close_pressed)
# Populate inventory
_populate_inventory()
# Pause game
get_tree().paused = true
func _populate_inventory():
# Clear existing slots
for child in item_grid.get_children():
child.queue_free()
# TODO: Get inventory from inventory manager
# For now, create empty slots
for i in range(20):
var slot = SLOT_SCENE.instantiate()
slot.slot_clicked.connect(_on_slot_clicked.bind(slot))
item_grid.add_child(slot)
func _on_slot_clicked(slot):
# Update item details
if slot.item_data:
selected_item = slot.item_data
item_name.text = slot.item_data.name
item_description.text = slot.item_data.description
item_image.texture = slot.item_data.icon
use_btn.disabled = false
else:
selected_item = null
item_name.text = "Empty slot"
item_description.text = ""
item_image.texture = null
use_btn.disabled = true
func _on_use_pressed():
if selected_item:
# TODO: Use item logic
pass
func _on_close_pressed():
get_tree().paused = false
queue_free()
func _input(event):
if event.is_action_pressed("ui_cancel"):
_on_close_pressed()
Dialogue Box Template
Create scene with:
CanvasLayer (root - layer: 20)
├── Control (spacer - size_flags_vertical: 3)
└── PanelContainer (dialogue_panel - anchor_left: 0, anchor_right: 1, anchor_bottom: 1, custom_minimum_size.y: 200)
└── MarginContainer (margins: 15px all sides)
└── VBoxContainer (separation: 10)
├── HBoxContainer (character_info - separation: 10)
│ ├── TextureRect (portrait - expand_mode: keep_size, custom_minimum_size: 80x80)
│ └── Label (character_name - text: "Character", font size: 20)
├── RichTextLabel (dialogue_text - bbcode_enabled: true, size_flags_vertical: 3, text: "Dialogue text goes here...")
└── VBoxContainer (choices_container - separation: 5)
Create accompanying script:
extends CanvasLayer
@onready var portrait = $PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/portrait
@onready var character_name = $PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/character_name
@onready var dialogue_text = $PanelContainer/MarginContainer/VBoxContainer/dialogue_text
@onready var choices_container = $PanelContainer/MarginContainer/VBoxContainer/choices_container
var current_dialogue_index: int = 0
var dialogue_data: Array = []
var text_speed: float = 0.05
var is_text_complete: bool = false
func _ready():
# Hide initially
$PanelContainer.modulate.a = 0
$PanelContainer.position.y = 50
func show_dialogue(data: Array):
dialogue_data = data
current_dialogue_index = 0
_display_current_dialogue()
# Slide in animation
var tween = create_tween()
tween.tween_property($PanelContainer, "modulate:a", 1.0, 0.3)
tween.parallel().tween_property($PanelContainer, "position:y", 0, 0.3)
func _display_current_dialogue():
if current_dialogue_index >= dialogue_data.size():
_close_dialogue()
return
var dialogue = dialogue_data[current_dialogue_index]
# Set character info
character_name.text = dialogue.get("character", "")
if dialogue.has("portrait"):
portrait.texture = dialogue.portrait
# Clear choices
for child in choices_container.get_children():
child.queue_free()
# Animate text
is_text_complete = false
_type_text(dialogue.text)
# Add choices if present
if dialogue.has("choices") and dialogue.choices.size() > 0:
await get_tree().create_timer(0.1).timeout # Wait for text to start
for i in range(dialogue.choices.size()):
var choice_text = dialogue.choices[i]
var btn = Button.new()
btn.text = choice_text
btn.pressed.connect(_on_choice_selected.bind(i))
choices_container.add_child(btn)
func _type_text(text: String):
dialogue_text.text = ""
dialogue_text.visible_characters = 0
dialogue_text.text = text
var char_count = text.length()
for i in range(char_count + 1):
dialogue_text.visible_characters = i
await get_tree().create_timer(text_speed).timeout
is_text_complete = true
func _input(event):
if event.is_action_pressed("ui_accept"):
if is_text_complete and choices_container.get_child_count() == 0:
_next_dialogue()
elif not is_text_complete:
# Skip text animation
dialogue_text.visible_ratio = 1.0
is_text_complete = true
func _next_dialogue():
current_dialogue_index += 1
_display_current_dialogue()
func _on_choice_selected(choice_index: int):
# TODO: Handle dialogue choice
# For now, just advance
_next_dialogue()
func _close_dialogue():
var tween = create_tween()
tween.tween_property($PanelContainer, "modulate:a", 0.0, 0.3)
tween.parallel().tween_property($PanelContainer, "position:y", 50, 0.3)
tween.tween_callback(queue_free)
Confirmation Dialog Template
Create scene with:
CanvasLayer (root - layer: 200)
├── ColorRect (overlay - full rect, color: #00000080)
├── CenterContainer (full rect)
│ └── PanelContainer (custom_minimum_size: 400x200)
│ └── MarginContainer (margins: 20px all sides)
│ └── VBoxContainer (separation: 20)
│ ├── Label (message - text: "Are you sure?", align: center, autowrap: true, size_flags_vertical: 3)
│ └── HBoxContainer (separation: 10)
│ ├── Button (cancel_btn - text: "Cancel", size_flags_horizontal: 3)
│ └── Button (confirm_btn - text: "Confirm", size_flags_horizontal: 3)
Create accompanying script:
extends CanvasLayer
signal confirmed
signal cancelled
@onready var message = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/message
@onready var cancel_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/cancel_btn
@onready var confirm_btn = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/confirm_btn
func _ready():
# Connect signals
cancel_btn.pressed.connect(_on_cancel_pressed)
confirm_btn.pressed.connect(_on_confirm_pressed)
# Set focus
cancel_btn.grab_focus()
# Pop-up animation
$CenterContainer.scale = Vector2(0.8, 0.8)
$CenterContainer.modulate.a = 0
var tween = create_tween()
tween.tween_property($CenterContainer, "scale", Vector2.ONE, 0.2)
tween.parallel().tween_property($CenterContainer, "modulate:a", 1.0, 0.2)
func set_message(text: String):
message.text = text
func _on_confirm_pressed():
confirmed.emit()
queue_free()
func _on_cancel_pressed():
cancelled.emit()
queue_free()
func _input(event):
if event.is_action_pressed("ui_cancel"):
_on_cancel_pressed()
After Creating Template
After creating the selected template:
-
Inform the user of the files created:
- Scene file location
- Script file location
-
Provide next steps:
- "The [template name] has been created at [path]"
- "You can customize the appearance by:"
- Creating a theme resource
- Adjusting colors, fonts, and spacing
- Adding custom textures/icons
- "To use this UI:"
- [Specific usage instructions for the template]
- "For more advanced UI customization, you can invoke the godot-ui skill or ask me for help!"
-
Offer to:
- Create additional related scenes (e.g., "Would you like me to create the inventory slot component as well?")
- Set up theme resources
- Add more features to the template