关于 defold-native-extension-editing
这是一份 Defold 原生扩展开发参考技能,涵盖 C++、Objective-C 和 JavaScript。内容包括扩展目录与清单、dmSDK 用法、Lua 回调及引擎参考资料。
主要功能
- 01扩展目录与清单结构
- 02C++ 和 JavaScript 原生入口
- 03直接使用 dmSDK 的模式
- 04异步 Lua 回调生命周期
- 05引擎源码与 API 参考
适用场景
- 01搭建 Defold 原生扩展
- 02在 C++ 中访问游戏对象
- 03异步事件后调用 Lua 回调
- 04核对平台库目录布局
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ indiesoftby/ defold-agent-config --skill defold-native-extension-editing安装后在 Claude Code 中输入/defold-native-extension-editing确认已加载。
详细介绍
Defold 原生扩展结构
原生扩展是一个包含 ext.manifest 和原生代码的文件夹,用于扩展 Defold 引擎。
必需的结构
my_extension/
├── ext.manifest # Extension manifest (YAML)
├── src/ # Source code (C/C++/ObjC/JS)
│ ├── extension.cpp # Main extension entry point
│ └── ...
├── include/ # Public headers (optional)
├── lib/ # Platform libraries (optional)
│ ├── x86_64-win32/
│ ├── x86_64-linux/
│ ├── arm64-ios/
│ └── ...
├── api/ # Script API definitions (optional)
│ └── my_extension.script_api
└── res/ # Platform resources (optional)
└── android/
ext.manifest 格式
name: "MyExtension"
platforms:
x86_64-win32:
context:
defines: ["MY_DEFINE"]
libs: ["user32"]
arm64-ios:
context:
frameworks: ["UIKit"]
扩展入口点(C++)
#define EXTENSION_NAME MyExtension
#define LIB_NAME "MyExtension"
#define MODULE_NAME "myextension"
#include <dmsdk/sdk.h>
static int MyFunction(lua_State* L)
{
// Implementation
return 0;
}
static const luaL_reg Module_methods[] =
{
{"my_function", MyFunction},
{0, 0}
};
static void LuaInit(lua_State* L)
{
int top = lua_gettop(L);
luaL_register(L, MODULE_NAME, Module_methods);
lua_pop(L, 1);
assert(top == lua_gettop(L));
}
static dmExtension::Result AppInitializeMyExtension(dmExtension::AppParams* params)
{
return dmExtension::RESULT_OK;
}
static dmExtension::Result InitializeMyExtension(dmExtension::Params* params)
{
LuaInit(params->m_L);
return dmExtension::RESULT_OK;
}
static dmExtension::Result FinalizeMyExtension(dmExtension::Params* params)
{
return dmExtension::RESULT_OK;
}
DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, AppInitializeMyExtension, 0, InitializeMyExtension, 0, 0, FinalizeMyExtension)
HTML5/JavaScript 扩展
对于 Web 平台,请在 src/ 中使用 .js 文件:
var MyExtension = {
MyFunction: function() {
// Implementation
}
};
mergeInto(LibraryManager.library, MyExtension);
平台库路径(lib/)
x86_64-win32/- Windows 64 位x86-win32/- Windows 32 位x86_64-linux/- Linux 64 位arm64-osx/- macOS ARMx86_64-osx/- macOS Intelarm64-ios/- iOS ARM64armv7-android/、arm64-android/- Androidjs-web/、wasm-web/- HTML5
优先使用 dmSDK,而非 Lua API 调用
始终使用直接的 C++ dmSDK 函数,而不是调用 Lua API 封装(例如 lua_getglobal(L, "go") + lua_call)。对于大多数 Lua 游戏对象操作,dmSDK 都提供了高效的 C++ 等效函数。
dmSDK 关键用法模式
从 Lua 获取游戏对象实例:
// Get the calling script's game object instance
dmGameObject::HInstance caller = dmScript::CheckGOInstance(L);
// Get the collection from any instance
dmGameObject::HCollection collection = dmGameObject::GetCollection(caller);
// Resolve a hash identifier (from Lua stack) to an instance
dmhash_t id = dmScript::CheckHash(L, index);
dmGameObject::HInstance target = dmGameObject::GetInstanceFromIdentifier(collection, id);
直接在 C++ 中操作游戏对象:
// Position (uses dmVMath::Point3)
dmVMath::Point3 pos = dmGameObject::GetPosition(instance);
dmGameObject::SetPosition(instance, dmVMath::Point3(x, y, z));
// Rotation (uses dmVMath::Quat)
dmVMath::Quat rot = dmGameObject::GetRotation(instance);
dmGameObject::SetRotation(instance, rot);
// Scale
float scale = dmGameObject::GetUniformScale(instance);
dmGameObject::SetScale(instance, scale);
dmGameObject::SetScale(instance, dmVMath::Vector3(sx, sy, sz));
数学类型——创建一次,在循环中重复使用:
dmVMath::Point3 pos(0.0f, 0.0f, 0.0f);
for (int i = 0; i < count; ++i)
{
pos.setX(computed_x);
pos.setY(computed_y);
dmGameObject::SetPosition(instance, pos);
}
通过 dmScript 提取 Lua 类型:
dmVMath::Vector3* v = dmScript::CheckVector3(L, index);
dmVMath::Vector4* v4 = dmScript::CheckVector4(L, index);
dmVMath::Quat* q = dmScript::CheckQuat(L, index);
dmVMath::Matrix4* m = dmScript::CheckMatrix4(L, index);
dmhash_t hash = dmScript::CheckHash(L, index);
dmhash_t hash = dmScript::CheckHashOrString(L, index);
通过 dmScript 异步调用 Lua 回调
当原生扩展需要异步调用用户提供的 Lua 回调(例如,在平台事件、计时器或异步操作之后)时,请使用 dmScript::LuaCallbackInfo。参考:https://github.com/indiesoftby/defold-page-visibility/tree/main/page_visibility/src
从 Lua 存储回调:
static dmScript::LuaCallbackInfo* g_Callback = 0;
static int SetCallback(lua_State* L)
{
DM_LUA_STACK_CHECK(L, 0);
// Destroy previous callback to avoid leaks
if (g_Callback)
{
dmScript::DestroyCallback(g_Callback);
g_Callback = 0;
}
if (lua_isfunction(L, 1))
{
g_Callback = dmScript::CreateCallback(L, 1);
}
return 0;
}
稍后调用回调(来自任何原生事件):
static void OnAsyncEvent(int result_code)
{
if (!g_Callback || !dmScript::IsCallbackValid(g_Callback))
return;
lua_State* L = dmScript::GetCallbackLuaContext(g_Callback);
DM_LUA_STACK_CHECK(L, 0);
if (!dmScript::SetupCallback(g_Callback))
{
dmScript::DestroyCallback(g_Callback);
g_Callback = 0;
return;
}
// Push arguments after self (self is already on stack from SetupCallback)
lua_pushnumber(L, result_code);
dmScript::PCall(L, 2, 0); // 2 = self + 1 user argument
dmScript::TeardownCallback(g_Callback);
}
在 Finalize 中清理(防止扩展卸载时发生泄漏):
static dmExtension::Result FinalizeMyExtension(dmExtension::Params* params)
{
if (g_Callback)
{
dmScript::DestroyCallback(g_Callback);
g_Callback = 0;
}
return dmExtension::RESULT_OK;
}
生命周期: CreateCallback → SetupCallback → PCall → TeardownCallback → DestroyCallback(完成后)。
对于一次性回调,请在 TeardownCallback 之后立即调用 DestroyCallback。对于持久监听器,请保留回调,仅在替换回调或 finalize 时销毁。
何时可以接受 Lua API 调用
仅在以下情况下,才退回使用 lua_getglobal/lua_call 模式:
- 没有 dmSDK C++ 等效函数(例如
go.animate、向任意 URL 执行msg.post) - 需要在不使用
dmScript::LuaCallbackInfo模式的情况下调用用户定义的 Lua 回调(少见)
将 Defold 引擎源代码作为参考
Defold 引擎源代码位于 https://github.com/defold/defold/tree/dev/engine,其中包含大量 dmSDK 用法示例。实现原生扩展时,请主动研究引擎源代码,以查找正确的 API 用法模式,尤其是:
engine/gameobject/— 游戏对象操作(dmGameObject::*)engine/gamesys/— 组件系统(精灵、碰撞、工厂等)engine/script/— 脚本桥接实用工具(dmScript::*)engine/dlib/— 数学、哈希、日志记录、缓冲区(dmVMath::*、dmLog*、dmBuffer::*)engine/render/— 渲染管线(dmRender::*)
此外,Spine 扩展 https://github.com/defold/extension-spine/tree/main/defold-spine 是 dmSDK 用法的绝佳实际参考——它展示了组件注册、游戏对象操作、渲染、资源管理和脚本绑定。
使用 Librarian 工具或 defold-api-fetch skill 获取特定 API 文档。如果 API 文档不够详尽,请在 GitHub 上浏览引擎源代码和 extension-spine,了解实际用法。
代码格式
使用原生扩展时,请确保项目根目录中存在 .clang-format,以保持 C/C++ 格式一致。如果缺少,请从 Defold 仓库获取:
https://raw.githubusercontent.com/defold/defold/refs/heads/dev/.clang-format
重要:此 .clang-format 来自 Defold 官方仓库,可确保代码风格与引擎保持一致。
API 参考
对于 C++ SDK 文档,请使用 defold-api-fetch skill 中的 C++ Native Extension APIs 部分(dmExtension、dmScript、dmBuffer 等)。