mcpField
D
defold-core

defold-core

github.com/gamedev-skills开发5.4k1.2k 次安装1 天前更新
代码仓库

关于 defold-core

defold-core 是用于开发和检查 Defold 游戏项目的技能,涵盖项目资源、Lua 生命周期与消息、对象生成和世界加载,并提供引用检查及基于 Bob 的构建和运行验证。

主要功能

  1. 01Defold 项目资源与文件指南
  2. 02Lua 生命周期、输入和消息模式
  3. 03运行时对象生成与世界加载
  4. 04项目引用检查脚本
  5. 05Bob 构建与无头运行检查

适用场景

  1. 01添加或修复游戏对象与脚本
  2. 02排查缺失资源和构建错误
  3. 03实现输入、消息或对象生成
  4. 04通过 collection proxy 加载关卡

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill defold-core

安装后在 Claude Code 中输入/defold-core确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
build-and-run.md
file-formats.md
scripts/
check_defold_project.py

详细介绍

Defold Core

构建 Defold 游戏的结构、连接与验证:编辑器资源文件、Lua 脚本生命周期、寻址与消息、运行时生成与世界加载,以及 Bob 构建和运行检查。 示例适用于 Defold 1.14.0;它们使用 Bob 1.14.0 构建,并在 1.14.0 无头引擎上运行。现有项目保留其 Defold 版本(参见版本)。

何时使用

  • 在 Defold 项目中添加或修复游戏对象、集合、脚本、输入、消息、生成或关卡加载时使用(根目录中有 game.project,以及 .collection、.go、.script、.gui_script 文件),或 Bob 报告资源缺失时使用。

何时不使用: 纯 Lua 或 LÖVE 项目(love2d-core);参考资料中提示以外的 GUI 布局、精灵、瓦片地图和物理深度内容;以及以下相关技能处理的与引擎无关的设计。

工作流

  1. 先检查。 阅读 game.project([bootstrap] main_collection、[input] game_binding、[project] dependencies)、主集合以及请求涉及的脚本。遵循项目的文件夹布局、id 命名和消息名称;不要重新组织。记录 CI 或团队使用的 Defold/Bob 版本。
  2. 编辑正确的文件类型。 游戏对象(.go)包含组件;集合(.collection)放置游戏对象和其他集合;逻辑写在 .script(游戏对象)、.gui_script(GUI)或共享的 .lua 模块中。编辑器文件是 protobuf 文本:保留其结构,并从 references/file-formats.md 复制形状,不要猜测字段。
  3. 检查引用。 运行 python scripts/check_defold_project.py <project-dir>(相对于此技能文件夹的路径;需要 Python 3.10+,无需安装 Defold)。修复所有 ERROR(缺失或大小写错误的 /paths、缺少已编译 c 后缀的 game.project 路径、文件不存在的 require)。警告涉及尚未使用的文件、未获取的库依赖项以及运行时加载的文件。反复运行,直到退出码为 0。
  4. 使用 Bob 构建。 java -jar bob.jar --root . --variant headless --archive resolve build 需要 JDK 25,以及与项目 Defold 版本匹配的 bob.jar。退出码为 1 且出现 ERROR: 行,表示引用损坏或 Lua 语法错误(含文件和行号);references/file-formats.md 说明了如何追踪错误中提到的生成文件。
  5. 运行。 构建成功并不能证明游戏可正常运行。在超时限制下运行无头包(references/build-and-run.md)并查看日志:ERROR:SCRIPT: 行表示 Lua 运行时错误,ERROR:GAMEOBJECT: 行表示地址错误或 socket 冲突。确认出现测试结束时输出的日志行;退出码本身无法证明任何事情。无头引擎没有键盘或鼠标,因此请在编辑器或桌面构建中检查输入变更,并说明哪些内容无法验证。

模式

项目和资源引用

game.project 是项目根目录中的 INI 文件。所有资源路径都是以根目录为起点的绝对路径(/main/player.go)。在 game.project 中,路径指向已编译文件,因此源路径末尾需要添加 c:

[bootstrap]
main_collection = /main/main.collectionc

[input]
game_binding = /input/game.input_bindingc

Bob 只会构建从 game.project 可达的内容;/builtins/... 随引擎提供。

集合、游戏对象、组件

集合放置实例,并可按实例覆盖脚本属性;游戏对象列出文件组件和嵌入式组件(转义后的 data: 字符串)。完整且已验证的文件见 references/file-formats.md。

