defold-core
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDefold Core
Defold 核心
Structure, wire and verify a Defold game: editor resource files, the Lua script lifecycle,
addressing and messages, runtime spawning and world loading, and a Bob build-and-run check.
Examples target Defold 1.14.0; they were built with Bob 1.14.0 and run on the 1.14.0
headless engine. An existing project keeps its Defold version (see Version).
组织、连接并验证 Defold 游戏:编辑器资源文件、Lua 脚本生命周期、
寻址与消息传递、运行时生成与世界加载,以及 Bob 构建并运行检查。
示例面向 Defold 1.14.0;它们使用 Bob 1.14.0 构建,并在 1.14.0
headless engine 上运行。现有项目保留其 Defold 版本(参见 版本)。
When to use
何时使用
- Use when adding or fixing game objects, collections, scripts, input, messages, spawning or
level loading in a Defold project (in the root,
game.project,.collection,.go,.scriptfiles), or when Bob reports a missing resource..gui_script
When not to use: plain Lua or LÖVE projects (); GUI layout, sprites,
tilemaps and physics depth beyond the pointers in the references; engine-agnostic design
handled by the related skills below.
love2d-core- 在 Defold 项目(根目录中的 、
game.project、.collection、.go、.script文件)中添加或修复 game objects、collections、scripts、 input、messages、spawning 或 level loading 时使用,或者当 Bob 报告缺失资源时使用。.gui_script
**何时不使用:**普通 Lua 或 LÖVE 项目();GUI 布局、sprites、
tilemaps 和超出参考资料指引范围的 physics depth;以及由下述相关 skills 处理的、
与引擎无关的设计。
love2d-coreWorkflow
工作流
- Inspect first. Read (
game.project,[bootstrap] main_collection,[input] game_binding), the main collection, and the scripts near the request. Follow the project's folder layout, id naming and message names; do not reorganize. Note which Defold/Bob version CI or the team uses.[project] dependencies - Edit the right file type. Game objects () hold components; collections (
.go) place game objects and other collections; logic goes in.collection(game objects),.script(GUI) or shared.gui_scriptmodules. Editor files are protobuf text: keep their structure, and copy shapes from.luainstead of guessing fields.references/file-formats.md - Check references. Run (path relative to this skill folder; Python 3.10+, no Defold install needed). Fix every
python scripts/check_defold_project.py <project-dir>(missing or wrong-caseERROR, a/pathspath without the compiledgame.projectsuffix, acwhose file does not exist). Warnings cover files nothing uses yet, unfetched library dependencies and runtime-loaded files. Re-run until it exits 0.require - Build with Bob. needs JDK 25 and a
java -jar bob.jar --root . --variant headless --archive resolve buildmatching the project's Defold version. Exit 1 plus anbob.jarline means a broken reference or a Lua syntax error (with file and line);ERROR:shows how to trace an error that names a generated file.references/file-formats.md - Run it. A build does not prove the game works. Run a headless bundle under a timeout
() and read the log:
references/build-and-run.mdlines are Lua runtime errors,ERROR:SCRIPT:lines bad addresses or socket clashes. Require a line your test prints when it finishes; the exit code alone proves nothing. The headless engine has no keyboard or mouse, so check input changes in the editor or a desktop build, and say what you could not verify.ERROR:GAMEOBJECT:
- 先检查。 读取 (
game.project、[bootstrap] main_collection、[input] game_binding)、主 collection,以及请求附近的 scripts。 遵循项目的文件夹布局、id 命名和消息名称;不要重新组织。记录 CI 或团队使用的 Defold/Bob 版本。[project] dependencies - 编辑正确的文件类型。 Game objects()包含 components;collections (
.go)放置 game objects 和其他 collections;逻辑放在.collection(game objects)、.script(GUI)或共享.gui_scriptmodules 中。Editor files 是 protobuf text:保持其结构,并从.lua复制 shapes, 而不是猜测字段。references/file-formats.md - 检查引用。 运行 (路径 相对于此 skill 文件夹;需要 Python 3.10+,无需安装 Defold)。修复每个
python scripts/check_defold_project.py <project-dir>(缺失或大小写错误的ERROR、/paths中缺少 compiledgame.projectsuffix 的 path, 或文件不存在的c)。Warnings 涵盖当前尚未使用的文件、未拉取的 library dependencies 和 runtime-loaded files。重新运行,直到其以 0 退出。require - 使用 Bob 构建。 需要 JDK 25,以及与项目 Defold 版本匹配的
java -jar bob.jar --root . --variant headless --archive resolve build。退出码为 1 且出现bob.jar行,表示引用损坏或 Lua syntax error(会给出文件和行号);ERROR:展示了如何追踪指向 generated file 的错误。references/file-formats.md - 运行。 通过构建并不能证明游戏可以工作。在 timeout 下运行 headless bundle
()并读取日志:
references/build-and-run.md行表示 Lua runtime errors,ERROR:SCRIPT:行表示错误地址或 socket 冲突。测试完成时应打印一行 指定内容;仅凭退出码不能证明任何事情。Headless engine 没有键盘或鼠标,因此应在 editor 或 desktop build 中检查 input 变化,并说明哪些内容无法验证。ERROR:GAMEOBJECT:
Patterns
模式
Project and resource references
项目与资源引用
game.project/main/player.gogame.projectcini
[bootstrap]
main_collection = /main/main.collectionc
[input]
game_binding = /input/game.input_bindingcBob builds only what is reachable from ; ships with the engine.
game.project/builtins/...game.project/main/player.gogame.projectcini
[bootstrap]
main_collection = /main/main.collectionc
[input]
game_binding = /input/game.input_bindingcBob 只构建从 可达的内容; 随 engine 提供。
game.project/builtins/...Collections, game objects, components
Collections、game objects 与 components
A collection places instances and can override script properties per instance; a game object
lists file components and embedded components (escaped strings). Full verified
files are in .
data:references/file-formats.mdtext
undefinedCollection 放置 instances,并可按 instance 覆盖 script properties;game object
列出 file components 和 embedded components(转义后的 strings)。完整且经过验证的
文件见 。
data:references/file-formats.mdtext
undefinedmain.collection (excerpt): place player.go and override its script's speed
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 }
}
}
undefinedinstances {
id: "player"
prototype: "/main/player.go"
component_properties {
id: "player"
properties { id: "speed" value: "250.0" type: PROPERTY_TYPE_NUMBER }
}
}
undefinedScript lifecycle, self
and properties
selfScript 生命周期、self
与 properties
selflua
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")
endCallbacks: , , , , (after every ,
before render), , , . Update order between game
objects is unspecified. runs 0..N times per frame at ; set too so physics steps at the
same rate, and put physics-driven code there. is userdata: store
fields on it, but and do not work. Property types: number,
boolean, hash, , /, , resources such as
, and (since 1.13.2) strings.
initfinalfixed_updateupdatelate_updateupdateon_messageon_inputon_reloadfixed_update[engine] fixed_update_frequency[physics] use_fixed_timestep = 1selfpairs(self)pprint(self)msg.urlvmath.vector3vector4vmath.quatresource.atlas("/main/main.atlas")lua
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回调包括:、、、、(每次
之后、render 之前)、、、。Game objects 之间的 update
顺序未指定。 每帧运行 0..N 次,频率由 决定;还需设置 ,使 physics steps
以相同频率运行,并将 physics 驱动的代码放在其中。 是 userdata:可以在其上存储
fields,但 和 不起作用。Property 类型包括:number、
boolean、hash、、/、、诸如
的 resources,以及(自 1.13.2 起)strings。
initfinalfixed_updateupdatelate_updateupdateon_messageon_inputon_reloadfixed_update[engine] fixed_update_frequency[physics] use_fixed_timestep = 1selfpairs(self)pprint(self)msg.urlvmath.vector3vector4vmath.quatresource.atlas("/main/main.atlas")Input bindings and the input stack
Input bindings 与 input stack
.input_bindingkey_triggermouse_triggertrueon_inputpressedreleased.input_bindingkey_triggermouse_triggeron_inputtruepressedreleasedAddressing and messages
寻址与 messages
A URL is : is this game object, this component,
a sibling component, a relative object, an
absolute one, an object in another world. Collections create naming
scopes, so a string id is resolved relative to the sender's collection; a pre-hashed id is
always absolute.
[socket:][path][#fragment]".""#""#sprite""enemy#ai""/manager#controller""main:/loader#loader"lua
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
endMessages are queued and dispatched later in the frame; the table is capped at 2 KB. A bad
address logs .
ERROR:GAMEOBJECT: Instance '/x' could not be found when dispatching messageURL 的格式为 : 表示当前 game object, 表示当前
component, 表示同级 component, 表示 relative object,
表示 absolute object, 表示另一个
world 中的 object。Collections 会创建 naming scopes,因此 string id 会相对于 sender 的
collection 解析;pre-hashed id 始终是 absolute。
[socket:][path][#fragment]".""#""#sprite""enemy#ai""/manager#controller""main:/loader#loader"lua
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
endMessages 会被排队并在 frame 稍后分发;table 上限为 2 KB。Bad address 会记录
。
ERROR:GAMEOBJECT: Instance '/x' could not be found when dispatching messageFactories and collection factories
Factories 与 collection factories
lua
-- 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 childrenPosition and rotation default to the spawning object's. takes effect at the
end of the frame. Per-instance properties for a collection factory are keyed by id:
.
go.delete(){ [hash("/leader")] = { hp = 5 } }lua
-- 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 childrenPosition 和 rotation 默认取自 spawning object。 在 frame 结束时生效。
Collection factory 的 per-instance properties 以 id 为 key:
。
go.delete(){ [hash("/leader")] = { hp = 5 } }Collection proxies (load another world)
Collection proxies(加载其他 world)
lua
-- 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
endThe loaded world's socket is the of the loaded collection file, so scripts in it
reply with and its own URLs read
. pauses a proxy world.
name:msg.post("main:/loader#loader", ...)level1:/level#levelset_time_step { factor = 0, mode = 0 }lua
-- 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已加载 world 的 socket 是 loaded collection 文件的 ,因此其中的 scripts 会以
回复,而其自身的 URLs 读作
。 会暂停 proxy world。
name:msg.post("main:/loader#loader", ...)level1:/level#levelset_time_step { factor = 0, mode = 0 }Version
版本
game.projectbob.jarlibrary.defold_min_versionjava -jar bob.jar --versiongame.projectbob.jarlibrary.defold_min_versionjava -jar bob.jar --versionPitfalls
常见问题
- Bob: → the setting lacks the compiled suffix and Bob stripped the last letter. Write
the bootstrap.main_collection '/main/main.collectio' can't be found(same for/main/main.collectionc→input.game_binding)..input_bindingc - Bob: → an embedded component in some
ERROR: build/default/_generated_….factory:0: the prototype '/main/x.go' can't be foundpoints at a missing file. Search the sources for the quoted path (.go), not the generated name, or run the checker./main/x.go - never fires → the object never sent
on_input, the action name differs from the binding,acquire_input_focuspoints elsewhere, or a listener higher on the stack returnsinput.game_binding.true - Input dead inside a proxy-loaded level → the game object holding the collection proxy did not acquire input focus.
- and the proxy never sends
already a socket with the same name→ the loaded collection'sproxy_loadedequals an already loaded world (often both arename:or"main"). Give every proxied collection a unique name."default" - never runs →
fixed_updateis 0 (default 60). For physics also set[engine] fixed_update_frequency, as the manual requires both.[physics] use_fixed_timestep = 1 - at run time although Bob succeeded → a
module 'modules.util' not foundused slashes (require). Use the dotted path from the project root,require "modules/util"forrequire "modules.util"./modules/util.lua - prints
tostring(id)→ hashes keep their source text only in debug builds. Compare hashes, never their strings.hash: [<unknown:…>] - A second engine logs , or a test run never ends → the debug engine opens an engine service (port 8001), a log server and a profiler port, and waits forever without
Failed to initialize Remotery. Run one engine at a time and always use a timeout.sys.exit
- Bob:→ 该设置缺少 compiled suffix,导致 Bob 去掉最后一个字母。写入
the bootstrap.main_collection '/main/main.collectio' can't be found(/main/main.collectionc同理 →input.game_binding)。.input_bindingc - Bob:→ 某个
ERROR: build/default/_generated_….factory:0: the prototype '/main/x.go' can't be found中的 embedded component 指向了不存在的文件。在 sources 中搜索 带引号的路径(.go),而不是 generated name,或运行 checker。/main/x.go - 从不触发 → 对象从未发送
on_input、action 名称与 binding 不同、acquire_input_focus指向别处,或 input stack 中更高层的 listener 返回了input.game_binding。true - Proxy-loaded level 中的输入失效 → 持有 collection proxy 的 game object 未获取 input focus。
- 且 proxy 从不发送
already a socket with the same name→ loaded collection 的proxy_loaded与已加载 world 相同(常见情况是两者都为name:或"main")。为每个 proxied collection 设置唯一名称。"default" - 从不运行 →
fixed_update为 0(默认值为 60)。 对于 physics,还需按 manual 要求同时设置[engine] fixed_update_frequency。[physics] use_fixed_timestep = 1 - 运行时出现 ,尽管 Bob 构建成功 → 某个
module 'modules.util' not found使用了斜杠(require)。使用从项目根目录开始的 dotted path:require "modules/util"对应require "modules.util"。/modules/util.lua - 输出
tostring(id)→ Hash 仅在 debug builds 中保留 source text。比较 hash,绝不要比较其字符串。hash: [<unknown:…>] - 第二个 engine 记录 ,或 test run 永不结束 → Debug engine 会打开 engine service(port 8001)、log server 和 profiler port,并在没有
Failed to initialize Remotery时永久等待。每次只运行一个 engine,并始终使用 timeout。sys.exit
References
参考文献
- — verified minimal
references/file-formats.md,game.project,.collection,.go,.atlasand script files, and how to trace build errors back to a source file; read before writing editor files by hand..input_binding - — JDK 25 and
references/build-and-run.md, Bob commands, platforms and variants, a headless smoke test with timeout and log checks, and pointers for GUI, sprites, tilemaps and collision objects.bob.jar - — read-only reference checker;
scripts/check_defold_project.pylists every check.--help
- — 已验证的最小
references/file-formats.md、game.project、.collection、.go、.atlas和 script 文件,以及如何将 build errors 追溯到 source file;手工编写 editor files 前请先阅读。.input_binding - — JDK 25 和
references/build-and-run.md、Bob commands、platforms 和 variants、带 timeout 和 log checks 的 headless smoke test,以及 GUI、sprites、 tilemaps 和 collision objects 的相关指引。bob.jar - — 只读 reference checker;
scripts/check_defold_project.py列出所有 checks。--help
Related skills
相关 skills
- — rebinding, gamepad support and buffering on top of Defold bindings.
input-systems - — HUD and menu design to build with GUI scenes and
game-ui-ux..gui_script - — fixed-timestep and collision tuning for collision objects.
physics-tuning - — follow/deadzone logic, typically applied in
camera-systems.late_update - — save formats and slots; pair with Defold's
save-systems/sys.save.sys.load - — juice built from
game-feeland timers.go.animate - ,
platformer— genre and layout guidance that binds to this skill.level-design - — the other Lua engine in this catalog, for LÖVE projects.
love2d-core
- — 基于 Defold bindings 进行按键重绑定、gamepad 支持和输入缓冲。
input-systems - — 使用 GUI scenes 和
game-ui-ux构建 HUD 与菜单设计。.gui_script - — 使用 collision objects 调优 fixed-timestep 和 collision。
physics-tuning - — follow/deadzone logic,通常在
camera-systems中实现。late_update - — 存档格式和槽位;与 Defold 的
save-systems/sys.save配合使用。sys.load - — 使用
game-feel和 timers 增强游戏手感。go.animate - ,
platformer— 与此 skill 配合使用的类型和关卡布局指导。level-design - — catalog 中的另一个 Lua engine,用于 LÖVE projects。
love2d-core