Skip to content
godot-generate-tests logo

Generate Godot Tests

godot-generate-tests

Use when developing Godot games and need comprehensive test coverage for GDScript classes, signals, scene initialization, and integration flows. Generates GUT framework unit tests, integration tests, mock/stub helpers, and CI/CD test runner configuration.

Asreonn/godot-superpowers0installs4stars

SKILL.md

Full skill instructions

Generate Godot Tests

Core Principle

Every public method should be tested. Tests prove your code works and protect against regressions when refactoring.

What This Skill Does

Generates comprehensive test suites for Godot GDScript code:

  • Unit tests for public methods with assertions
  • Signal connection and emission tests
  • Scene initialization and lifecycle tests
  • Physics/​Process loop behavior tests
  • Mock and stub helpers for dependencies
  • GUT framework configuration
  • CI/​CD integration for automated testing

Test Generation Patterns

Unit Tests for Public Methods

# Tests for player.gd
extends GutTest

var player: Player

func before_each():
    player = Player.new()
    add_child_autofree(player)

func test_take_damage_reduces_health():
    player.health = 100
    player.take_damage(25)
    assert_eq(player.health, 75, "Health should decrease by damage amount")

func test_take_damage_does_not_go_below_zero():
    player.health = 10
    player.take_damage(25)
    assert_eq(player.health, 0, "Health should not go below zero")

func test_is_alive_returns_true_when_health_positive():
    player.health = 1
    assert_true(player.is_alive(), "Should be alive with positive health")

func test_is_alive_returns_false_when_health_zero():
    player.health = 0
    assert_false(player.is_alive(), "Should not be alive with zero health")

Signal Connection Tests

# Tests for signal connections and emissions
extends GutTest

var player: Player
var signal_received: bool = false
var signal_args: Array = []

func before_each():
    player = Player.new()
    add_child_autofree(player)
    signal_received = false
    signal_args.clear()

func _on_health_changed(new_health: int):
    signal_received = true
    signal_args.append(new_health)

func test_health_changed_signal_emitted_on_damage():
    player.health = 100
    player.health_changed.connect(_on_health_changed)
    player.take_damage(25)
    assert_true(signal_received, "health_changed signal should be emitted")
    assert_eq(signal_args[0], 75, "Signal should pass new health value")

func test_died_signal_emitted_when_health_reaches_zero():
    var died_received = false
    player.died.connect(func(): died_received = true)
    player.health = 10
    player.take_damage(10)
    assert_true(died_received, "died signal should be emitted when health reaches zero")

Scene Initialization Tests

# Tests for scene setup and node references
extends GutTest

var player_scene: PackedScene
var player: Player

func before_all():
    player_scene = load("res://scenes/​player.tscn")

func before_each():
    player = player_scene.instantiate()
    add_child_autofree(player)

func test_player_has_required_nodes():
    assert_not_null(player.get_node_or_null("Sprite2D"), "Player should have Sprite2D")
    assert_not_null(player.get_node_or_null("CollisionShape2D"), "Player should have CollisionShape2D")
    assert_not_null(player.get_node_or_null("AnimationPlayer"), "Player should have AnimationPlayer")

func test_player_initializes_with_correct_health():
    assert_eq(player.health, 100, "Health should initialize to 100")
    assert_eq(player.max_health, 100, "Max health should initialize to 100")

func test_player_animation_player_has_idle_animation():
    var anim_player = player.get_node("AnimationPlayer")
    assert_true(anim_player.has_animation("idle"), "Should have idle animation")

Physics/​Process Loop Tests

# Tests for _physics_process and _process behavior
extends GutTest

var player: Player

func before_each():
    player = Player.new()
    add_child_autofree(player)

func test_velocity_applies_gravity_in_physics_process():
    player.velocity = Vector2(0, 0)
    player.gravity = 980.0
    
    # Simulate one physics frame (at 60 FPS)
    var delta = 1.0 / 60.0
    player._physics_process(delta)
    
    assert_almost_eq(player.velocity.y, 980.0 * delta, 0.01, "Velocity should increase by gravity * delta")

func test_move_and_slide_is_called_in_physics_process():
    # Mock the move_and_slide method
    var move_and_slide_called = false
    player.move_and_slide = func() -> bool:
        move_and_slide_called = true
        return true
    
    player._physics_process(0.016)
    assert_true(move_and_slide_called, "move_and_slide should be called")

