Agent skill
godot-fix-positions
Use when Godot project has position conflicts between editor (.tscn) and code (.gd), camera-following backgrounds, or runtime positions don't match editor preview. Orchestrates all 3 position sync mini-skills: sync-static-positions, sync-camera-positions, and sync-parallax. Each operation targets specific position conflict types.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/godot-fix-positions
SKILL.md
Godot Fix Positions Orchestrator
This orchestrator runs 3 position sync mini-skills in sequence. For individual operations, invoke mini-skills directly.
Core Principle: What You See Is What You Get (WYSIWYG)
Iron Law: Editor preview must match runtime behavior. No surprises.
This skill automatically detects and resolves position conflicts between editor-defined positions (.tscn files) and code-defined positions (.gd files). It handles static conflicts, camera-following backgrounds, parallax layers, and player-relative positioning.
UPON INVOCATION - START HERE
When this skill is invoked, IMMEDIATELY execute the following sequence:
1. Verify Godot Project (5 seconds)
# Check if this is a Godot project
ls project.godot 2>/dev/null && echo "✓ Godot project detected" || echo "✗ Not a Godot project"
If NOT a Godot project:
- Inform user this skill only works on Godot projects
- Ask if they want to navigate to the correct directory
- STOP here
If IS a Godot project:
- Proceed to step 2
2. Begin Phase 1: Detection & Analysis (AUTOMATIC)
Do NOT ask "What would you like me to do?" - Start analyzing immediately.
Execute these commands in parallel:
# Detect static position conflicts
find . -name "*.gd" -exec grep -l "position\s*=" {} \; | head -20
# Find camera-following patterns
grep -rn "position\s*=.*camera\|position\s*=.*player" --include="*.gd" . | head -20
# Find parallax backgrounds
find . -name "*parallax*.tscn" -o -name "*background*.tscn" | head -10
# Count potential conflicts
find . -name "*.gd" -exec grep -c "^\s*position\s*=" {} + | awk '{s+=$1} END {print s}'
3. Present Findings (30 seconds)
Show the user:
=== Editor Position Sync Analysis ===
Project: [project name from project.godot]
Conflicts detected:
- Static position assignments: X
- Camera-following elements: X
- Parallax backgrounds: X
- Player-relative positioning: X
Total: X position conflicts found
Sync includes:
✓ Detect editor vs code position conflicts
✓ Smart classification (intentional vs conflict)
✓ Camera-aware positioning
✓ Multiple sync strategies
✓ Git commit per operation
✓ Auto-rollback on test failure
Would you like me to:
1. Proceed with automatic sync (recommended)
2. Show detailed breakdown first
3. Cancel
4. Wait for User Choice
- If 1 (Proceed): Start Phase 2 immediately
- If 2 (Details): Show detailed file-by-file breakdown, then offer to proceed
- If 3 (Cancel): Exit skill
CRITICAL: Do NOT stop after loading the skill. Do NOT ask "What would you like me to do?" Start with step 1 immediately.
When to Use This Skill
Use this skill when you detect ANY of these symptoms:
Static Position Conflicts:
position = Vector2(x, y)in_ready()or_init()- Editor shows one position, runtime shows different position
- Nodes appear in wrong locations when game starts
- Visual debugging reveals position mismatches
Camera-Following Elements:
- Backgrounds that follow camera position
- UI elements that track player
- Parallax layers with camera sync
position = camera.positionpatterns
Player-Relative Positioning:
- Nodes positioned relative to player
position = player.position + offsetpatterns- Elements that track player movement
- Camera-relative UI elements
Editor Preview Mismatch:
- "It looks right in the editor but wrong at runtime"
- "The background jumps when the game starts"
- "My UI elements are in the wrong place"
- "Camera-following nodes don't preview correctly"
Decision Flowchart
User mentions position issues
↓
Is it editor vs runtime mismatch?
↓ YES ↓ NO
Use this skill Different issue
↓
Run Phase 1: Detection
↓
Conflicts found?
↓ YES ↓ NO
Run Phases 2-3 Document clean state
The Three Phases
Phase 1: Detection & Analysis (Automatic)
Purpose: Detect position conflicts and classify them intelligently.
Steps:
- Scan Project Files
# Find all Godot script files
find . -name "*.gd" -type f
# Find all scene files
find . -name "*.tscn" -type f
- Detect Position Assignments
Scan for position-setting patterns:
# Static position assignments
grep -rn "position\s*=\s*Vector2(" --include="*.gd" .
# Camera-following patterns
grep -rn "position\s*=.*camera" --include="*.gd" .
# Player-following patterns
grep -rn "position\s*=.*player" --include="*.gd" .
# Parallax patterns
grep -rn "ParallaxBackground\|ParallaxLayer" --include="*.gd" .
- Parse .tscn Files for Positions
Extract position properties from scenes:
# Find position declarations in .tscn files
grep -rn "^position\s*=\s*Vector2(" --include="*.tscn" .
- Intelligent Classification
For each detected position assignment, classify as:
- CONFLICT: Static position in
_ready()differs from .tscn - INTENTIONAL_DYNAMIC: Camera/player following (skip)
- INTENTIONAL_ANIMATION: Tweens/animations (skip)
- PROCESS_ASSIGNMENT: Every-frame in
_process()(critical warning)
Classification Logic:
def classify_position_assignment(context):
# Context = 30 lines around assignment
# SKIP: Animation/tween
if "tween" in context or "create_tween" in context:
return "INTENTIONAL_ANIMATION"
# SKIP: Camera following
if "camera.position" in context or "get_viewport().get_camera" in context:
return "INTENTIONAL_DYNAMIC"
# SKIP: Player following
if "player.position" in context or "target.position" in context:
return "INTENTIONAL_DYNAMIC"
# CRITICAL: Every frame assignment
if "_process(" in context:
return "PROCESS_ASSIGNMENT"
# CONFLICT: Static assignment in _ready
if "_ready(" in context and "Vector2(" in line:
return "CONFLICT"
return "UNKNOWN"
- Create Conflict Manifest
Generate a priority-ordered list:
| File | Node | Editor Position | Code Position | Classification | Sync Strategy |
|---|---|---|---|---|---|
| background.gd | Background | (0, 0) | camera.position | DYNAMIC | Document intent |
| enemy.gd | Enemy | (500, 300) | (100, 150) | CONFLICT | Code → Editor |
| ui_label.gd | Label | (10, 10) | (50, 50) | CONFLICT | Editor → Code |
Phase 1 Output:
- ✓ All position assignments detected
- ✓ Conflicts classified intelligently
- ✓ Manifest table generated
- ✓ Ready for synchronization
Phase 2: Position Synchronization (Three Strategies)
Process conflicts in priority order: Critical → Conflicts → Dynamic
CRITICAL - After Each Operation:
- Git commit the changes
- Run automatic validation test
- If test fails: Auto-revert and report error
- If test passes: Continue automatically to next conflict
- No user prompt unless test fails or user confirmation needed
Automatic Testing Procedure:
# Quick validation test (runs after each git commit)
echo "Running validation test..."
godot --headless --quit-after 5 project.godot 2>&1 | tee test_output.log
# Check for errors
if grep -q "ERROR\|SCRIPT ERROR\|Parse Error" test_output.log; then
echo "⚠️ Tests failed - reverting operation"
git reset --hard HEAD~1
echo "❌ Operation reverted. Error details:"
grep "ERROR\|SCRIPT ERROR\|Parse Error" test_output.log
# STOP and report to user
else
echo "✓ Tests passed - continuing"
rm test_output.log
# CONTINUE to next operation automatically
fi
Strategy A: Code → Editor Sync
Target: Static position conflicts where code position should be editor position
When to use:
- Code position is intentional final position
- Editor preview should match runtime
- Position set once in
_ready()and never changes
Process:
- Detect Conflict
# Example: enemy.gd sets position in _ready()
grep -A5 "_ready" enemy.gd
Example code:
# enemy.gd
func _ready():
position = Vector2(100, 150) # Conflict with .tscn
Example .tscn:
[node name="Enemy" type="CharacterBody2D"]
position = Vector2(500, 300) # Different from code
- Update .tscn File
Parse and update:
# Read .tscn file
with open("enemy.tscn") as f:
content = f.read()
# Find and replace position line
content = re.sub(
r'position = Vector2\([^)]+\)',
'position = Vector2(100, 150)',
content
)
# Write back
with open("enemy.tscn", "w") as f:
f.write(content)
- Update .gd File
Remove static assignment, add comment:
# enemy.gd
func _ready():
# Position moved to .tscn for editor visibility
# Was: position = Vector2(100, 150)
pass
- Git Commit
git add enemy.tscn enemy.gd
git commit -m "Sync: Move enemy position from code to editor (.tscn)
- Updated enemy.tscn position to Vector2(100, 150)
- Removed static position assignment from enemy.gd
- Editor now matches runtime position"
- Run automatic test (see Phase 2 header for test procedure)
Success criteria:
- .tscn position matches intended runtime position
- Code assignment removed
- Comment documents change
- Behavior unchanged
Strategy B: Editor → Code Sync
Target: Position should be set in code, remove .tscn position
When to use:
- Position is calculated/dynamic
- .tscn position is arbitrary/wrong
- Code position is source of truth
Process:
- Keep Code Assignment
Code already correct:
func _ready():
position = Vector2(100, 150) # Correct position
- Reset .tscn to Default
Update .tscn to neutral position:
[node name="Enemy" type="CharacterBody2D"]
# position removed (defaults to Vector2(0, 0))
Or explicitly set to origin:
position = Vector2(0, 0)
- Add Documentation
Add comment explaining:
func _ready():
# Position set in code (not in .tscn)
# Editor shows (0,0), runtime shows calculated position
position = Vector2(100, 150)
- Git Commit
git commit -m "Sync: Keep enemy position in code, reset editor
- Removed position from enemy.tscn (defaults to origin)
- Kept code position assignment
- Runtime position calculated in _ready()"
- Run automatic test
Strategy C: Camera-Aware Positioning (SPECIAL)
Target: Backgrounds, parallax layers, camera-following elements
When to use:
- Background follows camera
- Parallax layers
- Player-relative UI elements
- Dynamic camera positioning
Special Case - User's Background Issue:
Problem: Background positioned at (0, 0) in editor, but follows camera at runtime, causing confusion during level design.
Solution:
- Detect Camera-Following Pattern
# background.gd
func _process(delta):
position = camera.position # Follows camera
- Calculate Expected Editor Position
Find typical camera start position:
# Search for camera or player spawn position
grep -rn "camera.*position\|player.*position" --include="*.tscn" .
Example: Camera starts at (512, 300)
- Update .tscn to Camera Start Position
[node name="Background" type="Sprite2D"]
position = Vector2(512, 300) # Camera start position
- Document Camera-Relative Behavior
# background.gd
func _process(delta):
# EDITOR NOTE: Position in .tscn shows camera start position
# for accurate level design preview. At runtime, follows camera.
position = camera.position
- Create Editor Preview Tool (Optional)
Generate editor plugin to show camera bounds:
# addons/camera_preview/camera_preview.gd
@tool
extends EditorPlugin
func _enter_tree():
# Draw camera bounds in editor
pass
Result:
- Editor shows realistic preview
- Background appears at expected camera position
- Level design is accurate
- Runtime behavior unchanged
- Git Commit
git commit -m "Sync: Camera-aware background positioning
- Updated background.tscn to camera start position
- Added documentation for camera-following behavior
- Editor now shows accurate runtime preview"
- Run automatic test
Success criteria:
- Editor preview shows realistic camera view
- Runtime behavior unchanged
- Level design is accurate
- Documentation explains dynamic behavior
Phase 3: Verification & Reporting
Purpose: Ensure synchronization succeeded without regressions.
Steps:
- Open project in Godot
godot --editor project.godot
- Visual verification
- Open synced scenes in editor
- Compare editor positions with manifest
- Verify positions match expected
- Runtime verification
# Run main scene for 10 seconds
godot --quit-after 10 project.godot
- Check for conflicts
# Re-run detection to verify all conflicts resolved
grep -rn "position\s*=\s*Vector2(" --include="*.gd" . | wc -l
- Generate Report
=== Position Sync Complete ===
Conflicts resolved: X
- Code → Editor: X
- Editor → Code: X
- Camera-aware: X
Files modified:
- X .tscn files updated
- X .gd files updated
Git commits created: X
All tests passing ✓
No regressions detected ✓
Editor preview matches runtime ✓
Supporting Files
This skill uses modular reference files:
- position-detection-patterns.md: All grep patterns for position detection
- sync-strategies.md: Detailed step-by-step procedures
- camera-aware-positioning.md: Camera/parallax handling
- validation-tests.md: Testing procedures
Read these files for detailed implementation guidance.
Red Flags - STOP
These thoughts mean you're rationalizing away discipline:
| Rationalization | Reality | Fix |
|---|---|---|
| "This dynamic position is a conflict" | Misclassification breaks behavior | Use intelligent classifier |
| "I'll sync all positions" | Over-sync breaks intentional dynamic positioning | Only sync true conflicts |
| "Camera-following is a conflict" | Wrong - it's intentional | Skip dynamic patterns |
| "I don't need to test this change" | Untested = broken | Always verify |
| "Close enough in editor" | Iron Law has no exceptions | Exact positions only |
| "I'll manually fix .tscn" | Manual edits create errors | Parse programmatically |
Quick Reference: Conflict → Strategy
| Conflict Type | Detection | Strategy |
|---|---|---|
| Static in _ready() | position = Vector2() in _ready |
Code → Editor |
| Calculated position | position = calc_spawn() |
Editor → Code |
| Camera-following | position = camera.position |
Camera-aware |
| Parallax layer | ParallaxLayer + scroll | Camera-aware |
| Player-relative | position = player.position + offset |
Document dynamic |
| Animation/tween | tween_property("position") |
SKIP (intentional) |
| Every frame (_process) | position in _process | CRITICAL warning |
Camera-Aware Positioning - Special Features
For User's Background Use Case
Problem Statement:
- Background layer positioned at (0, 0) in editor
- At runtime, background follows camera:
position = camera.position - Editor shows wrong position → confusing for level design
- Need: Show correct position in BOTH editor and runtime
Solution:
- Detect Camera Start Position
# Find main camera or player spawn point
grep -rn "Camera2D\|player.*position" --include="*.tscn" .
- Calculate Editor Position
# Parse camera start position from main scene
camera_start = Vector2(512, 300) # Example
# Set background .tscn to camera start
background_position = camera_start
- Update Background .tscn
[node name="Background" type="Sprite2D"]
position = Vector2(512, 300) # Camera start (was 0, 0)
- Document Behavior
# background.gd
# EDITOR PREVIEW: Position set to camera start for accurate level design
# RUNTIME: Follows camera dynamically
func _process(delta):
position = camera.position
- Create Parallax Handler (If Needed)
For ParallaxBackground nodes:
# parallax_background.gd
@export var camera_start_position := Vector2(512, 300)
func _ready():
# Set initial scroll to camera start
scroll_offset = camera_start_position
Result:
- Editor shows background at camera start position
- Level design is accurate
- Runtime follows camera as intended
- WYSIWYG achieved ✓
Execution Strategy
This skill runs fully automatically:
- User invokes skill on Godot project
- Phase 1: Scan and report findings
- User approves synchronization
- Phase 2: Execute all sync operations with commits
- Phase 3: Verify and report results
User input required:
- Initial invocation
- Approval after Phase 1 analysis
- Strategy selection (if ambiguous)
- Final verification check
Everything else is automatic:
- Position conflict detection
- Intelligent classification
- .tscn file updates
- Script modifications
- Git commits
- Testing
Success Criteria
Synchronization is complete when:
- ✓ All static position conflicts resolved
- ✓ Camera-following elements documented
- ✓ Editor preview matches runtime positions
- ✓ All scenes load without errors
- ✓ Behavior identical to baseline
- ✓ Visual appearance unchanged
- ✓ Clean git history with descriptive commits
Integration with Other Skills
Works well with:
- godot-refactoring: Refactor before position sync
- Scene Hierarchy Cleaner: Organize backgrounds into groups
- Scene Layout Organizer: Align after position sync
Best practice workflow:
- Run godot-refactoring (if needed)
- Run editor-position-sync
- Run scene-hierarchy-cleaner (if needed)
- Continue development
Remember: Editor preview is your source of truth for visual design. Runtime must match what you see in the editor. No surprises.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?