# main.collection (excerpt): place player.go and override its script's speed
instances {
  id: "player"
  prototype: "/main/player.go"
  component_properties {
    id: "player"
    properties { id: "speed" value: "250.0" type: PROPERTY_TYPE_NUMBER }
  }
}
脚本生命周期、self 和属性
go.property("speed", 200)                   -- top level only, literal default; editable per instance

local LEFT = hash("left")                   -- hash ids once, not every frame
local RIGHT = hash("right")
local FIRE = hash("fire")

function init(self)
	msg.post(".", "acquire_input_focus")    -- no focus, no on_input calls
	self.held = {}                          -- per-instance state lives on self
end

function update(self, dt)
	local dir = (self.held[RIGHT] and 1 or 0) - (self.held[LEFT] and 1 or 0)
	if dir ~= 0 then
		local pos = go.get_position()
		pos.x = pos.x + dir * self.speed * dt    -- dt-scaled: same speed at any frame rate
		go.set_position(pos)
	end
end

function on_input(self, action_id, action)
	if action_id == LEFT or action_id == RIGHT then
		if action.pressed then self.held[action_id] = true
		elseif action.released then self.held[action_id] = nil end
		return true                         -- consumed: listeners lower on the stack skip it
	elseif action_id == FIRE and action.pressed then
		factory.create("#bulletfactory", go.get_position(), nil,
			{ velocity = vmath.vector3(0, 600, 0) })
		return true
	end
end

function final(self)
	msg.post(".", "release_input_focus")
end

回调:init、final、fixed_update、update、late_update(每次 update 后、渲染前)、on_message、on_input、on_reload。游戏对象之间的更新顺序未指定。每帧会按 [engine] fixed_update_frequency 运行 0..N 次 fixed_update;还要设置 [physics] use_fixed_timestep = 1,使物理步进频率相同,并将由物理驱动的代码放在这里。self 是 userdata:可以在其上存储字段,但 pairs(self) 和 pprint(self) 不起作用。属性类型:number、boolean、hash、msg.url、vmath.vector3/vector4、vmath.quat、资源(例如 resource.atlas("/main/main.atlas")),以及(从 1.13.2 开始)字符串。

输入绑定和输入栈

.input_binding 将设备映射到动作名称(key_trigger、mouse_trigger,以及 gamepad、touch 和 text triggers)。每个获取了焦点的组件都会进入输入栈;最后获取焦点的组件位于栈顶,并最先接收输入;从 on_input 返回 true 会在该组件处停止输入传递,这就是暂停菜单拦截游戏输入的方式。按键动作会报告 pressed 和 released;如上所述,自行跟踪按住的按键。

地址和消息