GUT Framework Setup

Test File Structure

project/
├── addons/
│   └── gut/
│       └── ...
├── tests/
│   ├── unit/
│   │   ├── test_player.gd
│   │   ├── test_enemy.gd
│   │   └── test_inventory.gd
│   ├── integration/
│   │   ├── test_combat_system.gd
│   │   └── test_save_load.gd
│   ├── mocks/
│   │   ├── mock_player.gd
│   │   └── mock_enemy.gd
│   └── test_runner.gd
└── .gutconfig.json

GUT Configuration

{
  "dirs": ["res://tests/​unit", "res://tests/​integration"],
  "prefix": "test_",
  "suffix": ".gd",
  "ignore_subdirs": ["mocks"],
  "log_level": 2,
  "should_exit": true,
  "should_exit_on_success": true,
  "compact_mode": false,
  "double_strategy": "partial",
  "pre_run_script": "",
  "post_run_script": ""
}

Common GUT Assertions

# Equality
assert_eq(actual, expected, "message")
assert_ne(actual, expected, "message")
assert_almost_eq(actual, expected, 0.01, "message")

# Boolean
assert_true(condition, "message")
assert_false(condition, "message")

# Null/​Not Null
assert_null(value, "message")
assert_not_null(value, "message")

# Type
assert_is_instance_of(object, Class, "message")
assert_is_not_instance_of(object, Class, "message")

# String
assert_string_contains(string, substring, "message")
assert_string_begins_with(string, prefix, "message")
assert_string_ends_with(string, suffix, "message")

# Signal
assert_signal_emitted(object, "signal_name", "message")
assert_signal_not_emitted(object, "signal_name", "message")
assert_signal_emitted_with_parameters(object, "signal_name", [arg1, arg2])

# File
assert_file_exists("res://path/​to/​file")
assert_file_does_not_exist("res://path/​to/​file")

Test Templates

Class Test Template

# tests/​unit/​test_{class_name}.gd
extends GutTest

var {class_instance}: {ClassName}

func before_all():
    # Runs once before all tests in this file
    pass

func before_each():
    # Runs before each test
    {class_instance} = {ClassName}.new()
    add_child_autofree({class_instance})

func after_each():
    # Runs after each test
    # Autofree handles cleanup
    pass

func after_all():
    # Runs once after all tests
    pass

# Test public methods
func test_{method_name}_{expected_behavior}():
    # Arrange
    {class_instance}.{setup_method}()
    
    # Act
    var result = {class_instance}.{method_name}()
    
    # Assert
    assert_eq(result, expected_value, "message")

# Test signals
func test_{signal_name}_emitted_when_{condition}():
    var signal_received = false
    {class_instance}.{signal_name}.connect(func(): signal_received = true)
    
    # Trigger condition
    {class_instance}.{trigger_method}()
    
    assert_true(signal_received, "message")

Signal Test Template

# Signal-specific tests
test_{signal_name}_connections:
  - Connects to {target_node} on ready
  - Emits with correct parameters
  - Disconnects on exit
  - Can be connected multiple times
  - Callback receives expected data

Scene Test Template

# tests/​unit/​test_{scene_name}_scene.gd
extends GutTest

var {scene_name}_scene: PackedScene
var {scene_instance}: Node

func before_all():
    {scene_name}_scene = load("res://{path}/​{scene_name}.tscn")
    assert_not_null({scene_name}_scene, "Scene should load successfully")

func before_each():
    {scene_instance} = {scene_name}_scene.instantiate()
    add_child_autofree({scene_instance})

func test_scene_has_required_children():
    # Verify node hierarchy
    assert_not_null({scene_instance}.get_node_or_null("{ChildNode}"), "Should have {ChildNode}")

func test_scene_initial_state():
    # Verify initial property values
    assert_eq({scene_instance}.{property}, {expected_value}, "Initial value should be correct")

func test_scene_ready_initializes_components():
    # Simulate ready
    await get_tree().process_frame
    
    # Verify initialization
    assert_true({scene_instance}.{component}.is_initialized, "Component should initialize on ready")

Mock/​Stub Generation

Mock Class Template

# tests/​mocks/​mock_{class_name}.gd
class_name Mock{ClassName}
extends {BaseClass}

