$npx -y skills add thedivergentai/GD-Agentic-Skills --skill godot-adapt-2d-to-3dExpert patterns for migrating 2D games to 3D including node type conversions, camera systems (third-person, first-person, orbit), physics layer migration, sprite-to-model art pipeline, and control scheme adaptations. Use when porting 2D projects to 3D or adding 3D elements. Trigg
| 1 | ## Godot 4.7 Baseline |
| 2 | |
| 3 | - Expert patterns in this skill target **Godot 4.7+** (stable, 2026-06-18). |
| 4 | - Consult the [Godot 4.7 migration guide](https://docs.godotengine.org/en/4.7/tutorials/migrating/upgrading_to_godot_4.7.html) when upgrading projects from 4.6. |
| 5 | - **NEVER** assume 4.6 defaults (stretch mode, audio area_mask, RichTextLabel percent flags) without checking 4.7 migration notes. |
| 6 | |
| 7 | # Adapt: 2D to 3D |
| 8 | |
| 9 | Expert guidance for migrating 2D games into the third dimension. |
| 10 | |
| 11 | ## NEVER Do |
| 12 | |
| 13 | - **NEVER directly replace Vector2 with Vector3(x, y, 0)** — This creates a "flat 3D" game with no depth gameplay. Add Z-axis movement or camera rotation to justify 3D. |
| 14 | - **NEVER keep 2D collision layers** — 2D and 3D physics use separate layer systems. You must reconfigure collision_layer/collision_mask for 3D nodes. |
| 15 | - **NEVER forget to add lighting** — 3D without lights is pitch black (unless using unlit materials). Add at least one DirectionalLight3D. |
| 16 | - **NEVER use Camera2D follow logic in 3D** — Camera3D needs spring arm or look-at logic. Direct position copying causes clipping and disorientation. |
| 17 | - **NEVER assume same performance** — 3D is 5-10x more demanding. Budget for lower draw calls, smaller viewport resolution on mobile. |
| 18 | - **NEVER use the rotation property for complex 3D logic** — 3D rotation uses Euler angles. Interpolating Euler angles causes unpredictable paths and Gimbal Lock. Always use `Quaternion` for 3D rotation interpolation or the `Basis` matrix for directional vectors. |
| 19 | - **NEVER ignore metric scaling** — 3D physics and lighting assume 1 unit = 1 meter. Scaling models inside the engine introduces precision errors. Export assets from DCCs at the correct metric scale. |
| 20 | - **NEVER disable physics interpolation when using custom camera follow scripts** — Updating camera position in `_process` to follow a body moving in `_physics_process` causes jitter. Use `Node3D.get_global_transform_interpolated()` for smooth transforms. |
| 21 | |
| 22 | --- |
| 23 | |
| 24 | ## Available Scripts |
| 25 | |
| 26 | > **MANDATORY**: Read the appropriate script before implementing the corresponding pattern. |
| 27 | |
| 28 | ### [sprite_plane.gd](scripts/sprite_plane.gd) |
| 29 | Sprite3D billboard configuration and world-to-screen projection for placing 2D UI over 3D objects. Handles behind-camera detection. |
| 30 | |
| 31 | ### [vector_mapping.gd](scripts/vector_mapping.gd) |
| 32 | Static utility for 2D→3D vector translation. The Y-to-Z rule: 2D Y (down) maps to 3D Z (forward). Essential for movement code. |
| 33 | |
| 34 | ### [crisp_projected_ui.gd](scripts/crisp_projected_ui.gd) |
| 35 | Projected 2D UI for 3D Objects mapping snippet. Replaces blurry text elements with true 2D Canvas space positioning projected from 3D space. |
| 36 | |
| 37 | --- |
| 38 | |
| 39 | ## Node Conversion Matrix |
| 40 | |
| 41 | | 2D Node | 3D Equivalent | Notes | |
| 42 | |---------|---------------|-------| |
| 43 | | CharacterBody2D | CharacterBody3D | Add Z-axis movement, rotate with mouse | |
| 44 | | RigidBody2D | RigidBody3D | Gravity now Vector3(0, -9.8, 0) | |
| 45 | | StaticBody2D | StaticBody3D | Collision shapes use Shape3D | |
| 46 | | Area2D | Area3D | Triggers work the same way | |
| 47 | | Sprite2D | MeshInstance3D + QuadMesh | Or use Sprite3D (billboarded) | |
| 48 | | AnimatedSprite2D | AnimatedSprite3D | Billboard mode available | |
| 49 | | TileMapLayer | GridMap | Requires MeshLibrary creation | |
| 50 | | Camera2D | Camera3D | Requires repositioning logic | |
| 51 | | CollisionShape2D | CollisionShape3D | BoxShape2D → BoxShape3D, etc. | |
| 52 | | RayCast2D | RayCast3D | target_position is now Vector3 | |
| 53 | |
| 54 | --- |
| 55 | |
| 56 | ## Migration Steps |
| 57 | |
| 58 | ### Step 1: Physics Layer Reconfiguration |
| 59 | |
| 60 | ```gdscript |
| 61 | # 2D collision layers are SEPARATE from 3D |
| 62 | # You must reconfigure in Project Settings → Layer Names → 3D Physics |
| 63 | |
| 64 | # Before (2D): |
| 65 | # Layer 1: Player |
| 66 | # Layer 2: Enemies |
| 67 | # Layer 3: World |
| 68 | |
| 69 | # After (3D) - same names, but different system |
| 70 | # In code, update all collision layer references: |
| 71 | |
| 72 | # 2D version: |
| 73 | # collision_layer = 0b0001 |
| 74 | |
| 75 | # 3D version (same logic, different node): |
| 76 | var character_3d := CharacterBody3D.new() |
| 77 | character_3d.collision_layer = 0b0001 # Layer 1: Player |
| 78 | character_3d.collision_mask = 0b0110 # Detect Enemies + World |
| 79 | ``` |
| 80 | |
| 81 | ### Step 2: Camera Conversion |
| 82 | |
| 83 | ```gdscript |
| 84 | # ❌ BAD: Direct 2D follow logic |
| 85 | extends Camera3D |
| 86 | |
| 87 | @onready var player: Node3D = $"../Player" |
| 88 | |
| 89 | func _process(delta: float) -> void: |
| 90 | global_position = player.global_position # Clipping, disorienting! |
| 91 | |
| 92 | # ✅ GOOD: Third-person camera with SpringArm3D |
| 93 | # Scene structure: |
| 94 | # Player (CharacterBody3D) |
| 95 | # └─ SpringArm3D |
| 96 | # └─ Camera3D |
| 97 | |
| 98 | # player.gd |
| 99 | extends CharacterBody3D |
| 100 | |
| 101 | @onready var spring_arm: SpringArm3D = $SpringArm3D |
| 102 | @onready var camera: C |