URL 格式为 [socket:][path][#fragment]:"." 表示当前游戏对象,"#" 表示当前组件,"#sprite" 表示同级组件,"enemy#ai" 表示相对对象,"/manager#controller" 表示绝对对象,"main:/loader#loader" 表示另一个世界中的对象。集合会创建命名范围,因此字符串 id 会相对于发送方所在的集合解析;预先哈希的 id 始终是绝对地址。

msg.post("/manager#controller", "enemy_spotted", { id = go.get_id() })

function on_message(self, message_id, message, sender)
	if message_id == hash("enemy_spotted") then
		print("from", sender, "id", message.id)
	end
end

消息会排队,并在帧稍后分发;表的大小上限为 2 KB。地址无效时会记录 ERROR:GAMEOBJECT: Instance '/x' could not be found when dispatching message。

工厂和集合工厂
-- factory component "#bulletfactory" -> one game object; properties override go.property values
local id = factory.create("#bulletfactory", pos, nil, { velocity = vmath.vector3(0, 600, 0) })

-- collectionfactory component "#wavefactory" -> table of collection ids -> new instance ids
local ids = collectionfactory.create("#wavefactory", vmath.vector3(480, 500, 0))
local leader = ids[hash("/leader")]         -- e.g. hash: [/collection0/leader]
go.delete(ids, true)                        -- later: delete them and their children

位置和旋转默认使用生成对象的位置和旋转。go.delete() 会在帧结束时生效。集合工厂的每个实例属性按 id 设置键: { [hash("/leader")] = { hp = 5 } }。

集合代理(加载另一个世界)
-- excerpt from the loader script; the collection's "#levelproxy" component is a collectionproxy
function init(self)
	msg.post(".", "acquire_input_focus")        -- else the loaded world gets no input
	msg.post("#levelproxy", "async_load")       -- or "load"
end

function on_message(self, message_id, message, sender)
	if message_id == hash("proxy_loaded") then
		msg.post(sender, "init")
		msg.post(sender, "enable")
	elseif message_id == hash("level_done") then
		msg.post("#levelproxy", "unload")       -- disables and finalizes first
	elseif message_id == hash("proxy_unloaded") then
		-- safe to load the next level here
	end
end

已加载世界的 socket 是已加载集合文件的 name:,因此其中的脚本会用 msg.post("main:/loader#loader", ...) 回复,其自身的 URL 则写作 level1:/level#level。set_time_step { factor = 0, mode = 0 } 会暂停代理世界。

版本

game.project 不包含引擎版本键。请使用项目构建时所用的 Defold 编辑器或 bob.jar 版本(CI 通常会固定一个发行版 sha1);如果项目是库,则使用 library.defold_min_version。java -jar bob.jar --version 会打印版本和 sha1。保持使用该版本;除非有人要求,否则不要迁移。

常见问题

  • Bob:the bootstrap.main_collection '/main/main.collectio' can't be found → 设置缺少编译后的后缀,Bob 去掉了最后一个字母。请写成 /main/main.collectionc(input.game_binding 同理,应为 .input_bindingc)。
  • Bob:ERROR: build/default/_generated_….factory:0: the prototype '/main/x.go' can't be found → 某个 .go 中的嵌入式组件指向了不存在的文件。请搜索源文件中的引号路径(/main/x.go),而不是生成的名称,或运行检查器。
  • on_input 从不触发 → 对象从未发送 acquire_input_focus,动作名称与绑定不一致,input.game_binding 指向了其他位置,或者调用栈中更高层的监听器返回了 true。
  • 通过代理加载的关卡中输入失效 → 持有集合代理的游戏对象未获取输入焦点。
  • 出现 already a socket with the same name,且代理从不发送 proxy_loaded → 已加载集合的 name: 与已加载的世界同名(通常两者都是 "main" 或 "default")。请为每个代理集合指定唯一名称。
  • fixed_update 从不运行 → [engine] fixed_update_frequency 为 0(默认值为 60)。对于物理模拟,还要设置 [physics] use_fixed_timestep = 1,因为手册要求同时设置这两项。
  • Bob 构建成功,但运行时出现 module 'modules.util' not found → require 使用了斜杠(require "modules/util")。对于 /modules/util.lua,请使用从项目根目录开始的点分路径 require "modules.util"。
  • tostring(id) 输出 hash: [<unknown:…>] → 哈希仅在调试构建中保留其源文本。请比较哈希,不要比较它们的字符串。
  • 第二个引擎记录 Failed to initialize Remotery,或测试运行始终不结束 → 调试引擎会打开引擎服务(端口 8001)、日志服务器和分析器端口,并在未调用 sys.exit 时无限等待。一次只运行一个引擎,并始终使用超时。

参考资料

  • references/file-formats.md — 已验证的最小 game.project、.collection、.go、 .atlas、.input_binding 和脚本文件,以及如何将构建错误追溯到源文件;手动编写编辑器文件前请先阅读。
  • references/build-and-run.md — JDK 25 和 bob.jar、Bob 命令、平台和变体、带超时和日志检查的无头冒烟测试,以及 GUI、 精灵、瓦片地图和碰撞对象的参考资料。
  • scripts/check_defold_project.py — 只读参考检查器;--help 会列出所有检查项。

相关技能

  • input-systems — 在 Defold 绑定的基础上实现重新绑定、手柄支持和输入缓冲。
  • game-ui-ux — 使用 GUI 场景和 .gui_script 构建 HUD 和菜单。
  • physics-tuning — 为碰撞对象调整固定时间步长和碰撞参数。
  • camera-systems — 跟随和死区逻辑,通常在 late_update 中应用。
  • save-systems — 存档格式和槽位;与 Defold 的 sys.save/sys.load 配合使用。
  • game-feel — 使用 go.animate 和计时器增强游戏手感。
  • platformer、level-design — 提供与此技能配套的类型和布局指导。
  • love2d-core — 本目录中的另一款 Lua 引擎,适用于 LÖVE 项目。

评论

支持 Markdown