# Mock state tracking
var {method_name}_calls: Array = []
var {method_name}_return_value = null

func {method_name}(args):
    {method_name}_calls.append(args)
    return {method_name}_return_value

# Helper to configure return value
func set_{method_name}_return(value):
    {method_name}_return_value = value

# Helper to verify calls
func assert_{method_name}_called(times: int = 1):
    assert_eq({method_name}_calls.size(), times, "Expected {method_name} to be called {times} times")

func assert_{method_name}_called_with(args):
    var found = false
    for call in {method_name}_calls:
        if call == args:
            found = true
            break
    assert_true(found, "Expected {method_name} to be called with {args}")

Stub Helper for Dependencies

# tests/​mocks/​stub_helpers.gd
class_name StubHelpers

static func stub_player() -> Player:
    var player = Player.new()
    player.health = 100
    player.max_health = 100
    player.speed = 200
    player.damage = 10
    return player

static func stub_enemy(enemy_type: String = "basic") -> Enemy:
    var enemy = Enemy.new()
    enemy.enemy_type = enemy_type
    match enemy_type:
        "basic":
            enemy.health = 50
            enemy.damage = 5
        "boss":
            enemy.health = 500
            enemy.damage = 25
    return enemy

static func stub_weapon(weapon_type: String) -> Weapon:
    var weapon = Weapon.new()
    weapon.weapon_type = weapon_type
    weapon.damage = _get_weapon_damage(weapon_type)
    return weapon

static func _get_weapon_damage(weapon_type: String) -> int:
    match weapon_type:
        "sword": return 15
        "bow": return 10
        "staff": return 20
        _: return 5

Test Double with Partial Mocking

# Using GUT's partial double for selective mocking
extends GutTest

var player: Player

func before_each():
    # Create partial double - only mock specific methods
    player = partial_double(Player).instantiate()
    add_child_autofree(player)

func test_player_uses_real_movement_but_mocked_combat():
    # Real movement
    player.velocity = Vector2(100, 0)
    player._physics_process(0.016)
    
    # Mocked combat - stub the take_damage method
    stub(player, "take_damage").to_return(false)
    
    # Test that combat uses stub
    var result = player.take_damage(100)
    assert_eq(result, false, "Should use stubbed return value")

Example Transformations

Example 1: Player Combat Class

Before (Script):

# player.gd
class_name Player
extends CharacterBody2D

@export var health: int = 100
@export var max_health: int = 100
@export var damage: int = 10

signal health_changed(new_health: int)
signal died

func take_damage(amount: int) -> void:
    health = max(0, health - amount)
    health_changed.emit(health)
    if health == 0:
        died.emit()

func heal(amount: int) -> void:
    health = min(max_health, health + amount)
    health_changed.emit(health)

func is_alive() -> bool:
    return health > 0

func attack(target: Node) -> void:
    if target.has_method("take_damage"):
        target.take_damage(damage)

After (Test File):

# tests/​unit/​test_player.gd
extends GutTest

var player: Player

func before_each():
    player = Player.new()
    add_child_autofree(player)

func test_take_damage_reduces_health():
    player.health = 100
    player.take_damage(25)
    assert_eq(player.health, 75)

func test_take_damage_clamps_at_zero():
    player.health = 10
    player.take_damage(25)
    assert_eq(player.health, 0)

func test_heal_increases_health():
    player.health = 50
    player.heal(25)
    assert_eq(player.health, 75)

func test_heal_clamps_at_max_health():
    player.health = 90
    player.max_health = 100
    player.heal(25)
    assert_eq(player.health, 100)

func test_is_alive_true_with_health():
    player.health = 1
    assert_true(player.is_alive())

func test_is_alive_false_with_zero_health():
    player.health = 0
    assert_false(player.is_alive())

func test_health_changed_signal_emitted_on_damage():
    watch_signals(player)
    player.take_damage(25)
    assert_signal_emitted(player, "health_changed")

func test_died_signal_emitted_on_fatal_damage():
    watch_signals(player)
    player.health = 25
    player.take_damage(25)
    assert_signal_emitted(player, "died")

func test_attack_calls_take_damage_on_target():
    var mock_target = partial_double(CharacterBody2D).instantiate()
    add_child_autofree(mock_target)
    stub(mock_target, "take_damage").to_do_nothing()
    
    player.attack(mock_target)
    
    assert_called(mock_target, "take_damage")

