feat(studio): Add Quick Start Guide, Viewport HUD, 3D raycast picking, quick add toolbar, and starter Baseplate
checkpoint / checkpoint (push) Successful in 6m20s
checkpoint / checkpoint (push) Successful in 6m20s
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user