All skills
thedivergentai avatar

/godot-adapt-2d-to-3d

@9d6e91e

Expert 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. Trigger keywords: CharacterBody2D to CharacterBody3D, Area2D to Area3D, Camera2D to Camera3D, Vector2 to Vector3, collision_layer migration, sprite to MeshInstance3D, 2D to 3D conversion.

Use this Skill: https://skilld.dev/gh/thedivergentai/gd-agentic-skills/godot-adapt-2d-to-3d

This session only. Nothing lands on disk.

referencesmigration-recipes.md

≈1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Migration Recipes

Migration Steps

Step 1: Physics Layer Reconfiguration

# 2D collision layers are SEPARATE from 3D
# You must reconfigure in Project Settings → Layer Names → 3D Physics

# Before (2D):
# Layer 1: Player
# Layer 2: Enemies
# Layer 3: World

# After (3D) - same names, but different system
# In code, update all collision layer references:

# 2D version:
# collision_layer = 0b0001

# 3D version (same logic, different node):
var character_3d := CharacterBody3D.new()
character_3d.collision_layer = 0b0001  # Layer 1: Player
character_3d.collision_mask = 0b0110   # Detect Enemies + World

Step 2: Camera Conversion

# ❌ BAD: Direct 2D follow logic
extends Camera3D

@onready var player: Node3D = $"../Player"

func _process(delta: float) -> void:
    global_position = player.global_position  # Clipping, disorienting!

# ✅ GOOD: Third-person camera with SpringArm3D
# Scene structure:
# Player (CharacterBody3D)
#   └─ SpringArm3D
#       └─ Camera3D

# player.gd
extends CharacterBody3D

@onready var spring_arm: SpringArm3D = $SpringArm3D
@onready var camera: Camera3D = $SpringArm3D/Camera3D

func _ready() -> void:
    spring_arm.spring_length = 10.0  # Distance from player
    spring_arm.position = Vector3(0, 2, 0)  # Above player

func _unhandled_input(event: InputEvent) -> void:
    if event is InputEventMouseMotion:
        spring_arm.rotate_y(-event.relative.x * 0.005)  # Horizontal rotation
        spring_arm.rotate_object_local(Vector3.RIGHT, -event.relative.y * 0.005)  # Vertical
        
        # Clamp vertical rotation
        spring_arm.rotation.x = clamp(spring_arm.rotation.x, -PI/3, PI/6)

Step 3: Movement Conversion

# 2D platformer movement
extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

func _physics_process(delta: float) -> void:
    if not is_on_floor():
        velocity.y += gravity * delta
    
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY
    
    var direction := Input.get_axis("left", "right")
    velocity.x = direction * SPEED
    
    move_and_slide()

# ✅ 3D equivalent (third-person platformer)
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5
const GRAVITY = 9.8

@onready var spring_arm: SpringArm3D = $SpringArm3D

func _physics_process(delta: float) -> void:
    if not is_on_floor():
        velocity.y -= GRAVITY * delta
    
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY
    
    # Movement relative to camera direction
    var input_dir := Input.get_vector("left", "right", "forward", "back")
    var camera_basis := spring_arm.global_transform.basis
    var direction := (camera_basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
    
    if direction:
        velocity.x = direction.x * SPEED
        velocity.z = direction.z * SPEED
        
        # Rotate player to face movement direction
        rotation.y = lerp_angle(rotation.y, atan2(-direction.x, -direction.z), 0.1)
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        velocity.z = move_toward(velocity.z, 0, SPEED)
    
    move_and_slide()

Performance Budgeting

2D vs 3D Performance

Metric 2D Budget 3D Budget Notes
Draw calls 100-200 50-100 Use fewer meshes
Vertices Unlimited 100K-500K LOD important
Lights N/A 3-5 shadowed Expensive
Transparent objects Many <10 Sorting overhead
Particle systems Many 2-3 max GPU godot-particles only

Optimization Checklist

# 1. Use LOD for distant objects
var mesh_instance := MeshInstance3D.new()
mesh_instance.lod_bias = 1.0  # Lower detail sooner

# 2. Occlusion culling
# Use OccluderInstance3D for large walls/buildings

# 3. Reduce shadow distance
var sun := DirectionalLight3D.new()
sun.directional_shadow_max_distance = 50.0  # Don't render far shadows

# 4. Use unlit materials for distant objects
var material := StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED

Source: SKILL.md on GitHub

1 warning17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    The skill provides a collection of Godot scripts and architectural patterns for converting 2D games to 3D. It covers node conversion, camera systems, physics layer migration, and UI projection. No security risks were identified.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    3/3 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 9d6e91e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 weeks ago.

Activeupdated 2 months ago

README badge

README badge for thedivergentai/gd-agentic-skills/godot-adapt-2d-to-3d