Example 2: Inventory System

Before (Script):

# inventory.gd
class_name Inventory
extends Node

signal item_added(item: Item, slot: int)
signal item_removed(item: Item, slot: int)
signal inventory_full

const MAX_SLOTS = 20
var items: Array[Item] = []

func add_item(item: Item) -> bool:
    if items.size() >= MAX_SLOTS:
        inventory_full.emit()
        return false
    
    items.append(item)
    item_added.emit(item, items.size() - 1)
    return true

func remove_item(slot: int) -> Item:
    if slot < 0 or slot >= items.size():
        return null
    
    var item = items[slot]
    items.remove_at(slot)
    item_removed.emit(item, slot)
    return item

func has_item(item_name: String) -> bool:
    for item in items:
        if item.name == item_name:
            return true
    return false

func get_item_count() -> int:
    return items.size()

func is_full() -> bool:
    return items.size() >= MAX_SLOTS

After (Test File):

# tests/​unit/​test_inventory.gd
extends GutTest

var inventory: Inventory

func before_each():
    inventory = Inventory.new()
    add_child_autofree(inventory)

func test_add_item_returns_true_when_space_available():
    var item = Item.new()
    item.name = "Sword"
    assert_true(inventory.add_item(item))

func test_add_item_returns_false_when_full():
    # Fill inventory
    for i in range(Inventory.MAX_SLOTS):
        inventory.add_item(Item.new())
    
    var result = inventory.add_item(Item.new())
    assert_false(result)

func test_add_item_emits_item_added_signal():
    watch_signals(inventory)
    var item = Item.new()
    item.name = "Sword"
    
    inventory.add_item(item)
    
    assert_signal_emitted_with_parameters(inventory, "item_added", [item, 0])

func test_add_item_emits_inventory_full_when_no_space():
    watch_signals(inventory)
    for i in range(Inventory.MAX_SLOTS):
        inventory.add_item(Item.new())
    
    inventory.add_item(Item.new())
    assert_signal_emitted(inventory, "inventory_full")

func test_remove_item_returns_item_at_slot():
    var item = Item.new()
    item.name = "Sword"
    inventory.add_item(item)
    
    var removed = inventory.remove_item(0)
    assert_eq(removed, item)

func test_remove_item_returns_null_for_invalid_slot():
    assert_null(inventory.remove_item(-1))
    assert_null(inventory.remove_item(100))

func test_remove_item_emits_item_removed_signal():
    var item = Item.new()
    item.name = "Sword"
    inventory.add_item(item)
    watch_signals(inventory)
    
    inventory.remove_item(0)
    assert_signal_emitted_with_parameters(inventory, "item_removed", [item, 0])

func test_has_item_returns_true_when_item_present():
    var item = Item.new()
    item.name = "Sword"
    inventory.add_item(item)
    
    assert_true(inventory.has_item("Sword"))

func test_has_item_returns_false_when_item_not_present():
    assert_false(inventory.has_item("NonExistent"))

func test_get_item_count_returns_number_of_items():
    assert_eq(inventory.get_item_count(), 0)
    inventory.add_item(Item.new())
    assert_eq(inventory.get_item_count(), 1)
    inventory.add_item(Item.new())
    assert_eq(inventory.get_item_count(), 2)

func test_is_full_returns_true_at_capacity():
    for i in range(Inventory.MAX_SLOTS):
        inventory.add_item(Item.new())
    assert_true(inventory.is_full())

func test_is_full_returns_false_when_not_full():
    assert_false(inventory.is_full())

Example 3: Integration Test - Combat System

Integration Test:

# tests/​integration/​test_combat_system.gd
extends GutTest

var player: Player
var enemy: Enemy
var combat_manager: CombatManager

func before_each():
    player = load("res://scenes/​player.tscn").instantiate()
    enemy = load("res://scenes/​enemy.tscn").instantiate()
    combat_manager = CombatManager.new()
    
    add_child_autofree(player)
    add_child_autofree(enemy)
    add_child_autofree(combat_manager)
    
    combat_manager.player = player
    combat_manager.enemy = enemy

