Initial commit
This commit is contained in:
347
commands/debug.md
Normal file
347
commands/debug.md
Normal file
@@ -0,0 +1,347 @@
|
||||
---
|
||||
allowed_tools:
|
||||
- mcp__godot__get_debug_output
|
||||
- Skill
|
||||
---
|
||||
|
||||
View enhanced debug output from the running Godot project with error highlighting and filtering.
|
||||
|
||||
# Process
|
||||
|
||||
1. **Check if project is running**
|
||||
- Use mcp__godot__get_debug_output to fetch current debug output
|
||||
|
||||
2. **Parse and enhance the output:**
|
||||
- Identify error types (ERROR, WARNING, INFO)
|
||||
- Parse stack traces
|
||||
- Extract file paths and line numbers
|
||||
- Highlight important information
|
||||
|
||||
3. **Display formatted output:**
|
||||
|
||||
## Error Formatting
|
||||
|
||||
Present errors with the following enhanced format:
|
||||
|
||||
### ERROR messages
|
||||
```
|
||||
[ERROR] <error_message>
|
||||
Location: <file_path>:<line_number>
|
||||
Function: <function_name>
|
||||
Details: <additional_context>
|
||||
|
||||
Stack Trace:
|
||||
at <function> (<file>:<line>)
|
||||
at <function> (<file>:<line>)
|
||||
```
|
||||
|
||||
### WARNING messages
|
||||
```
|
||||
[WARNING] <warning_message>
|
||||
Location: <file_path>:<line_number>
|
||||
Context: <relevant_context>
|
||||
```
|
||||
|
||||
### INFO/DEBUG messages
|
||||
```
|
||||
[INFO] <message>
|
||||
```
|
||||
|
||||
## Common Error Patterns to Recognize
|
||||
|
||||
### 1. Null Reference Errors
|
||||
Pattern: `Attempt to call function .* in base 'null instance'`
|
||||
|
||||
Response:
|
||||
```
|
||||
[ERROR] Null Reference Error
|
||||
Trying to call a method on a null object
|
||||
|
||||
Quick Fixes:
|
||||
- Check if the object exists before calling methods
|
||||
- Verify @onready node paths are correct
|
||||
- Ensure nodes aren't freed before being accessed
|
||||
|
||||
Example fix:
|
||||
if my_node != null:
|
||||
my_node.some_method()
|
||||
```
|
||||
|
||||
### 2. Node Not Found
|
||||
Pattern: `Node not found: .*` or `get_node: .*not found`
|
||||
|
||||
Response:
|
||||
```
|
||||
[ERROR] Node Not Found
|
||||
Path: <node_path>
|
||||
|
||||
Possible Causes:
|
||||
- Node doesn't exist in the scene tree
|
||||
- Incorrect node path (check capitalization)
|
||||
- Node hasn't been added yet (timing issue)
|
||||
|
||||
Quick Fixes:
|
||||
- Use get_node_or_null() to check if node exists
|
||||
- Verify the node path in the scene tree
|
||||
- Use @onready for scene tree references
|
||||
```
|
||||
|
||||
### 3. Index Out of Range
|
||||
Pattern: `Index .* out of range`
|
||||
|
||||
Response:
|
||||
```
|
||||
[ERROR] Array Index Out of Bounds
|
||||
Index: <index_value>
|
||||
Array Size: <size>
|
||||
|
||||
Quick Fixes:
|
||||
- Check array size before accessing: if index < array.size()
|
||||
- Use range-based loops instead of index access
|
||||
- Validate index is not negative
|
||||
```
|
||||
|
||||
### 4. Type Errors
|
||||
Pattern: `Invalid operands .* and .* in operator`
|
||||
|
||||
Response:
|
||||
```
|
||||
[ERROR] Type Mismatch
|
||||
Cannot perform operation between incompatible types
|
||||
|
||||
Quick Fixes:
|
||||
- Ensure variables are initialized
|
||||
- Check for null values before operations
|
||||
- Verify type annotations match actual types
|
||||
```
|
||||
|
||||
### 5. Signal Errors
|
||||
Pattern: `Signal .* already connected` or `Attempt to call an invalid function`
|
||||
|
||||
Response:
|
||||
```
|
||||
[ERROR] Signal Connection Error
|
||||
|
||||
Quick Fixes:
|
||||
- Check if signal is already connected before connecting
|
||||
- Verify method signature matches signal parameters
|
||||
- Ensure method exists and is spelled correctly
|
||||
```
|
||||
|
||||
## Enhanced Features
|
||||
|
||||
### Error Grouping
|
||||
- Group repeated errors together
|
||||
- Show count of occurrences
|
||||
- Display first occurrence time
|
||||
|
||||
Example:
|
||||
```
|
||||
[ERROR] (x5) Null instance access
|
||||
First occurred at: 14:32:15
|
||||
Last occurred at: 14:32:23
|
||||
|
||||
See details with: /gd:debug --expand=<error_id>
|
||||
```
|
||||
|
||||
### Filtering Options
|
||||
|
||||
Ask user if they want to filter output:
|
||||
|
||||
Question: "Would you like to filter the debug output?"
|
||||
Header: "Filter"
|
||||
Multi-select: false
|
||||
Options:
|
||||
- Show All: Display all messages (errors, warnings, info)
|
||||
- Errors Only: Show only ERROR messages
|
||||
- Errors + Warnings: Show errors and warnings
|
||||
- Custom Filter: Filter by keyword
|
||||
|
||||
### Auto-suggestions
|
||||
|
||||
For each error, provide:
|
||||
1. **Error Type**: Classification of the error
|
||||
2. **Likely Cause**: Most common reason for this error
|
||||
3. **Suggested Fix**: Code example or action to take
|
||||
4. **Related Documentation**: Link to relevant Godot docs if applicable
|
||||
|
||||
Example output:
|
||||
```
|
||||
─────────────────────────────────────
|
||||
[ERROR] Invalid get index 'health' (on base: 'null instance')
|
||||
|
||||
Location: res://scripts/player.gd:45
|
||||
Function: take_damage()
|
||||
|
||||
Likely Cause:
|
||||
The player node is null when trying to access the 'health' property.
|
||||
This usually happens when:
|
||||
- A node reference (@onready var) points to a non-existent node
|
||||
- The node was freed/removed before this code runs
|
||||
- The node path is incorrect
|
||||
|
||||
Suggested Fix:
|
||||
# Add null check before accessing:
|
||||
if player != null:
|
||||
player.health -= damage
|
||||
|
||||
# Or verify node exists in _ready():
|
||||
func _ready():
|
||||
assert(player != null, "Player node not found!")
|
||||
|
||||
Related Code (player.gd:45):
|
||||
45 | func take_damage(amount):
|
||||
46 | player.health -= amount # ERROR HERE
|
||||
47 | update_health_bar()
|
||||
|
||||
Next Steps:
|
||||
1. Check that the player node exists in the scene tree
|
||||
2. Verify the node path for @onready variables
|
||||
3. Add null checks before accessing node properties
|
||||
|
||||
─────────────────────────────────────
|
||||
```
|
||||
|
||||
## Watch Mode
|
||||
|
||||
Offer to enable watch mode:
|
||||
|
||||
"Would you like to enable watch mode? This will continuously monitor for new errors."
|
||||
|
||||
If yes:
|
||||
- Poll mcp__godot__get_debug_output every 2 seconds
|
||||
- Display new errors as they occur
|
||||
- Highlight new errors in real-time
|
||||
- User can press Ctrl+C or type 'stop' to exit watch mode
|
||||
|
||||
Example watch mode output:
|
||||
```
|
||||
[WATCH MODE] Monitoring debug output... (Press Ctrl+C to stop)
|
||||
|
||||
14:35:12 | [INFO] Game started
|
||||
14:35:15 | [WARNING] Texture not found: res://sprites/missing.png
|
||||
14:35:18 | [ERROR] Null instance access in enemy.gd:23
|
||||
|
||||
Press Enter to see error details, or type 'stop' to exit...
|
||||
```
|
||||
|
||||
## Additional Commands
|
||||
|
||||
After showing debug output, offer helpful commands:
|
||||
|
||||
```
|
||||
Debug output retrieved.
|
||||
|
||||
Helpful commands:
|
||||
/gd:debug --errors-only Show only errors
|
||||
/gd:debug --watch Enable watch mode
|
||||
/gd:restart Restart the game
|
||||
|
||||
Need help fixing an error? Just ask! The godot-debugging skill can help interpret and fix these errors.
|
||||
```
|
||||
|
||||
# Integration with godot-debugging Skill
|
||||
|
||||
After displaying errors, automatically activate the godot-debugging skill to provide:
|
||||
- Detailed error explanations
|
||||
- Code fixes
|
||||
- Prevention strategies
|
||||
|
||||
# Example Full Output
|
||||
|
||||
```
|
||||
Debug Output (Last 50 lines)
|
||||
═══════════════════════════════════════
|
||||
|
||||
[INFO] Project initialized
|
||||
[INFO] Scene loaded: res://scenes/main.tscn
|
||||
|
||||
[WARNING] Node has been removed from scene: Label
|
||||
Location: res://scenes/ui/menu.tscn
|
||||
Context: Attempting to access removed node
|
||||
|
||||
Note: This usually happens when you call queue_free() on a node
|
||||
and then try to access it later in the same frame.
|
||||
|
||||
─────────────────────────────────────
|
||||
|
||||
[ERROR] Attempt to call function 'move_and_slide' in base 'null instance' on a null instance
|
||||
Location: res://scripts/player.gd:67
|
||||
Function: _physics_process(delta)
|
||||
|
||||
Stack Trace:
|
||||
at CharacterBody2D._physics_process (res://scripts/player.gd:67)
|
||||
at Node._propagate_process (core)
|
||||
|
||||
🔍 Analysis:
|
||||
The player node (CharacterBody2D) is trying to call move_and_slide()
|
||||
but the instance is null. This typically means:
|
||||
- The player was freed but physics_process is still running
|
||||
- The script is on a freed node
|
||||
|
||||
💡 Suggested Fix:
|
||||
func _physics_process(delta):
|
||||
if not is_instance_valid(self):
|
||||
return
|
||||
|
||||
velocity.y += gravity * delta
|
||||
move_and_slide()
|
||||
|
||||
─────────────────────────────────────
|
||||
|
||||
Summary:
|
||||
• Total Messages: 24
|
||||
• Errors: 3 (🔴)
|
||||
• Warnings: 5 (🟡)
|
||||
• Info: 16 (🔵)
|
||||
|
||||
Most Recent Error:
|
||||
res://scripts/player.gd:67 - Null instance in _physics_process
|
||||
|
||||
═══════════════════════════════════════
|
||||
|
||||
Would you like me to help fix any of these errors?
|
||||
```
|
||||
|
||||
# Error History Tracking
|
||||
|
||||
Keep track of errors across debug sessions:
|
||||
- Store error fingerprints (file + line + error type)
|
||||
- Track if error is new or recurring
|
||||
- Show resolution history if previously fixed
|
||||
|
||||
Example:
|
||||
```
|
||||
[ERROR] (Recurring) Null instance access
|
||||
This error has occurred 3 times in the last hour
|
||||
Previously seen: 14:15, 14:22, 14:35
|
||||
|
||||
Note: This might indicate a deeper issue with your code structure.
|
||||
Consider adding more robust null checks or restructuring your node references.
|
||||
```
|
||||
|
||||
# Implementation Notes
|
||||
|
||||
1. **Parse debug output intelligently**
|
||||
- Extract file paths as clickable links (if terminal supports it)
|
||||
- Parse stack traces into structured format
|
||||
- Identify error patterns automatically
|
||||
|
||||
2. **Color coding** (if terminal supports it)
|
||||
- Red for errors
|
||||
- Yellow for warnings
|
||||
- Blue for info
|
||||
- Gray for debug
|
||||
|
||||
3. **Smart filtering**
|
||||
- Remember user's last filter choice
|
||||
- Allow regex patterns for advanced filtering
|
||||
- Group similar errors together
|
||||
|
||||
4. **Quick actions**
|
||||
- Offer to open file at error line
|
||||
- Suggest running godot-debugging skill
|
||||
- Provide quick fix options
|
||||
|
||||
After displaying the enhanced debug output, remind the user:
|
||||
"💡 Tip: I can help fix these errors! Just ask about any specific error and I'll provide a detailed explanation and fix using the godot-debugging skill."
|
||||
28
commands/init-game.md
Normal file
28
commands/init-game.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
description: Initialize a new Godot game project with interactive planning
|
||||
allowed-tools:
|
||||
- Task
|
||||
- Write
|
||||
- Read
|
||||
- mcp__godot__*
|
||||
---
|
||||
|
||||
You are helping initialize a new Godot game project.
|
||||
|
||||
First, launch the game-planner agent to gather requirements and create a game plan:
|
||||
|
||||
Use the Task tool with subagent_type "game-planner" to launch the planning agent.
|
||||
|
||||
After the planning agent completes, use the returned game plan to:
|
||||
1. Update or create the project.godot file with appropriate settings (2D/3D, display settings, etc.)
|
||||
2. Create the main scene based on the game type (Node2D for 2D games, Node3D for 3D games)
|
||||
3. Set up the basic folder structure:
|
||||
- scenes/ (for game scenes)
|
||||
- scripts/ (for GDScript files)
|
||||
- assets/ (for images, sounds, etc.)
|
||||
- resources/ (for Godot resource files)
|
||||
4. Create a README.md documenting the project plan, core mechanics, and initial architecture
|
||||
|
||||
Use the Godot MCP tools to create scenes and add nodes as appropriate based on the game plan.
|
||||
|
||||
Inform the user when initialization is complete and suggest next steps for development.
|
||||
141
commands/restart.md
Normal file
141
commands/restart.md
Normal file
@@ -0,0 +1,141 @@
|
||||
---
|
||||
description: Quickly restart the running Godot game
|
||||
allowed_tools:
|
||||
- mcp__godot__stop_project
|
||||
- mcp__godot__run_project
|
||||
- mcp__godot__get_debug_output
|
||||
---
|
||||
|
||||
Quickly restart the running Godot project for fast iteration during development.
|
||||
|
||||
# Process
|
||||
|
||||
1. **Stop the current instance**
|
||||
- Use mcp__godot__stop_project to stop the running game
|
||||
- Display: `♻️ Restarting game...`
|
||||
|
||||
2. **Wait briefly** (0.5 seconds) for clean shutdown
|
||||
|
||||
3. **Start the project again**
|
||||
- Use mcp__godot__run_project with projectPath set to current working directory
|
||||
|
||||
4. **Get initial output**
|
||||
- Wait 1.5 seconds
|
||||
- Use mcp__godot__get_debug_output to check for errors
|
||||
|
||||
5. **Display result**
|
||||
|
||||
## If restart successful:
|
||||
```
|
||||
✓ Game restarted successfully!
|
||||
|
||||
Quick stats:
|
||||
• Restart time: <elapsed_time>
|
||||
• Status: Running
|
||||
|
||||
Commands:
|
||||
/gd:restart Restart again
|
||||
/gd:debug View debug output
|
||||
/gd:stop Stop the game
|
||||
|
||||
💡 Tip: Keep this workflow for rapid iteration:
|
||||
Edit code → Save → /gd:restart → Test
|
||||
```
|
||||
|
||||
## If errors occurred:
|
||||
```
|
||||
⚠ Game restarted with errors:
|
||||
|
||||
<formatted_errors>
|
||||
|
||||
Use /gd:debug for detailed error information
|
||||
|
||||
Commands:
|
||||
/gd:debug View full debug output
|
||||
/gd:stop Stop the game
|
||||
|
||||
Would you like help fixing these errors?
|
||||
```
|
||||
|
||||
## If no game was running:
|
||||
```
|
||||
ℹ No game was running. Starting fresh...
|
||||
|
||||
<use same output as /gd:run>
|
||||
```
|
||||
|
||||
# Usage Tips
|
||||
|
||||
Display these tips the first time user runs /gd:restart:
|
||||
|
||||
```
|
||||
💡 Restart Command Tips:
|
||||
|
||||
1. Fast Iteration: Use /gd:restart after making code changes
|
||||
instead of manually stopping and starting the game.
|
||||
|
||||
2. Keyboard Shortcut: Save this command for quick access
|
||||
(check if your terminal supports command aliases)
|
||||
|
||||
3. Watch Your Console: Errors from the restart will be
|
||||
displayed immediately.
|
||||
|
||||
4. No Need to Stop First: /gd:restart handles stopping
|
||||
automatically.
|
||||
|
||||
Typical workflow:
|
||||
1. Make changes to your code
|
||||
2. Save (Cmd+S / Ctrl+S)
|
||||
3. Run /gd:restart
|
||||
4. Test your changes
|
||||
5. Repeat!
|
||||
```
|
||||
|
||||
# Performance Optimization
|
||||
|
||||
Track restart metrics for user feedback:
|
||||
```
|
||||
📊 Restart Performance:
|
||||
• This restart: 2.1s
|
||||
• Average: 2.3s
|
||||
• Fastest: 1.8s
|
||||
|
||||
Your game restarts quickly! This is great for iteration.
|
||||
```
|
||||
|
||||
# Error Recovery
|
||||
|
||||
If restart fails:
|
||||
```
|
||||
❌ Restart failed!
|
||||
|
||||
Possible causes:
|
||||
- Previous instance didn't shut down cleanly
|
||||
- Godot editor is not responding
|
||||
- MCP server connection issue
|
||||
|
||||
Try these steps:
|
||||
1. Wait a few seconds and try again
|
||||
2. Check if Godot editor is running
|
||||
3. Run /gd:setup to verify MCP configuration
|
||||
|
||||
Or manually:
|
||||
1. /gd:stop (ensure game stops)
|
||||
2. /gd:run (start fresh)
|
||||
```
|
||||
|
||||
# Integration with Error Monitoring
|
||||
|
||||
After restart, if errors detected, activate godot-debugging skill hints:
|
||||
```
|
||||
🔍 Detected issues in restart:
|
||||
|
||||
[ERROR] Null instance in player.gd:45
|
||||
|
||||
Common cause: Node references changed or removed
|
||||
|
||||
Quick fix: Verify all @onready var paths are correct
|
||||
after scene modifications.
|
||||
|
||||
Want detailed help? Just ask about this error!
|
||||
```
|
||||
339
commands/run.md
Normal file
339
commands/run.md
Normal file
@@ -0,0 +1,339 @@
|
||||
---
|
||||
description: Run the Godot game project with optional watch mode and enhanced error display
|
||||
allowed_tools:
|
||||
- mcp__godot__run_project
|
||||
- mcp__godot__get_debug_output
|
||||
- AskUserQuestion
|
||||
- Skill
|
||||
---
|
||||
|
||||
Run the current Godot project using the MCP server with enhanced debugging features.
|
||||
|
||||
# Process
|
||||
|
||||
## 1. Parse Command Arguments
|
||||
|
||||
Check if user provided any flags:
|
||||
- `--watch` or `-w`: Enable watch mode (auto-restart on file changes)
|
||||
- `--debug` or `-d`: Show detailed debug output immediately
|
||||
- `--no-output`: Run without showing initial output
|
||||
|
||||
Examples:
|
||||
- `/gd:run` - Normal run
|
||||
- `/gd:run --watch` - Run with watch mode
|
||||
- `/gd:run --debug` - Run with immediate debug output
|
||||
|
||||
## 2. Start the Project
|
||||
|
||||
Use mcp__godot__run_project with projectPath set to the current working directory.
|
||||
|
||||
Display:
|
||||
```
|
||||
🎮 Starting Godot project...
|
||||
Project: <current_directory>
|
||||
```
|
||||
|
||||
## 3. Get Initial Output
|
||||
|
||||
Wait 2 seconds, then use mcp__godot__get_debug_output to fetch initial output.
|
||||
|
||||
## 4. Parse and Display Output
|
||||
|
||||
Parse the output for:
|
||||
- **Errors** (lines containing "ERROR", "Error:", or stack traces)
|
||||
- **Warnings** (lines containing "WARNING", "WARN")
|
||||
- **Info** (other output)
|
||||
|
||||
Display with enhanced formatting:
|
||||
|
||||
### If No Errors:
|
||||
```
|
||||
✓ Game started successfully!
|
||||
|
||||
Output:
|
||||
<formatted_output>
|
||||
|
||||
Commands:
|
||||
/gd:stop Stop the running game
|
||||
/gd:debug View enhanced debug output
|
||||
/gd:restart Quick restart
|
||||
|
||||
Tip: The game is now running. Make changes to your code and use /gd:restart to quickly reload!
|
||||
```
|
||||
|
||||
### If Errors Found:
|
||||
```
|
||||
⚠ Game started with errors:
|
||||
|
||||
<formatted_errors>
|
||||
|
||||
Commands:
|
||||
/gd:debug View detailed error information
|
||||
/gd:stop Stop the game
|
||||
|
||||
Would you like me to help fix these errors? I can explain what's wrong and suggest solutions.
|
||||
```
|
||||
|
||||
## 5. Error Highlighting
|
||||
|
||||
When displaying errors, highlight key information:
|
||||
|
||||
```
|
||||
[ERROR] <error_type>
|
||||
File: <file_path>:<line_number>
|
||||
Message: <error_message>
|
||||
|
||||
💡 Quick Tip: <brief_suggestion>
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
[ERROR] Null Instance
|
||||
File: res://scripts/player.gd:45
|
||||
Message: Attempt to call function 'take_damage' in base 'null instance' on a null instance
|
||||
|
||||
💡 Quick Tip: Add null check before calling methods: if node != null: node.method()
|
||||
```
|
||||
|
||||
```
|
||||
[WARNING] Resource Not Found
|
||||
File: res://scenes/level.tscn
|
||||
Message: Cannot load resource at path: 'res://sprites/missing.png'
|
||||
|
||||
💡 Quick Tip: Check that the file exists and the path is correct (case-sensitive)
|
||||
```
|
||||
|
||||
## 6. Watch Mode (if --watch flag provided)
|
||||
|
||||
If watch mode is enabled:
|
||||
|
||||
1. Display:
|
||||
```
|
||||
📁 Watch mode enabled
|
||||
Monitoring for file changes in:
|
||||
- *.gd (GDScript files)
|
||||
- *.tscn (Scene files)
|
||||
- *.tres (Resource files)
|
||||
|
||||
The game will auto-restart when you save changes.
|
||||
Press Ctrl+C or type 'stop' to exit watch mode.
|
||||
```
|
||||
|
||||
2. Set up file watching (conceptual - explain to user):
|
||||
```
|
||||
Note: Watch mode requires manual restart for now.
|
||||
|
||||
To enable auto-restart:
|
||||
1. Make your code changes
|
||||
2. Use /gd:restart to quickly reload
|
||||
3. Repeat as needed
|
||||
|
||||
Future enhancement: Automatic file watching with instant reload.
|
||||
```
|
||||
|
||||
3. Provide workflow tips:
|
||||
```
|
||||
💡 Rapid Iteration Workflow:
|
||||
1. Edit your code
|
||||
2. Save the file (Ctrl+S / Cmd+S)
|
||||
3. Run /gd:restart
|
||||
4. See changes immediately!
|
||||
|
||||
This is much faster than closing and reopening the game.
|
||||
```
|
||||
|
||||
## 7. Error Count Summary
|
||||
|
||||
After initial run, provide summary:
|
||||
```
|
||||
═══════════════════════════════════════
|
||||
Status Summary
|
||||
═══════════════════════════════════════
|
||||
🔴 Errors: <count>
|
||||
🟡 Warnings: <count>
|
||||
🔵 Info messages: <count>
|
||||
|
||||
⏱ Startup time: <elapsed_time>
|
||||
═══════════════════════════════════════
|
||||
```
|
||||
|
||||
## 8. Offer Help
|
||||
|
||||
Based on what was found:
|
||||
|
||||
### If errors present:
|
||||
```
|
||||
I noticed some errors. Would you like me to:
|
||||
1. Explain what these errors mean
|
||||
2. Show you how to fix them
|
||||
3. Run the debugger for more details
|
||||
|
||||
Just ask! For example: "Help me fix the null instance error"
|
||||
```
|
||||
|
||||
### If warnings only:
|
||||
```
|
||||
There are some warnings you might want to address.
|
||||
Use /gd:debug to see detailed information.
|
||||
```
|
||||
|
||||
### If clean run:
|
||||
```
|
||||
Everything looks good! Your game is running smoothly.
|
||||
```
|
||||
|
||||
## 9. Continuous Monitoring (Enhanced)
|
||||
|
||||
Offer to monitor for new errors:
|
||||
|
||||
Ask user:
|
||||
Question: "Would you like me to monitor for runtime errors?"
|
||||
Header: "Monitoring"
|
||||
Multi-select: false
|
||||
Options:
|
||||
- Yes, watch for errors: I'll alert you if new errors occur
|
||||
- No, just run: Just start the game without monitoring
|
||||
- Debug mode: Show all output in real-time
|
||||
|
||||
If "Yes, watch for errors" selected:
|
||||
```
|
||||
👀 Monitoring enabled
|
||||
I'll alert you if new errors occur while the game is running.
|
||||
|
||||
You can check anytime with /gd:debug
|
||||
```
|
||||
|
||||
Then periodically (conceptually - every 30 seconds or when user asks):
|
||||
- Check for new errors with get_debug_output
|
||||
- If new errors found, alert user:
|
||||
```
|
||||
⚠ New error detected!
|
||||
|
||||
[ERROR] <brief_description>
|
||||
|
||||
Use /gd:debug to see full details
|
||||
```
|
||||
|
||||
## 10. Integration with Debugging Skill
|
||||
|
||||
If errors are found, automatically suggest using the debugging skill:
|
||||
|
||||
```
|
||||
💬 Need help understanding these errors?
|
||||
|
||||
Try asking:
|
||||
- "What does this error mean?"
|
||||
- "How do I fix the null instance error?"
|
||||
- "Debug the player.gd script"
|
||||
|
||||
I can explain and fix these issues for you!
|
||||
```
|
||||
|
||||
## 11. Performance Notes
|
||||
|
||||
Display performance indicators if available:
|
||||
```
|
||||
⚡ Performance:
|
||||
FPS: <if_available>
|
||||
Memory: <if_available>
|
||||
|
||||
Use /gd:profile for detailed performance analysis
|
||||
```
|
||||
|
||||
## 12. Quick Actions
|
||||
|
||||
At the end, always show quick actions:
|
||||
```
|
||||
Quick Commands:
|
||||
/gd:stop Stop the game
|
||||
/gd:restart Restart quickly
|
||||
/gd:debug Detailed debug view
|
||||
/gd:profile Performance analysis
|
||||
|
||||
💡 Press Tab to see all /gd: commands
|
||||
```
|
||||
|
||||
# Example Full Output
|
||||
|
||||
## Success Case:
|
||||
```
|
||||
🎮 Starting Godot project...
|
||||
Project: /Users/user/games/platformer
|
||||
|
||||
✓ Game started successfully!
|
||||
|
||||
Output:
|
||||
Godot Engine v4.2.1 - https://godotengine.org
|
||||
Vulkan API 1.3.0 - Device: Apple M1
|
||||
|
||||
Loading scene: res://scenes/main.tscn
|
||||
Player initialized
|
||||
Level loaded: Level_1
|
||||
|
||||
═══════════════════════════════════════
|
||||
Status Summary
|
||||
═══════════════════════════════════════
|
||||
🔴 Errors: 0
|
||||
🟡 Warnings: 0
|
||||
🔵 Info messages: 4
|
||||
|
||||
⏱ Startup time: 1.2s
|
||||
═══════════════════════════════════════
|
||||
|
||||
Everything looks good! Your game is running smoothly.
|
||||
|
||||
Commands:
|
||||
/gd:stop Stop the running game
|
||||
/gd:debug View debug output
|
||||
/gd:restart Quick restart
|
||||
|
||||
Tip: Make changes to your code and use /gd:restart to quickly reload!
|
||||
```
|
||||
|
||||
## Error Case:
|
||||
```
|
||||
🎮 Starting Godot project...
|
||||
Project: /Users/user/games/platformer
|
||||
|
||||
⚠ Game started with errors:
|
||||
|
||||
[ERROR] Null Instance
|
||||
File: res://scripts/player.gd:45
|
||||
Message: Attempt to call function 'take_damage' in base 'null instance'
|
||||
|
||||
💡 Quick Tip: Add null check: if player != null: player.take_damage(10)
|
||||
|
||||
[WARNING] Resource Not Found
|
||||
File: res://scenes/level.tscn
|
||||
Message: Cannot load resource: 'res://sprites/enemy.png'
|
||||
|
||||
💡 Quick Tip: Verify the file path is correct and the file exists
|
||||
|
||||
═══════════════════════════════════════
|
||||
Status Summary
|
||||
═══════════════════════════════════════
|
||||
🔴 Errors: 1
|
||||
🟡 Warnings: 1
|
||||
🔵 Info messages: 3
|
||||
|
||||
⏱ Startup time: 1.5s
|
||||
═══════════════════════════════════════
|
||||
|
||||
💬 Need help fixing these errors?
|
||||
|
||||
I can explain what went wrong and show you how to fix it.
|
||||
Just ask: "Help me fix the null instance error"
|
||||
|
||||
Commands:
|
||||
/gd:debug View detailed error information
|
||||
/gd:stop Stop the game
|
||||
```
|
||||
|
||||
# Important Notes
|
||||
|
||||
- Always format output in a user-friendly way
|
||||
- Highlight errors in a visually distinct manner
|
||||
- Provide actionable suggestions, not just error messages
|
||||
- Make it easy to transition to fixing errors (mention debugging skill)
|
||||
- Track context for follow-up questions about specific errors
|
||||
583
commands/scene.md
Normal file
583
commands/scene.md
Normal file
@@ -0,0 +1,583 @@
|
||||
---
|
||||
allowed_tools:
|
||||
- AskUserQuestion
|
||||
- mcp__godot__*
|
||||
- Write
|
||||
- Read
|
||||
---
|
||||
|
||||
Create common scene templates for 2D games, including characters, enemies, levels, and more.
|
||||
|
||||
# Process
|
||||
|
||||
1. **Ask the user what type of scene they want to create:**
|
||||
|
||||
Use AskUserQuestion with the following options:
|
||||
|
||||
Question: "What type of scene would you like to create?"
|
||||
Header: "Scene Type"
|
||||
Multi-select: false
|
||||
Options:
|
||||
- 2D Player Character: Platformer character with movement and collision
|
||||
- 2D Enemy: Basic enemy with AI placeholder
|
||||
- 2D Level: Level scene with tilemap and camera
|
||||
- 2D Projectile: Bullet/projectile with movement
|
||||
- Collectible: Coin/item pickup
|
||||
- Interactable Object: Chest, door, switch, etc.
|
||||
|
||||
Question 2: "Where should the scene be created?"
|
||||
Header: "Location"
|
||||
Multi-select: false
|
||||
Options:
|
||||
- scenes/characters/: For player and NPCs
|
||||
- scenes/enemies/: For enemy characters
|
||||
- scenes/levels/: For level scenes
|
||||
- scenes/objects/: For items and interactables
|
||||
- Custom path: I'll specify the path
|
||||
|
||||
2. **If Custom path selected, ask for the specific path and scene name**
|
||||
|
||||
3. **Create the appropriate template based on selection**
|
||||
|
||||
## 2D Player Character Template
|
||||
|
||||
Create scene with:
|
||||
```
|
||||
CharacterBody2D (root)
|
||||
├── Sprite2D (sprite)
|
||||
├── CollisionShape2D (collision)
|
||||
├── AnimationPlayer (animation_player)
|
||||
└── Camera2D (camera)
|
||||
```
|
||||
|
||||
Node details:
|
||||
- CharacterBody2D: motion_mode = MOTION_MODE_GROUNDED
|
||||
- Sprite2D: centered = true, texture = placeholder (32x32 white rect)
|
||||
- CollisionShape2D: shape = RectangleShape2D (16x32)
|
||||
- Camera2D: enabled = true, position_smoothing_enabled = true
|
||||
|
||||
Create accompanying script:
|
||||
```gdscript
|
||||
extends CharacterBody2D
|
||||
|
||||
# Movement parameters
|
||||
@export var speed: float = 200.0
|
||||
@export var jump_velocity: float = -400.0
|
||||
@export var acceleration: float = 800.0
|
||||
@export var friction: float = 1000.0
|
||||
|
||||
# Get the gravity from the project settings
|
||||
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
|
||||
|
||||
@onready var sprite = $Sprite2D
|
||||
@onready var animation_player = $AnimationPlayer
|
||||
|
||||
func _physics_process(delta):
|
||||
# Add gravity
|
||||
if not is_on_floor():
|
||||
velocity.y += gravity * delta
|
||||
|
||||
# Handle jump
|
||||
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
|
||||
velocity.y = jump_velocity
|
||||
|
||||
# Get input direction
|
||||
var direction = Input.get_axis("ui_left", "ui_right")
|
||||
|
||||
# Apply movement
|
||||
if direction != 0:
|
||||
velocity.x = move_toward(velocity.x, direction * speed, acceleration * delta)
|
||||
|
||||
# Flip sprite based on direction
|
||||
sprite.flip_h = direction < 0
|
||||
|
||||
else:
|
||||
# Apply friction when no input
|
||||
velocity.x = move_toward(velocity.x, 0, friction * delta)
|
||||
|
||||
move_and_slide()
|
||||
|
||||
# Animation logic (requires animations to be set up)
|
||||
_update_animation()
|
||||
|
||||
func _update_animation():
|
||||
if not animation_player:
|
||||
return
|
||||
|
||||
# TODO: Create animations in AnimationPlayer:
|
||||
# - "idle": Standing still
|
||||
# - "run": Running
|
||||
# - "jump": Jumping up
|
||||
# - "fall": Falling down
|
||||
|
||||
# Example animation logic:
|
||||
# if not is_on_floor():
|
||||
# if velocity.y < 0:
|
||||
# animation_player.play("jump")
|
||||
# else:
|
||||
# animation_player.play("fall")
|
||||
# elif abs(velocity.x) > 10:
|
||||
# animation_player.play("run")
|
||||
# else:
|
||||
# animation_player.play("idle")
|
||||
pass
|
||||
```
|
||||
|
||||
## 2D Enemy Template
|
||||
|
||||
Create scene with:
|
||||
```
|
||||
CharacterBody2D (root)
|
||||
├── Sprite2D (sprite)
|
||||
├── CollisionShape2D (collision)
|
||||
├── Area2D (detection_area)
|
||||
│ └── CollisionShape2D (detection_collision)
|
||||
├── AnimationPlayer (animation_player)
|
||||
└── Timer (patrol_timer)
|
||||
```
|
||||
|
||||
Node details:
|
||||
- CharacterBody2D: motion_mode = MOTION_MODE_GROUNDED
|
||||
- Sprite2D: centered = true, modulate = Color(1, 0.5, 0.5) (reddish tint)
|
||||
- CollisionShape2D: shape = RectangleShape2D (16x24)
|
||||
- Area2D/CollisionShape2D: shape = CircleShape2D (radius = 100) for player detection
|
||||
- Timer: wait_time = 2.0, one_shot = false
|
||||
|
||||
Create accompanying script:
|
||||
```gdscript
|
||||
extends CharacterBody2D
|
||||
|
||||
enum State { IDLE, PATROL, CHASE, ATTACK }
|
||||
|
||||
# Movement parameters
|
||||
@export var patrol_speed: float = 50.0
|
||||
@export var chase_speed: float = 150.0
|
||||
@export var detection_range: float = 100.0
|
||||
@export var attack_range: float = 30.0
|
||||
@export var health: int = 100
|
||||
|
||||
var current_state: State = State.IDLE
|
||||
var player: Node2D = null
|
||||
var patrol_direction: int = 1
|
||||
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
|
||||
|
||||
@onready var sprite = $Sprite2D
|
||||
@onready var detection_area = $detection_area
|
||||
@onready var patrol_timer = $patrol_timer
|
||||
|
||||
func _ready():
|
||||
# Connect area signals for player detection
|
||||
detection_area.body_entered.connect(_on_detection_area_entered)
|
||||
detection_area.body_exited.connect(_on_detection_area_exited)
|
||||
|
||||
# Connect patrol timer
|
||||
patrol_timer.timeout.connect(_on_patrol_timer_timeout)
|
||||
patrol_timer.start()
|
||||
|
||||
current_state = State.PATROL
|
||||
|
||||
func _physics_process(delta):
|
||||
# Apply gravity
|
||||
if not is_on_floor():
|
||||
velocity.y += gravity * delta
|
||||
|
||||
# State machine
|
||||
match current_state:
|
||||
State.IDLE:
|
||||
velocity.x = 0
|
||||
|
||||
State.PATROL:
|
||||
velocity.x = patrol_direction * patrol_speed
|
||||
sprite.flip_h = patrol_direction < 0
|
||||
|
||||
# Turn around at ledges or walls
|
||||
if is_on_wall() or not _check_floor_ahead():
|
||||
patrol_direction *= -1
|
||||
|
||||
State.CHASE:
|
||||
if player:
|
||||
var direction = sign(player.global_position.x - global_position.x)
|
||||
velocity.x = direction * chase_speed
|
||||
sprite.flip_h = direction < 0
|
||||
|
||||
# Check if close enough to attack
|
||||
var distance = global_position.distance_to(player.global_position)
|
||||
if distance < attack_range:
|
||||
current_state = State.ATTACK
|
||||
else:
|
||||
current_state = State.PATROL
|
||||
|
||||
State.ATTACK:
|
||||
velocity.x = 0
|
||||
# TODO: Implement attack logic
|
||||
# For now, just return to chase after a moment
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
current_state = State.CHASE
|
||||
|
||||
move_and_slide()
|
||||
|
||||
func _check_floor_ahead() -> bool:
|
||||
# Raycast to check if there's floor ahead
|
||||
var space_state = get_world_2d().direct_space_state
|
||||
var query = PhysicsRayQueryParameters2D.create(
|
||||
global_position,
|
||||
global_position + Vector2(patrol_direction * 20, 30)
|
||||
)
|
||||
var result = space_state.intersect_ray(query)
|
||||
return result.size() > 0
|
||||
|
||||
func _on_detection_area_entered(body):
|
||||
if body.is_in_group("player"):
|
||||
player = body
|
||||
current_state = State.CHASE
|
||||
|
||||
func _on_detection_area_exited(body):
|
||||
if body == player:
|
||||
player = null
|
||||
current_state = State.PATROL
|
||||
|
||||
func _on_patrol_timer_timeout():
|
||||
if current_state == State.IDLE or current_state == State.PATROL:
|
||||
patrol_direction *= -1
|
||||
|
||||
func take_damage(amount: int):
|
||||
health -= amount
|
||||
# TODO: Add damage flash/animation
|
||||
|
||||
if health <= 0:
|
||||
die()
|
||||
|
||||
func die():
|
||||
# TODO: Add death animation
|
||||
queue_free()
|
||||
```
|
||||
|
||||
## 2D Level Template
|
||||
|
||||
Create scene with:
|
||||
```
|
||||
Node2D (root)
|
||||
├── TileMap (tilemap)
|
||||
├── Node2D (spawn_points)
|
||||
│ └── Marker2D (player_spawn)
|
||||
├── Node2D (enemies)
|
||||
├── Node2D (collectibles)
|
||||
├── ParallaxBackground (background)
|
||||
│ └── ParallaxLayer (layer1)
|
||||
│ └── Sprite2D (bg_sprite)
|
||||
└── Camera2D (camera)
|
||||
```
|
||||
|
||||
Node details:
|
||||
- TileMap: tile_set = null (needs to be set by user), layer 0 = "Ground", layer 1 = "Walls"
|
||||
- Camera2D: enabled = true, limit_left = 0, limit_top = 0
|
||||
|
||||
Create accompanying script:
|
||||
```gdscript
|
||||
extends Node2D
|
||||
|
||||
@onready var tilemap = $TileMap
|
||||
@onready var player_spawn = $spawn_points/player_spawn
|
||||
@onready var camera = $Camera2D
|
||||
|
||||
# Preload player scene
|
||||
const PLAYER_SCENE = preload("res://scenes/characters/player.tscn")
|
||||
|
||||
func _ready():
|
||||
# Spawn player
|
||||
spawn_player()
|
||||
|
||||
# Set camera limits based on level bounds
|
||||
_setup_camera_limits()
|
||||
|
||||
func spawn_player():
|
||||
# Instance and add player
|
||||
var player = PLAYER_SCENE.instantiate()
|
||||
player.global_position = player_spawn.global_position
|
||||
add_child(player)
|
||||
|
||||
# Make camera follow player
|
||||
camera.enabled = false # Disable level camera
|
||||
# Player should have its own camera
|
||||
|
||||
func _setup_camera_limits():
|
||||
# Get tilemap bounds
|
||||
var used_rect = tilemap.get_used_rect()
|
||||
var tile_size = tilemap.tile_set.tile_size if tilemap.tile_set else Vector2(16, 16)
|
||||
|
||||
# Set camera limits to level bounds
|
||||
camera.limit_left = used_rect.position.x * tile_size.x
|
||||
camera.limit_top = used_rect.position.y * tile_size.y
|
||||
camera.limit_right = used_rect.end.x * tile_size.x
|
||||
camera.limit_bottom = used_rect.end.y * tile_size.y
|
||||
|
||||
# Helper function to spawn enemies at runtime
|
||||
func spawn_enemy(enemy_scene: PackedScene, position: Vector2):
|
||||
var enemy = enemy_scene.instantiate()
|
||||
enemy.global_position = position
|
||||
$enemies.add_child(enemy)
|
||||
|
||||
# Helper function to spawn collectibles
|
||||
func spawn_collectible(collectible_scene: PackedScene, position: Vector2):
|
||||
var collectible = collectible_scene.instantiate()
|
||||
collectible.global_position = position
|
||||
$collectibles.add_child(collectible)
|
||||
```
|
||||
|
||||
## 2D Projectile Template
|
||||
|
||||
Create scene with:
|
||||
```
|
||||
Area2D (root)
|
||||
├── Sprite2D (sprite)
|
||||
├── CollisionShape2D (collision)
|
||||
└── Timer (lifetime_timer)
|
||||
```
|
||||
|
||||
Node details:
|
||||
- Sprite2D: centered = true, modulate = Color(1, 1, 0) (yellow)
|
||||
- CollisionShape2D: shape = CircleShape2D (radius = 4)
|
||||
- Timer: wait_time = 5.0, one_shot = true, autostart = true
|
||||
|
||||
Create accompanying script:
|
||||
```gdscript
|
||||
extends Area2D
|
||||
|
||||
@export var speed: float = 300.0
|
||||
@export var damage: int = 10
|
||||
@export var lifetime: float = 5.0
|
||||
|
||||
var direction: Vector2 = Vector2.RIGHT
|
||||
|
||||
@onready var sprite = $Sprite2D
|
||||
@onready var lifetime_timer = $lifetime_timer
|
||||
|
||||
func _ready():
|
||||
# Connect signals
|
||||
body_entered.connect(_on_body_entered)
|
||||
area_entered.connect(_on_area_entered)
|
||||
lifetime_timer.timeout.connect(_on_lifetime_timeout)
|
||||
|
||||
# Set lifetime
|
||||
lifetime_timer.wait_time = lifetime
|
||||
|
||||
func _physics_process(delta):
|
||||
# Move in direction
|
||||
position += direction.normalized() * speed * delta
|
||||
|
||||
func set_direction(new_direction: Vector2):
|
||||
direction = new_direction.normalized()
|
||||
|
||||
# Rotate sprite to face direction
|
||||
rotation = direction.angle()
|
||||
|
||||
func _on_body_entered(body):
|
||||
# Hit something solid
|
||||
if body.has_method("take_damage"):
|
||||
body.take_damage(damage)
|
||||
|
||||
# Create hit effect
|
||||
_create_hit_effect()
|
||||
|
||||
queue_free()
|
||||
|
||||
func _on_area_entered(area):
|
||||
# Hit another area (enemy hitbox, etc.)
|
||||
if area.get_parent().has_method("take_damage"):
|
||||
area.get_parent().take_damage(damage)
|
||||
|
||||
# Create hit effect
|
||||
_create_hit_effect()
|
||||
|
||||
queue_free()
|
||||
|
||||
func _on_lifetime_timeout():
|
||||
# Despawn after lifetime expires
|
||||
queue_free()
|
||||
|
||||
func _create_hit_effect():
|
||||
# TODO: Spawn particle effect or animation
|
||||
pass
|
||||
```
|
||||
|
||||
## Collectible Template
|
||||
|
||||
Create scene with:
|
||||
```
|
||||
Area2D (root)
|
||||
├── Sprite2D (sprite)
|
||||
├── CollisionShape2D (collision)
|
||||
├── AnimationPlayer (animation_player)
|
||||
└── AudioStreamPlayer2D (pickup_sound)
|
||||
```
|
||||
|
||||
Node details:
|
||||
- Sprite2D: centered = true, modulate = Color(1, 0.8, 0) (gold color)
|
||||
- CollisionShape2D: shape = CircleShape2D (radius = 8)
|
||||
|
||||
Create accompanying script:
|
||||
```gdscript
|
||||
extends Area2D
|
||||
|
||||
@export var collect_value: int = 1
|
||||
@export var float_amplitude: float = 5.0
|
||||
@export var float_speed: float = 2.0
|
||||
|
||||
var start_y: float
|
||||
var time: float = 0.0
|
||||
|
||||
@onready var sprite = $Sprite2D
|
||||
@onready var animation_player = $AnimationPlayer
|
||||
@onready var pickup_sound = $pickup_sound
|
||||
|
||||
func _ready():
|
||||
# Connect collection signal
|
||||
body_entered.connect(_on_body_entered)
|
||||
|
||||
# Store starting position for floating animation
|
||||
start_y = position.y
|
||||
|
||||
# Random offset for variety
|
||||
time = randf() * TAU
|
||||
|
||||
func _process(delta):
|
||||
# Floating animation
|
||||
time += delta * float_speed
|
||||
position.y = start_y + sin(time) * float_amplitude
|
||||
|
||||
# Rotate for visual interest
|
||||
sprite.rotation += delta * 2.0
|
||||
|
||||
func _on_body_entered(body):
|
||||
if body.is_in_group("player"):
|
||||
# Notify player of collection
|
||||
if body.has_method("collect_item"):
|
||||
body.collect_item(collect_value)
|
||||
|
||||
# Play pickup sound
|
||||
if pickup_sound and pickup_sound.stream:
|
||||
# Play sound then destroy
|
||||
pickup_sound.play()
|
||||
|
||||
# Hide visuals but keep node alive for sound
|
||||
sprite.visible = false
|
||||
$CollisionShape2D.set_deferred("disabled", true)
|
||||
|
||||
await pickup_sound.finished
|
||||
|
||||
queue_free()
|
||||
```
|
||||
|
||||
## Interactable Object Template
|
||||
|
||||
Create scene with:
|
||||
```
|
||||
StaticBody2D (root)
|
||||
├── Sprite2D (sprite)
|
||||
├── CollisionShape2D (collision)
|
||||
├── Area2D (interaction_area)
|
||||
│ └── CollisionShape2D (interaction_collision)
|
||||
└── Label (prompt_label)
|
||||
```
|
||||
|
||||
Node details:
|
||||
- Sprite2D: centered = true
|
||||
- CollisionShape2D: shape = RectangleShape2D (32x32)
|
||||
- Area2D/CollisionShape2D: shape = RectangleShape2D (48x48) for interaction detection
|
||||
- Label: text = "Press E", horizontal_alignment = CENTER, visible = false
|
||||
|
||||
Create accompanying script:
|
||||
```gdscript
|
||||
extends StaticBody2D
|
||||
|
||||
signal interacted
|
||||
|
||||
@export var interaction_text: String = "Press E"
|
||||
@export var can_interact_multiple_times: bool = false
|
||||
|
||||
var player_nearby: bool = false
|
||||
var has_been_used: bool = false
|
||||
|
||||
@onready var sprite = $Sprite2D
|
||||
@onready var interaction_area = $interaction_area
|
||||
@onready var prompt_label = $prompt_label
|
||||
|
||||
func _ready():
|
||||
# Connect area signals
|
||||
interaction_area.body_entered.connect(_on_interaction_area_entered)
|
||||
interaction_area.body_exited.connect(_on_interaction_area_exited)
|
||||
|
||||
# Set prompt text
|
||||
prompt_label.text = interaction_text
|
||||
|
||||
# Position prompt above object
|
||||
prompt_label.position.y = -40
|
||||
|
||||
func _process(_delta):
|
||||
if player_nearby and Input.is_action_just_pressed("ui_accept"):
|
||||
if not has_been_used or can_interact_multiple_times:
|
||||
_interact()
|
||||
|
||||
func _on_interaction_area_entered(body):
|
||||
if body.is_in_group("player"):
|
||||
player_nearby = true
|
||||
|
||||
# Show prompt if not used or can use multiple times
|
||||
if not has_been_used or can_interact_multiple_times:
|
||||
prompt_label.visible = true
|
||||
|
||||
func _on_interaction_area_exited(body):
|
||||
if body.is_in_group("player"):
|
||||
player_nearby = false
|
||||
prompt_label.visible = false
|
||||
|
||||
func _interact():
|
||||
# Mark as used
|
||||
has_been_used = true
|
||||
|
||||
# Hide prompt if can't be used again
|
||||
if not can_interact_multiple_times:
|
||||
prompt_label.visible = false
|
||||
|
||||
# Emit signal for other systems to respond
|
||||
interacted.emit()
|
||||
|
||||
# TODO: Implement specific interaction logic
|
||||
# Examples:
|
||||
# - Open a door
|
||||
# - Play an animation
|
||||
# - Give an item
|
||||
# - Show dialogue
|
||||
# - Toggle a mechanism
|
||||
|
||||
print("Interacted with ", name)
|
||||
```
|
||||
|
||||
# After Creating Scene
|
||||
|
||||
After creating the selected scene:
|
||||
|
||||
1. Inform the user of the files created:
|
||||
- Scene file location
|
||||
- Script file location
|
||||
|
||||
2. Provide customization tips:
|
||||
- "The [scene name] has been created at [path]"
|
||||
- "To customize this scene:"
|
||||
- Replace the placeholder sprite with your own artwork
|
||||
- Adjust the exported variables in the Inspector
|
||||
- Add animations in the AnimationPlayer
|
||||
- [Template-specific tips]
|
||||
|
||||
3. Provide usage instructions:
|
||||
- For characters: "Add this to your level by dragging into the scene or instantiating in code"
|
||||
- For levels: "Set this as your main scene in Project Settings or change to it with get_tree().change_scene_to_file()"
|
||||
- For projectiles: "Instance this in your player/enemy script when firing"
|
||||
- For collectibles/interactables: "Place these in your level scene as children"
|
||||
|
||||
4. Offer next steps:
|
||||
- "Would you like me to:"
|
||||
- Create related scenes (e.g., "create a projectile for this character?")
|
||||
- Add more features (e.g., "add a dash ability?", "add more enemy states?")
|
||||
- Set up animations (if AnimationPlayer exists)
|
||||
- Create a test level to try it out
|
||||
24
commands/setup.md
Normal file
24
commands/setup.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
description: Validate Godot environment and configure MCP server
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Write
|
||||
- Bash(ls:*,which:*,test:*)
|
||||
---
|
||||
|
||||
You are helping set up the Godot development environment for this project.
|
||||
|
||||
Run the setup script to validate the environment and configure the MCP server:
|
||||
|
||||
!bash ${CLAUDE_PLUGIN_ROOT}/scripts/setup-mcp.sh
|
||||
|
||||
The script will:
|
||||
1. Check if Godot is installed at common locations
|
||||
2. Verify the Godot MCP server is built and ready at ~/projects/godot-mcp
|
||||
3. Create .mcp.json from the template with detected paths
|
||||
4. Report any issues and provide guidance
|
||||
|
||||
After running the script:
|
||||
- If successful, inform the user that the environment is ready and they can now use `/gd:init-game` to start planning their game
|
||||
- If there are issues, explain what needs to be fixed and how to resolve them
|
||||
- Remind the user to restart Claude Code for the MCP server changes to take effect
|
||||
11
commands/stop.md
Normal file
11
commands/stop.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description: Stop the running Godot game
|
||||
allowed-tools:
|
||||
- mcp__godot__stop_project
|
||||
---
|
||||
|
||||
Stop the currently running Godot project.
|
||||
|
||||
1. Use mcp__godot__stop_project to stop the game
|
||||
2. Display any final output or errors
|
||||
3. Confirm to the user that the game has been stopped
|
||||
652
commands/ui-template.md
Normal file
652
commands/ui-template.md
Normal file
@@ -0,0 +1,652 @@
|
||||
---
|
||||
allowed_tools:
|
||||
- AskUserQuestion
|
||||
- mcp__godot__*
|
||||
- Write
|
||||
- Read
|
||||
- Skill
|
||||
---
|
||||
|
||||
Create a quick UI template for common game UI screens.
|
||||
|
||||
# Process
|
||||
|
||||
1. **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
|
||||
|
||||
2. **If Custom path selected, ask for the specific path**
|
||||
|
||||
3. **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:
|
||||
```gdscript
|
||||
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:
|
||||
```gdscript
|
||||
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:
|
||||
```gdscript
|
||||
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:
|
||||
```gdscript
|
||||
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:
|
||||
```gdscript
|
||||
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:
|
||||
```gdscript
|
||||
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:
|
||||
```gdscript
|
||||
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:
|
||||
|
||||
1. Inform the user of the files created:
|
||||
- Scene file location
|
||||
- Script file location
|
||||
|
||||
2. 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!"
|
||||
|
||||
3. 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
|
||||
Reference in New Issue
Block a user