重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
godot by randroids-dojo/skills
npx skills add https://github.com/randroids-dojo/skills --skill godot开发、测试、构建和部署 Godot 4.x 游戏。
# GdUnit4 - 单元测试框架 (GDScript,在 Godot 内运行)
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --run-tests
# PlayGodot - 游戏自动化框架 (Python,类似于游戏的 Playwright)
export GODOT_PATH=/path/to/godot-automation-fork
pytest tests/ -v
# 导出 Web 构建
godot --headless --export-release "Web" ./build/index.html
# 部署到 Vercel
vercel deploy ./build --prod
| GdUnit4 | PlayGodot
---|---|---
类型 | 单元测试 | 游戏自动化
语言 | GDScript | Python
运行环境 | Godot 内部 | 外部 (类似 Playwright)
要求 | 插件 | 自定义 Godot 分支
最适合 | 单元/组件测试 | E2E/集成测试
GdUnit4 直接在 Godot 内部运行用 GDScript 编写的测试。
project/
├── addons/gdUnit4/ # GdUnit4 插件
├── test/ # 测试目录
│ ├── game_test.gd
│ └── player_test.gd
└── scripts/
└── game.gd
# 安装 GdUnit4
git clone --depth 1 https://github.com/MikeSchulze/gdUnit4.git addons/gdUnit4
# 在项目设置 → 插件中启用插件
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
# 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")
# 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()
# 所有测试
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --run-tests
# 特定测试文件
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \
--run-tests --add res://test/my_test.gd
# 为 CI 生成报告
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \
--run-tests --report-directory ./reports
# 值
assert_that(value).is_equal(expected)
assert_that(value).is_not_null()
assert_that(condition).is_true()
# 数字
assert_that(number).is_greater(5)
assert_that(number).is_between(1, 100)
# 字符串
assert_that(text).contains("expected")
assert_that(text).starts_with("prefix")
# 数组
assert_that(array).contains(element)
assert_that(array).has_size(5)
# 信号
await assert_signal(node).is_emitted("signal_name")
# 鼠标
runner.set_mouse_position(Vector2(100, 100))
runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
runner.simulate_mouse_button_released(MOUSE_BUTTON_LEFT)
# 键盘
runner.simulate_key_pressed(KEY_SPACE)
runner.simulate_key_pressed(KEY_S, false, true) # Ctrl+S
# 输入动作
runner.simulate_action_pressed("jump")
runner.simulate_action_released("jump")
# 等待
await runner.await_input_processed()
await runner.await_idle_frame()
await runner.await_signal("game_over", [], 5000)
PlayGodot 是一个用于 Godot 的游戏自动化框架——类似于 Playwright,但是用于游戏。它通过原生 RemoteDebugger 协议支持 E2E 测试、自动化游戏玩法以及外部控制 Godot 游戏。
要求:
# 安装 PlayGodot
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install playgodot
# 选项 1:下载预构建的二进制文件 (推荐)
# 查看发布版本:https://github.com/Randroids-Dojo/godot/releases/tag/automation-latest
# - godot-automation-linux-x86_64.zip
# - godot-automation-macos-universal.zip (Intel + Apple Silicon)
# 选项 2:从源码构建自定义 Godot 分支
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
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
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 赢得顶行
await game.call(GAME, "make_move", [pos])
is_active = await game.call(GAME, "is_game_active")
assert is_active is False
export GODOT_PATH=/path/to/godot-automation-fork
pytest tests/ -v
pytest tests/test_game.py::test_clicking_cell -v
# 节点交互
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)
# 节点查询
paths = await game.query_nodes("*Button*")
count = await game.count_nodes("*Label*")
# 鼠标输入
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")
# 键盘输入
await game.press_key("space")
await game.press_key("ctrl+s")
await game.type_text("hello")
# 输入动作
await game.press_action("jump")
await game.hold_action("sprint", 2.0)
# 触摸输入
await game.tap(300, 200)
await game.swipe(100, 100, 400, 100)
await game.pinch((200, 200), 0.5)
# 截图
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 = await game.get_current_scene()
await game.change_scene("res://scenes/level2.tscn")
await game.reload_scene()
# 游戏状态
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()
# 等待
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")
# 需要 export_presets.cfg 中包含 Web 预设
godot --headless --export-release "Web" ./build/index.html
[preset.0]
name="Web"
platform="Web"
runnable=true
export_path="build/index.html"
npm i -g vercel
vercel deploy ./build --prod
- 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/gdunit4-quickstart.md - GdUnit4 设置references/scene-runner.md - 输入模拟 APIreferences/assertions.md - 断言方法references/playgodot.md - PlayGodot 指南references/deployment.md - 部署指南references/ci-integration.md - CI/CD 设置每周安装量
66
仓库
GitHub 星标数
19
首次出现
2026年2月3日
安全审计
安装于
gemini-cli62
opencode62
github-copilot61
codex59
amp56
kimi-cli56
Develop, test, build, and deploy Godot 4.x games.
# 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
| 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 runs tests written in GDScript directly inside Godot.
project/
├── addons/gdUnit4/ # GdUnit4 addon
├── test/ # Test directory
│ ├── game_test.gd
│ └── player_test.gd
└── scripts/
└── game.gd
# Install GdUnit4
git clone --depth 1 https://github.com/MikeSchulze/gdUnit4.git addons/gdUnit4
# Enable plugin in Project Settings → Plugins
# 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")
# 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()
# 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
# 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")
# 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 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:
# 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
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
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
export GODOT_PATH=/path/to/godot-automation-fork
pytest tests/ -v
pytest tests/test_game.py::test_clicking_cell -v
# 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")
# Requires export_presets.cfg with Web preset
godot --headless --export-release "Web" ./build/index.html
[preset.0]
name="Web"
platform="Web"
runnable=true
export_path="build/index.html"
npm i -g vercel
vercel deploy ./build --prod
- 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/gdunit4-quickstart.md - GdUnit4 setupreferences/scene-runner.md - Input simulation APIreferences/assertions.md - Assertion methodsreferences/playgodot.md - PlayGodot guidereferences/deployment.md - Deployment guidereferences/ci-integration.md - CI/CD setupWeekly Installs
66
Repository
GitHub Stars
19
First Seen
Feb 3, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykWarn
Installed on
gemini-cli62
opencode62
github-copilot61
codex59
amp56
kimi-cli56
Skills CLI 使用指南:AI Agent 技能包管理器安装与管理教程
48,700 周安装
GitNexus 指南:代码智能与影响分析工具,快速理解、调试与重构代码库
131 周安装
Chargebee 集成指南:SDK、API 与 Webhook 实现订阅计费
122 周安装
Tamagui Monorepo 跨平台开发指南:React Native、Next.js、Expo、TypeScript 全栈解决方案
123 周安装
Node.js开发专家指南:TypeScript、Payload CMS、Next.js与Vue.js全栈实战
122 周安装
Novel Writer 新手指南:七步方法论教你从零开始写小说
125 周安装
Motion动画库指南:高性能JavaScript/TypeScript网页动效开发与性能优化
123 周安装