godot

Develop, test, build, and deploy Godot 4.x games.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "godot" with this command: npx skills add randroids-dojo/skills/randroids-dojo-skills-godot

Godot Skill

Develop, test, build, and deploy Godot 4.x games.

Quick Reference

GdUnit4 - Unit testing framework (GDScript, runs inside Godot)

godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --run-tests

PlayGodot - Game automation framework (Python, like Playwright for games)

export GODOT_PATH=/path/to/godot-automation-fork pytest tests/ -v

Export web build

godot --headless --export-release "Web" ./build/index.html

Deploy to Vercel

vercel deploy ./build --prod

Testing Overview

GdUnit4 PlayGodot

Type Unit testing Game automation

Language GDScript Python

Runs Inside Godot External (like Playwright)

Requires Addon Custom Godot fork

Best for Unit/component tests E2E/integration tests

GdUnit4 (GDScript Tests)

GdUnit4 runs tests written in GDScript directly inside Godot.

Project Structure

project/ ├── addons/gdUnit4/ # GdUnit4 addon ├── test/ # Test directory │ ├── game_test.gd │ └── player_test.gd └── scripts/ └── game.gd

Setup

Install GdUnit4

git clone --depth 1 https://github.com/MikeSchulze/gdUnit4.git addons/gdUnit4

Enable plugin in Project Settings → Plugins

Basic Unit Test

test/game_test.gd

extends GdUnitTestSuite

var game: Node

func before_test() -> void: game = auto_free(load("res://scripts/game.gd").new())

func test_initial_state() -> void: assert_that(game.is_game_active()).is_true() assert_that(game.get_current_player()).is_equal("X")

func test_make_move() -> void: var success := game.make_move(4) assert_that(success).is_true() assert_that(game.get_board_state()[4]).is_equal("X")

Scene Test with Input Simulation

test/game_scene_test.gd

extends GdUnitTestSuite

var runner: GdUnitSceneRunner

func before_test() -> void: runner = scene_runner("res://scenes/main.tscn")

func after_test() -> void: runner.free()

func test_click_cell() -> void: await runner.await_idle_frame()

var cell = runner.find_child("Cell4")
runner.set_mouse_position(cell.global_position + cell.size / 2)
runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
await runner.await_input_processed()

var game = runner.scene()
assert_that(game.get_board_state()[4]).is_equal("X")

func test_keyboard_restart() -> void: runner.simulate_key_pressed(KEY_R) await runner.await_input_processed() assert_that(runner.scene().is_game_active()).is_true()

Running GdUnit4 Tests

All tests

godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --run-tests

Specific test file

godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd
--run-tests --add res://test/my_test.gd

Generate reports for CI

godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd
--run-tests --report-directory ./reports

GdUnit4 Assertions

Values

assert_that(value).is_equal(expected) assert_that(value).is_not_null() assert_that(condition).is_true()

Numbers

assert_that(number).is_greater(5) assert_that(number).is_between(1, 100)

Strings

assert_that(text).contains("expected") assert_that(text).starts_with("prefix")

Arrays

assert_that(array).contains(element) assert_that(array).has_size(5)

Signals

await assert_signal(node).is_emitted("signal_name")

Scene Runner Input API

Mouse

runner.set_mouse_position(Vector2(100, 100)) runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT) runner.simulate_mouse_button_released(MOUSE_BUTTON_LEFT)

Keyboard

runner.simulate_key_pressed(KEY_SPACE) runner.simulate_key_pressed(KEY_S, false, true) # Ctrl+S

Input actions

runner.simulate_action_pressed("jump") runner.simulate_action_released("jump")

Waiting

await runner.await_input_processed() await runner.await_idle_frame() await runner.await_signal("game_over", [], 5000)

PlayGodot (Game Automation)

PlayGodot is a game automation framework for Godot - like Playwright, but for games. It enables E2E testing, automated gameplay, and external control of Godot games via the native RemoteDebugger protocol.

Requirements:

  • Custom Godot fork: Randroids-Dojo/godot (automation branch)

  • PlayGodot Python library

Setup

Install PlayGodot

python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install playgodot

Option 1: Download pre-built binary (recommended)

See releases: https://github.com/Randroids-Dojo/godot/releases/tag/automation-latest

- godot-automation-linux-x86_64.zip

- godot-automation-macos-universal.zip (Intel + Apple Silicon)

Option 2: Build custom Godot fork from source

git clone https://github.com/Randroids-Dojo/godot.git cd godot && git checkout automation scons platform=macos arch=arm64 target=editor -j8 # macOS Apple Silicon

