Modernizes GDScript 1.0 (Godot 3.x) code to GDScript 2.0 (Godot 4.x) syntax. Converts yield to await, onready to @onready, export to @export, setget to property syntax, and adds optional static typing. Essential for Godot 3.x to 4.x migration projects.
Scanned 2/12/2026
Install via CLI
openskills install majiayu000/claude-skill-registry---
name: godot-modernize-gdscript
version: 1.0.0
displayName: "Godot GDScript 2.0 Modernizer"
description: >
Modernizes GDScript 1.0 (Godot 3.x) code to GDScript 2.0 (Godot 4.x) syntax.
Converts yield to await, onready to @onready, export to @export, setget to
property syntax, and adds optional static typing. Essential for Godot 3.x
to 4.x migration projects.
author: "Asreonn"
license: MIT
category: game-development
type: tool
difficulty: intermediate
audience: [developers, migrators]
keywords:
- godot
- gdscript
- modernization
- migration
- godot3
- godot4
- yield
- await
- static-typing
platforms: [macos, linux, windows]
repository: https://github.com/asreonn/godot-superpowers
homepage: https://github.com/asreonn/godot-superpowers#readme
filesystem:
read:
- "${PROJECT_ROOT}/**/*.gd"
- "${PROJECT_ROOT}/project.godot"
write:
- "${PROJECT_ROOT}/**/*.gd"
deny:
- "**/.env*"
- "**/secrets*"
- "**/*.key"
behavior:
timeout: 300
retry: 2
cache: true
interactive: true
---
# Godot GDScript 2.0 Modernizer
**Converts GDScript 1.0 (Godot 3.x) patterns to GDScript 2.0 (Godot 4.x) syntax.**
## Core Conversions
This skill performs five key modernizations:
| GDScript 1.0 | GDScript 2.0 | Pattern |
|-------------|--------------|---------|
| `yield(object, "signal")` | `await object.signal` | Async operations |
| `onready var` | `@onready var` | Node references |
| `export var` | `@export var` | Inspector variables |
| `var x setget set_x, get_x` | Property syntax | Getters/setters |
| `var health = 100` | `var health: int = 100` | Static typing |
---
## UPON INVOCATION - START HERE
When this skill is invoked, IMMEDIATELY execute:
### 1. Verify Godot Project (5 seconds)
```bash
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
- STOP here
**If IS a Godot project:**
- Proceed to step 2
### 2. Detect GDScript Version (10 seconds)
```bash
# Check for Godot 3.x patterns
echo "=== Detecting GDScript 1.0 Patterns ==="
echo "yield statements:"
grep -rn "yield(" --include="*.gd" . | wc -l
echo "onready declarations:"
grep -rn "^onready var" --include="*.gd" . | wc -l
echo "export declarations:"
grep -rn "^export var\|^export(int)\|^export(float)" --include="*.gd" . | wc -l
echo "setget properties:"
grep -rn "setget" --include="*.gd" . | wc -l
echo "untyped variables:"
grep -rn "^var [a-z]" --include="*.gd" . | grep -v ":" | wc -l
```
### 3. Present Findings
Show the user:
```
=== GDScript 2.0 Modernization Analysis ===
Project: [project name]
Current: GDScript 1.0 (Godot 3.x style)
Patterns to modernize:
- yield statements: X
- onready variables: X
- export variables: X
- setget properties: X
- untyped variables: X
Total: X modernizations needed
Modernization includes:
✓ yield → await conversion
✓ onready → @onready
✓ export → @export
✓ setget → property syntax
✓ Optional static typing
✓ Git commit per file
✓ Backup before changes
Would you like me to:
1. Modernize all files (recommended)
2. Show detailed breakdown first
3. Select specific conversions
4. Cancel
```
### 4. Wait for User Choice
- **If 1 (Proceed):** Start Phase 2 immediately
- **If 2 (Details):** Show file-by-file breakdown, then offer to proceed
- **If 3 (Selective):** Ask which conversions to apply
- **If 4 (Cancel):** Exit skill
---
## Phase 1: Analysis & Inventory
### 1.1 Create File Inventory
```bash
# Find all .gd files
find . -name "*.gd" -type f | sort > /tmp/gdscript_files.txt
wc -l /tmp/gdscript_files.txt
echo "files found"
```
### 1.2 Analyze Each File
For each .gd file, detect patterns:
```bash
# Analyze patterns per file
for file in $(cat /tmp/gdscript_files.txt); do
echo "=== $file ==="
grep -c "yield(" "$file" 2>/dev/null || echo 0
grep -c "^onready var" "$file" 2>/dev/null || echo 0
grep -c "^export" "$file" 2>/dev/null || echo 0
grep -c "setget" "$file" 2>/dev/null || echo 0
done
```
### 1.3 Create Modernization Plan
```
Modernization Plan:
===================
1. yield → await conversions (X files)
2. onready → @onready (X files)
3. export → @export (X files)
4. setget → properties (X files)
5. Static typing (X files)
Total files to modify: X
Estimated time: Auto (user doesn't wait)
Backup created: YES (git tag)
Rollback available: YES
```
---
## Phase 2: Modernization Operations
### Conversion A: yield → await
**Detection:**
```bash
grep -rn "yield(" --include="*.gd" .
```
**Common Patterns:**
| Old Pattern | New Pattern |
|-------------|-------------|
| `yield(get_tree().create_timer(1.0), "timeout")` | `await get_tree().create_timer(1.0).timeout` |
| `yield(timer, "timeout")` | `await timer.timeout` |
| `yield(animation_player, "animation_finished")` | `await animation_player.animation_finished` |
| `yield(get_tree(), "idle_frame")` | `await get_tree().process_frame` |
**Transformation Process:**
1. **Extract yield statement**
```gdscript
# Before
yield(get_tree().create_timer(2.0), "timeout")
```
2. **Convert to await**
```gdscript
# After
await get_tree().create_timer(2.0).timeout
```
3. **Handle variable assignment**
```gdscript
# Before
var result = yield(async_function(), "completed")
# After
var result = await async_function()
```
**Implementation:**
```python
# Pseudo-code for replacement
def convert_yield_to_await(line):
# Pattern: yield(object, "signal_name")
# Convert to: await object.signal_name
import re
pattern = r'yield\(([^,]+),\s*"([^"]+)"\)'
replacement = r'await \1.\2'
return re.sub(pattern, replacement, line)
```
---
### Conversion B: onready → @onready
**Detection:**
```bash
grep -rn "^onready var" --include="*.gd" .
```
**Transformation:**
```gdscript
# Before
onready var player = $Player
onready var health_bar = $UI/HealthBar
# After
@onready var player: Node = $Player
@onready var health_bar: ProgressBar = $UI/HealthBar
```
**Process:**
1. Replace `onready` with `@onready`
2. Add type hints where detectable
3. Keep variable name and initialization
**Type Inference (Optional):**
```gdscript
# If $Player is CharacterBody2D in scene
@onready var player: CharacterBody2D = $Player
# If $Timer is Timer node
@onready var _timer: Timer = $Timer
```
---
### Conversion C: export → @export
**Detection:**
```bash
grep -rn "^export" --include="*.gd" .
```
**Transformation:**
```gdscript
# Before
export var speed = 200
export(int) var max_health = 100
export(float) var jump_force = 500.0
export(String) var character_name = "Player"
export(NodePath) var target_path
# After
@export var speed: float = 200.0
@export var max_health: int = 100
@export var jump_force: float = 500.0
@export var character_name: String = "Player"
@export var target_path: NodePath
```
**Export Types Mapping:**
| Old Export | New Export | Type |
|------------|------------|------|
| `export(int)` | `@export var x: int` | Integer |
| `export(float)` | `@export var x: float` | Float |
| `export(String)` | `@export var x: String` | String |
| `export(bool)` | `@export var x: bool` | Boolean |
| `export(Color)` | `@export var x: Color` | Color |
| `export(Vector2)` | `@export var x: Vector2` | Vector2 |
| `export(Vector3)` | `@export var x: Vector3` | Vector3 |
| `export(NodePath)` | `@export var x: NodePath` | NodePath |
| `export var` | `@export var x: Type` | Inferred |
---
### Conversion D: setget → Property Syntax
**Detection:**
```bash
grep -rn "setget" --include="*.gd" .
```
**Transformation:**
```gdscript
# Before (GDScript 1.0)
var health = 100 setget set_health, get_health
func set_health(value):
health = clamp(value, 0, max_health)
health_changed.emit(health)
func get_health():
return health
```
```gdscript
# After (GDScript 2.0)
var health: int = 100:
set(value):
health = clamp(value, 0, max_health)
health_changed.emit(health)
get:
return health
```
**Process:**
1. Remove `setget` from variable declaration
2. Add colon after type
3. Inline setter and getter
4. Use `set(value):` and `get:` syntax
**Complex Example:**
```gdscript
# Before
var score = 0 setget set_score
func set_score(value):
score = max(0, value)
update_ui()
# After
var score: int = 0:
set(value):
score = max(0, value)
update_ui()
```
---
### Conversion E: Static Typing (Optional)
**Detection:**
```bash
grep -rn "^var [a-z_]* = " --include="*.gd" . | grep -v ":"
```
**Type Inference:**
| Value | Inferred Type |
|-------|--------------|
| `= 100` | `int` |
| `= 3.14` | `float` |
| `= "text"` | `String` |
| `= true` | `bool` |
| `= Vector2(x, y)` | `Vector2` |
| `= $Node` | Node type from scene |
**Transformation:**
```gdscript
# Before
var health = 100
var speed = 200.5
var name = "Player"
var active = true
var position = Vector2.ZERO
# After
var health: int = 100
var speed: float = 200.5
var name: String = "Player"
var active: bool = true
var position: Vector2 = Vector2.ZERO
```
---
## Phase 3: Execution & Safety
### 3.1 Create Git Baseline
```bash
# Create backup tag
git tag gdscript1-baseline-$(date +%Y%m%d-%H%M%S)
# Stage any current changes
git add .
git commit -m "Baseline: Pre-GDScript 2.0 modernization" || echo "No changes to commit"
```
### 3.2 Process Files Sequentially
For each .gd file:
```bash
# Process file
python3 << 'EOF'
import re
def modernize_gdscript(content):
# Conversion 1: yield → await
content = re.sub(
r'yield\(([^,]+),\s*"([^"]+)"\)',
r'await \1.\2',
content
)
# Conversion 2: onready → @onready
content = re.sub(
r'^onready var',
'@onready var',
content,
flags=re.MULTILINE
)
# Conversion 3: export → @export (simple)
content = re.sub(
r'^export var',
'@export var',
content,
flags=re.MULTILINE
)
return content
# Read, process, write
with open("script.gd", "r") as f:
content = f.read()
new_content = modernize_gdscript(content)
with open("script.gd", "w") as f:
f.write(new_content)
EOF
```
### 3.3 Commit Per File
```bash
# After each file modification
git add "$file"
git commit -m "Modernize: $file to GDScript 2.0
- Converted yield to await
- Updated onready to @onready
- Migrated export to @export
- Applied static typing"
```
### 3.4 Validation After Each File
```bash
# Validate syntax
godot --headless --quit-after 2 project.godot 2>&1 | grep -i "error" && {
echo "Syntax error detected in $file"
git reset --hard HEAD~1
echo "Reverted changes to $file"
}
```
---
## Phase 4: Verification & Testing
### 4.1 Syntax Validation
```bash
# Check all modified files for syntax errors
for file in $(git diff --name-only HEAD~10..HEAD | grep "\.gd$"); do
echo "Checking $file..."
# Basic syntax check
gdscript_tool --check "$file" 2>/dev/null || echo "Manual review needed: $file"
done
```
### 4.2 Pattern Verification
```bash
# Verify no old patterns remain
echo "Checking for remaining GDScript 1.0 patterns..."
echo "yield statements remaining:"
grep -rn "yield(" --include="*.gd" . | wc -l
echo "onready remaining:"
grep -rn "^onready var" --include="*.gd" . | wc -l
echo "export remaining:"
grep -rn "^export var\|^export(" --include="*.gd" . | wc -l
```
### 4.3 Godot Project Test
```bash
# Open project in Godot to verify
godot --editor project.godot &
sleep 5
# Check for errors
# (User should visually verify no red errors in Output panel)
```
---
## Examples
### Example 1: Complete Script Modernization
**Before (GDScript 1.0):**
```gdscript
extends CharacterBody2D
export var speed = 200
export(int) var health = 100
onready var sprite = $Sprite
onready var animation = $AnimationPlayer
var damage = 10 setget set_damage
func set_damage(value):
damage = clamp(value, 0, 100)
func _ready():
yield(get_tree().create_timer(1.0), "timeout")
start_game()
func take_damage(amount):
health -= amount
if health <= 0:
yield(play_death_animation(), "completed")
queue_free()
func play_death_animation():
animation.play("death")
yield(animation, "animation_finished")
```
**After (GDScript 2.0):**
```gdscript
extends CharacterBody2D
@export var speed: float = 200.0
@export var health: int = 100
@onready var sprite: Sprite2D = $Sprite
@onready var animation: AnimationPlayer = $AnimationPlayer
var damage: int = 10:
set(value):
damage = clamp(value, 0, 100)
func _ready():
await get_tree().create_timer(1.0).timeout
start_game()
func take_damage(amount: int) -> void:
health -= amount
if health <= 0:
await play_death_animation()
queue_free()
func play_death_animation() -> void:
animation.play("death")
await animation.animation_finished
```
### Example 2: Signal Connection Modernization
**Before:**
```gdscript
func _ready():
button.connect("pressed", self, "_on_button_pressed")
timer.connect("timeout", self, "_on_timeout")
func _on_button_pressed():
pass
func _on_timeout():
pass
```
**After:**
```gdscript
func _ready():
button.pressed.connect(_on_button_pressed)
timer.timeout.connect(_on_timeout)
func _on_button_pressed() -> void:
pass
func _on_timeout() -> void:
pass
```
---
## Success Criteria
Modernization complete when:
- ✓ Zero `yield(` statements remain (converted to await)
- ✓ Zero `onready var` remain (converted to @onready)
- ✓ Zero `export var` remain (converted to @export)
- ✓ Zero `setget` remain (converted to property syntax)
- ✓ All scripts compile without errors
- ✓ No functional changes (behavior identical)
- ✓ Git history shows clear modernization commits
---
## Common Issues & Solutions
### Issue 1: yield with function calls
**Problem:**
```gdscript
var result = yield(load_data(), "completed")
```
**Solution:**
```gdscript
var result = await load_data()
```
### Issue 2: Coroutines with state
**Problem:**
```gdscript
func process():
yield(do_step_1(), "completed")
yield(do_step_2(), "completed")
```
**Solution:**
```gdscript
func process() -> void:
await do_step_1()
await do_step_2()
```
### Issue 3: setget with only getter or setter
**Problem:**
```gdscript
var score = 0 setget , get_score # Only getter
```
**Solution:**
```gdscript
var score: int = 0:
get:
return score
```
---
## Rollback Procedure
If modernization causes issues:
```bash
# Find baseline tag
git tag | grep "gdscript1-baseline"
# Reset to pre-modernization state
git reset --hard <baseline-tag>
# Or revert specific commits
git revert <modernization-commit-hash>
```
---
## Integration with Other Skills
**Use before:**
- `godot-refactor` - Modernize syntax first, then refactor architecture
- `godot-migrate-tilemap` - Update code before TileMap migration
**Use after:**
- Project conversion from Godot 3.x to 4.x
- `godot-organize-project` - Organize files first
---
**This skill automates the tedious parts of GDScript 1.0 → 2.0 migration while preserving exact functionality.**
No comments yet. Be the first to comment!