func test_player_attack_damages_enemy():
    enemy.health = 100
    var initial_health = enemy.health
    
    player.attack(enemy)
    
    assert_lt(enemy.health, initial_health, "Enemy should take damage from player attack")

func test_enemy_death_emits_signal():
    watch_signals(enemy)
    enemy.health = 1
    
    player.attack(enemy)
    
    assert_signal_emitted(enemy, "died")

func test_combat_manager_tracks_damage_dealt():
    player.damage = 25
    enemy.health = 100
    
    combat_manager.initiate_combat()
    player.attack(enemy)
    
    assert_eq(combat_manager.damage_dealt, 25, "Combat manager should track damage dealt")

func test_combat_ends_when_enemy_dies():
    enemy.health = 1
    
    combat_manager.initiate_combat()
    player.attack(enemy)
    
    assert_true(combat_manager.is_combat_ended, "Combat should end when enemy dies")

CI/​CD Integration

GitHub Actions Workflow

# .github/​workflows/​godot-tests.yml
name: Godot Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/​checkout@v4
      
      - name: Setup Godot
        uses: chickensoft-games/​setup-godot@v1
        with:
          version: 4.2.1
          use-dotnet: false
      
      - name: Install GUT
        run: |
          git clone https://github.com/​bitwes/​Gut.git addons/​gut
          # Or use your project's specific GUT version
      
      - name: Run Tests
        run: |
          godot --headless --script addons/​gut/​gut_cmdln.gd -gexit
      
      - name: Upload Test Results
        uses: actions/​upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: gut_logs/

GitLab CI Configuration

# .gitlab-ci.yml
test:godot:
  image: barichello/​godot-ci:4.2.1
  script:
    - git clone https://github.com/​bitwes/​Gut.git addons/​gut
    - godot --headless --script addons/​gut/​gut_cmdln.gd -gexit
  artifacts:
    when: always
    paths:
      - gut_logs/
    expire_in: 1 week

Test Runner Script

# tests/​test_runner.gd
extends SceneTree

func _init():
    var gut = load("res://addons/​gut/​gut.gd").new()
    gut.connect("tests_finished", _on_tests_finished)
    
    # Configure from .gutconfig.json
    var config = _load_config()
    
    for dir in config.dirs:
        gut.add_directory(dir)
    
    gut.set_yield_between_tests(true)
    gut.set_exit_on_success(config.should_exit_on_success)
    
    root.add_child(gut)
    gut.test_scripts()

func _on_tests_finished():
    quit()

func _load_config() -> Dictionary:
    var file = FileAccess.open("res://.gutconfig.json", FileAccess.READ)
    if file:
        return JSON.parse_string(file.get_as_text())
    return {}

When to Use

You're Adding New Features

Generate tests alongside new code to ensure correctness.

You're Refactoring Legacy Code

Create tests before refactoring to verify behavior doesn't change.

You Need Confidence in Changes

Comprehensive test suite prevents regressions.

You're Setting Up CI/​CD

Automated testing ensures code quality on every commit.

You're Practicing TDD

Generate test templates to speed up red-green-refactor cycle.

When NOT to Use

Prototype/​Throwaway Code

Don't test code that will be rewritten.

Simple Setters/​Getters

Testing trivial property access adds noise without value.

Editor-Only Tools

Code that only runs in editor doesn't need runtime tests.

Visual/​Art Code

Purely visual changes are better tested manually.

Safety

  • Generated tests are starting points - review and customize
  • Tests include comments explaining expected behavior
  • Edge cases are documented with specific test cases
  • Auto-rollback available if tests break existing code
  • Git commits track test file generation

Integration

Works with:

  • godot-extract-to-scenes - Generate tests for extracted components
  • godot-split-scripts - Create tests for each split module
  • godot-refactor (orchestrator) - Comprehensive testing after refactoring
  • godot-add-signals - Test signal connections and emissions

Process

  1. Scan - Find all .gd files in project
  2. Analyze - Identify public methods, signals, and node references
  3. Generate - Create test files with GUT framework structure
  4. Mock - Generate stub helpers for dependencies
  5. Configure - Create .gutconfig.json and CI/​CD workflows
  6. Commit - Git commit with test suite

Generated Structure

