feat(studio): Add Quick Start Guide, Viewport HUD, 3D raycast picking, quick add toolbar, and starter Baseplate
checkpoint / checkpoint (push) Successful in 6m20s

This commit is contained in:
2026-09-22 20:11:02 +00:00
parent 721dbe5ae7
commit 3f0ba543e1
7 changed files with 633 additions and 47 deletions
+221
View File
@@ -0,0 +1,221 @@
class_name StudioHelpDialog
extends PanelContainer
## Interactive Welcome, Quick Start Guide & Controls Cheatsheet for ModBison Studio.
signal closed
const SETTINGS_PATH := "user://studio_settings.json"
var _check_show_on_start: CheckBox
func _init() -> void:
name = "StudioHelpDialog"
custom_minimum_size = Vector2(720, 520)
_build_ui()
static func should_show_on_startup() -> bool:
if not FileAccess.file_exists(SETTINGS_PATH):
return true
var fa := FileAccess.open(SETTINGS_PATH, FileAccess.READ)
if fa == null:
return true
var parsed = JSON.parse_string(fa.get_as_text())
fa.close()
if parsed is Dictionary and parsed.has("show_help_on_startup"):
return bool(parsed["show_help_on_startup"])
return true
static func save_show_on_startup(show_help: bool) -> void:
var data := {"show_help_on_startup": show_help}
var fa := FileAccess.open(SETTINGS_PATH, FileAccess.WRITE)
if fa != null:
fa.store_string(JSON.stringify(data))
fa.close()
func _build_ui() -> void:
# Style panel
var style := StyleBoxFlat.new()
style.bg_color = Color(0.14, 0.16, 0.20, 0.98)
style.border_color = Color(0.9, 0.75, 0.25)
style.set_border_width_all(2)
style.set_corner_radius_all(8)
style.content_margin_left = 24
style.content_margin_top = 20
style.content_margin_right = 24
style.content_margin_bottom = 20
add_theme_stylebox_override("panel", style)
var root_vbox := VBoxContainer.new()
root_vbox.add_theme_constant_override("separation", 14)
add_child(root_vbox)
# Header Row
var header_hbox := HBoxContainer.new()
var title_lbl := Label.new()
title_lbl.text = "🦬 Welcome to ModBison Studio!"
title_lbl.add_theme_font_size_override("font_size", 20)
title_lbl.add_theme_color_override("font_color", Color(1.0, 0.88, 0.35))
header_hbox.add_child(title_lbl)
var spacer := Control.new()
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
header_hbox.add_child(spacer)
var close_x := Button.new()
close_x.text = "✕"
close_x.custom_minimum_size = Vector2(32, 28)
close_x.pressed.connect(_on_close_pressed)
header_hbox.add_child(close_x)
root_vbox.add_child(header_hbox)
var subtitle := Label.new()
subtitle.text = "Your creator workbench for designing 3D worlds, placing interactive props, and playtesting in real-time."
subtitle.add_theme_font_size_override("font_size", 12)
subtitle.add_theme_color_override("font_color", Color(0.75, 0.78, 0.82))
root_vbox.add_child(subtitle)
# Tab Container for Guide & Shortcuts
var tabs := TabContainer.new()
tabs.size_flags_vertical = Control.SIZE_EXPAND_FILL
# Tab 1: Quick Start Guide
var guide_scroll := ScrollContainer.new()
guide_scroll.name = "Quick Start Guide"
guide_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
var guide_content := VBoxContainer.new()
guide_content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
guide_content.add_theme_constant_override("separation", 14)
_add_guide_step(guide_content, "🎥 1. Navigate the 3D World",
"• Fly Camera: Hold Right Mouse Button (RMB) + W, A, S, D, Q, E\n" +
"• Orbit Camera: Hold Middle Mouse Button (MMB) and move mouse\n" +
"• Zoom: Scroll mouse wheel up/down\n" +
"• Focus View: Press [F] to instantly center and zoom onto the selected object")
_add_guide_step(guide_content, "🖱️ 2. Select & Transform Props",
"• Click any 3D model directly in the viewport (or in the Scene Hierarchy on the left)\n" +
"• Switch transformation tools via top Toolbar or hotkeys:\n" +
" [1] ↖ Select Mode [2] ✛ Move (drag arrows) [3] ↻ Rotate [4] ⤢ Scale\n" +
"• Snapping: Set grid snap step (0.5m, 1.0m, etc.) from the Toolbar snap box\n" +
"• Duplicate: Press [Ctrl + D] to duplicate, [Delete] to remove")
_add_guide_step(guide_content, "📦 3. Add Models & Stacking",
"• Quick Insert: Click [+ Bison], [+ Crate], [+ Tree], or [+ Lamp] on the top toolbar\n" +
"• Collections Dock: Open the Collections tab on the right to browse theme packs and drag & drop models directly onto the ground!\n" +
"• Object Stacking: Place items directly on top of other items — Studio automatically snaps surfaces so they stick together!")
_add_guide_step(guide_content, "▶️ 4. Playtest Your World (F5)",
"• Hit [F5] or click the green '▶ Play' button on the toolbar\n" +
"• Your player avatar spawns immediately with full walking, jumping, and riding physics!\n" +
"• Hit [F5] again or click '⏹ Stop' to cleanly return to Edit mode (all objects roll back to their original positions)")
_add_guide_step(guide_content, "💾 5. Save & Return to Game",
"• Save: Press [Ctrl + S] or File -> Save Project\n" +
"• Return: Click '🏠 Main Menu' in the top-right corner to return to the game menu anytime")
guide_scroll.add_child(guide_content)
tabs.add_child(guide_scroll)
# Tab 2: Keyboard & Mouse Reference
var keys_scroll := ScrollContainer.new()
keys_scroll.name = "Shortcuts & Controls"
keys_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
var keys_vbox := VBoxContainer.new()
keys_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
keys_vbox.add_theme_constant_override("separation", 10)
_add_shortcut_row(keys_vbox, "RMB + WASDQE", "Fly camera through scene (Hold Shift to fly faster, Ctrl for precision)")
_add_shortcut_row(keys_vbox, "MMB + Drag", "Pan camera across scene")
_add_shortcut_row(keys_vbox, "Mouse Wheel", "Zoom camera in / out")
_add_shortcut_row(keys_vbox, "LMB (Left Click)", "Select object in 3D viewport or click gizmo handles")
_add_shortcut_row(keys_vbox, "F Key", "Focus camera on selected object")
_add_shortcut_row(keys_vbox, "1, 2, 3, 4 Keys", "Switch tool: [1] Select, [2] Move, [3] Rotate, [4] Scale")
_add_shortcut_row(keys_vbox, "7, 1, 3 Keys", "View Presets: [7] Top Ortho, [1] Front Ortho, [3] Side Ortho")
_add_shortcut_row(keys_vbox, "5 Key", "Toggle Perspective / Orthographic projection")
_add_shortcut_row(keys_vbox, "F5 Key", "Toggle Playtest (Play Solo / Stop Playtest)")
_add_shortcut_row(keys_vbox, "Ctrl + Z / Ctrl + Y", "Undo / Redo world modifications")
_add_shortcut_row(keys_vbox, "Ctrl + D", "Duplicate selected object")
_add_shortcut_row(keys_vbox, "Delete / Backspace", "Delete selected object")
_add_shortcut_row(keys_vbox, "Ctrl + S", "Save project to disk (.mbworld)")
_add_shortcut_row(keys_vbox, "F1 Key", "Toggle this Help & Controls guide")
keys_scroll.add_child(keys_vbox)
tabs.add_child(keys_scroll)
root_vbox.add_child(tabs)
# Footer Row
var footer_hbox := HBoxContainer.new()
_check_show_on_start = CheckBox.new()
_check_show_on_start.text = "Show this guide when opening Studio"
_check_show_on_start.button_pressed = should_show_on_startup()
_check_show_on_start.toggled.connect(func(val): save_show_on_startup(val))
footer_hbox.add_child(_check_show_on_start)
var f_spacer := Control.new()
f_spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
footer_hbox.add_child(f_spacer)
var btn_close := Button.new()
btn_close.text = "✨ Let's Build! (Close)"
btn_close.add_theme_color_override("font_color", Color(1.0, 0.9, 0.35))
btn_close.custom_minimum_size = Vector2(160, 34)
btn_close.pressed.connect(_on_close_pressed)
footer_hbox.add_child(btn_close)
root_vbox.add_child(footer_hbox)
func _add_guide_step(parent: VBoxContainer, title: String, text: String) -> void:
var box := VBoxContainer.new()
var t := Label.new()
t.text = title
t.add_theme_font_size_override("font_size", 13)
t.add_theme_color_override("font_color", Color(0.35, 0.85, 1.0))
box.add_child(t)
var b := Label.new()
b.text = text
b.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
b.add_theme_font_size_override("font_size", 11)
b.add_theme_color_override("font_color", Color(0.85, 0.87, 0.9))
box.add_child(b)
parent.add_child(box)
func _add_shortcut_row(parent: VBoxContainer, key: String, desc: String) -> void:
var row := HBoxContainer.new()
var k_lbl := Label.new()
k_lbl.text = key
k_lbl.custom_minimum_size = Vector2(180, 0)
k_lbl.add_theme_font_size_override("font_size", 11)
k_lbl.add_theme_color_override("font_color", Color(1.0, 0.85, 0.4))
row.add_child(k_lbl)
var d_lbl := Label.new()
d_lbl.text = desc
d_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
d_lbl.add_theme_font_size_override("font_size", 11)
d_lbl.add_theme_color_override("font_color", Color(0.82, 0.85, 0.88))
row.add_child(d_lbl)
parent.add_child(row)
func _on_close_pressed() -> void:
visible = false
closed.emit()
func _unhandled_input(event: InputEvent) -> void:
if not visible:
return
if event is InputEventKey:
var k := event as InputEventKey
if k.pressed and not k.echo:
if k.keycode == KEY_ESCAPE or k.keycode == KEY_F1:
_on_close_pressed()
get_viewport().set_input_as_handled()