scons platform=macos arch=x86_64 target=editor -j8 # macOS Intel

scons platform=linuxbsd target=editor -j8 # Linux

scons platform=windows target=editor -j8 # Windows

Test Configuration (conftest.py)

import os import pytest_asyncio from pathlib import Path from playgodot import Godot

GODOT_PROJECT = Path(file).parent.parent GODOT_PATH = os.environ.get("GODOT_PATH", "/path/to/godot-fork")

@pytest_asyncio.fixture async def game(): async with Godot.launch( str(GODOT_PROJECT), headless=True, timeout=15.0, godot_path=GODOT_PATH, ) as g: await g.wait_for_node("/root/Game") yield g

Writing PlayGodot Tests

import pytest

GAME = "/root/Game"

@pytest.mark.asyncio async def test_game_starts_empty(game): board = await game.call(GAME, "get_board_state") assert board == ["", "", "", "", "", "", "", "", ""]

@pytest.mark.asyncio async def test_clicking_cell(game): await game.click("/root/Game/VBoxContainer/GameBoard/GridContainer/Cell4") board = await game.call(GAME, "get_board_state") assert board[4] == "X"

@pytest.mark.asyncio async def test_game_win(game): for pos in [0, 3, 1, 4, 2]: # X wins top row await game.call(GAME, "make_move", [pos])

is_active = await game.call(GAME, "is_game_active")
assert is_active is False

Running PlayGodot Tests

export GODOT_PATH=/path/to/godot-automation-fork pytest tests/ -v pytest tests/test_game.py::test_clicking_cell -v

PlayGodot API

Node interaction

node = await game.get_node("/root/Game") await game.wait_for_node("/root/Game", timeout=10.0) exists = await game.node_exists("/root/Game") result = await game.call("/root/Node", "method", [arg1, arg2]) value = await game.get_property("/root/Node", "property") await game.set_property("/root/Node", "property", value)

Node queries

paths = await game.query_nodes("Button") count = await game.count_nodes("Label")

Mouse input

await game.click("/root/Button") await game.click(300, 200) await game.double_click("/root/Button") await game.right_click(100, 100) await game.drag("/root/Item", "/root/Slot")

Keyboard input

await game.press_key("space") await game.press_key("ctrl+s") await game.type_text("hello")

Input actions

await game.press_action("jump") await game.hold_action("sprint", 2.0)

Touch input

await game.tap(300, 200) await game.swipe(100, 100, 400, 100) await game.pinch((200, 200), 0.5)

Screenshots

png_bytes = await game.screenshot() await game.screenshot("/tmp/screenshot.png") similarity = await game.compare_screenshot("expected.png") await game.assert_screenshot("reference.png", threshold=0.99)

Scene management

scene = await game.get_current_scene() await game.change_scene("res://scenes/level2.tscn") await game.reload_scene()

Game state

await game.pause() await game.unpause() is_paused = await game.is_paused() await game.set_time_scale(0.5) scale = await game.get_time_scale()

Waiting

await game.wait_for_node("/root/Game/SpawnedEnemy", timeout=5.0) await game.wait_for_visible("/root/Game/UI/GameOverPanel", timeout=10.0) await game.wait_for_signal("game_over") await game.wait_for_signal("health_changed", source="/root/Game/Player")

Building & Deployment

Web Export

Requires export_presets.cfg with Web preset

godot --headless --export-release "Web" ./build/index.html

Export Preset (export_presets.cfg)

[preset.0] name="Web" platform="Web" runnable=true export_path="build/index.html"

Deploy to Vercel

npm i -g vercel vercel deploy ./build --prod

CI/CD

GitHub Actions Example

  • name: Setup Godot uses: chickensoft-games/setup-godot@v2 with: version: 4.3.0 include-templates: true

  • name: Run GdUnit4 Tests run: | godot --headless --path .
    -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd
    --run-tests --report-directory ./reports

  • name: Upload Results uses: actions/upload-artifact@v4 if: always() with: name: test-results path: reports/

References

  • references/gdunit4-quickstart.md

  • GdUnit4 setup

  • references/scene-runner.md

  • Input simulation API

  • references/assertions.md

  • Assertion methods

  • references/playgodot.md

  • PlayGodot guide

  • references/deployment.md

  • Deployment guide

  • references/ci-integration.md

  • CI/CD setup

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

unreal

No summary provided by upstream source.

Repository SourceNeeds Review
General

loop

No summary provided by upstream source.

Repository SourceNeeds Review
General

task-tracking-dots

No summary provided by upstream source.

Repository SourceNeeds Review
General

slipbox

No summary provided by upstream source.

Repository SourceNeeds Review