tests/
├── unit/
│   ├── test_{class1}.gd      # Unit tests for each class
│   ├── test_{class2}.gd
│   └── ...
├── integration/
│   ├── test_{system1}.gd     # Integration test suites
│   └── ...
├── mocks/
│   ├── mock_{class1}.gd      # Mock implementations
│   ├── stub_helpers.gd       # Test fixture utilities
│   └── ...
├── scenes/
│   ├── test_{scene1}.gd      # Scene instantiation tests
│   └── ...
└── test_runner.gd            # CLI test runner

More skills from Asreonn

godot-sync-camera-positions logo
Asreonn/godot-superpowers

godot-sync-camera-positions

Use when Godot background layers or elements follow the camera at runtime but show incorrect position in editor. Detects camera-following patterns and syncs editor position to match typical runtime state. Makes editor preview accurately show what players will see during gameplay.

4 0
View
godot-refactor logo
Asreonn/godot-superpowers

godot-refactor

Use when Godot project has code-created objects, tight coupling via direct references, monolithic scripts over 150 lines, or needs scene-first architecture with signals and composition. Orchestrates all 5 code quality mini-skills: extract-to-scenes, split-scripts, add-signals, extract-resources, ...

4 0
View
godot-split-scripts logo
Asreonn/godot-superpowers

godot-split-scripts

Use when Godot scripts exceed 150 lines or have multiple responsibilities. Detects scripts doing too many things (player movement + combat + inventory + UI). Automatically splits into focused scripts following Single Responsibility Principle, preserves behavior exactly, and creates git commits pe...

4 0
View
godot-convert-shaders logo
Asreonn/godot-superpowers

godot-convert-shaders

Converts Godot 3.x shader code to Godot 4.x shader syntax. Updates SCREEN_TEXTURE to screen_texture uniform, DEPTH_TEXTURE to depth_texture uniform, texture() function changes, built-in varyings updates, and light function signature changes. Essential for rendering and visual effect migrations.

4 0
View
godot-migrate-tilemap logo
Asreonn/godot-superpowers

godot-migrate-tilemap

Migrates legacy TileMap (Godot 4.0-4.2) to new TileMapLayer system (Godot 4.3+). Handles TileMap node conversion, API migration (set_cell to set_cells_terrain_connect), TileSet resource updates, physics layer mapping, navigation layer conversion, and custom data preservation. Essential for upgrad...

4 0
View
godot-setup-animationtree logo
Asreonn/godot-superpowers

godot-setup-animationtree

Use when setting up AnimationTree nodes for state machine-driven animation, creating AnimationNodeStateMachine graphs, configuring BlendSpace2D/3D for locomotion blending, implementing state transition conditions, or creating animation blend trees for complex animation mixing. Supports player cha...

4 0
View
godot-extract-resources logo
Asreonn/godot-superpowers

godot-extract-resources

Use when Godot code has hardcoded game data in const arrays, dictionaries, or embedded values. Detects inline data like enemy stats, item definitions, level configurations. Automatically extracts to .tres resource files, making data visible in editor, easily modifiable, and supports data-driven d...

4 0
View
godot-modernize-input logo
Asreonn/godot-superpowers

godot-modernize-input

Use when handling input in Godot 4.x projects and need to modernize patterns. Generates Input Map configurations from hardcoded key checks, sets up joypad rumble (Godot 4.x feature), implements gyroscope/accelerometer input on mobile, creates context-sensitive input handling for different game st...

4 0
View
godot-sync-parallax logo
Asreonn/godot-superpowers

godot-sync-parallax

Use when Godot ParallaxBackground/ParallaxLayer nodes show incorrect positions in editor due to parallax multipliers. Calculates correct editor positions based on camera start position and parallax motion scale. Makes parallax layers preview realistically in editor, matching gameplay appearance.

4 0
View
godot-setup-export logo
Asreonn/godot-superpowers

godot-setup-export

Use when setting up multi-platform exports for Godot projects, configuring export presets, icons, feature tags, build scripts, or CI/CD pipelines

4 0
View
godot-fix-positions logo
Asreonn/godot-superpowers

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...

4 0
View
godot-organize-assets logo
Asreonn/godot-superpowers

godot-organize-assets

Use when Godot project has disorganized assets (sprites, audio, fonts, materials). Detects scattered asset files and organizes them into clear categories with consistent naming. Creates sprite atlases where beneficial, organizes audio by type, and groups related assets together.

4 0
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View