mcpField
D
defold-native-extension-editing

defold-native-extension-editing

github.com/indiesoftby開発10448 回インストール27 週間前に更新
リポジトリ

defold-native-extension-editing について

Defold のネイティブ拡張を C++、Objective-C、JavaScript で開発するためのスキルです。拡張の構成、dmSDK の利用、Lua コールバック、エンジンの参考資料を扱います。

主な機能

  1. 01拡張フォルダーとマニフェストの構成
  2. 02C++ と JavaScript のエントリーポイント
  3. 03dmSDK の直接利用パターン
  4. 04非同期 Lua コールバックのライフサイクル
  5. 05エンジンソースと API 参照

活用シーン

  1. 01Defold ネイティブ拡張のひな形作成
  2. 02C++ からゲームオブジェクトへアクセス
  3. 03非同期イベント後の Lua コールバック
  4. 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 ARM
  • x86_64-osx/ - macOS Intel
  • arm64-ios/ - iOS ARM64
  • armv7-android/、arm64-android/ - Android
  • js-web/、wasm-web/ - HTML5

Lua API 呼び出しより dmSDK を優先する

Lua API のラッパーを呼び出す代わりに、常に dmSDK の C++ 関数を直接使用してください(例:lua_getglobal(L, "go") + lua_call)。dmSDK は、ほとんどの Lua ゲームオブジェクト操作に対して効率的な 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 エンジンのソースを参考にする

https://github.com/defold/defold/tree/dev/engine にある Defold エンジンのソースには、dmSDK の使用例が多数含まれています。ネイティブ拡張を実装する際は、特に次の箇所を参照して、正しい API の使用パターンを積極的に調べてください。

  • engine/gameobject/ — ゲームオブジェクトの操作(dmGameObject::*)
  • engine/gamesys/ — コンポーネントシステム(スプライト、コリジョン、ファクトリーなど)
  • engine/script/ — スクリプトブリッジのユーティリティ(dmScript::*)
  • engine/dlib/ — 数学、ハッシュ、ロギング、バッファー(dmVMath::*、dmLog*、dmBuffer::*)
  • engine/render/ — レンダリングパイプライン(dmRender::*)

さらに、https://github.com/defold/extension-spine/tree/main/defold-spine にある Spine 拡張は、dmSDK の使い方を示す優れた実例です。コンポーネントの登録、ゲームオブジェクトの操作、レンダリング、リソース管理、スクリプトバインディングについて説明しています。

特定の API ドキュメントを取得するには、Librarian ツールまたは defold-api-fetch スキルを使用してください。API ドキュメントだけでは不十分な場合は、GitHub でエンジンのソースと extension-spine を確認し、実際の使用例を参照してください。

コードのフォーマット

ネイティブ拡張を扱う際は、一貫した C/C++ のフォーマットを適用するため、プロジェクトのルートに .clang-format があることを確認してください。存在しない場合は、Defold リポジトリから取得してください。

https://raw.githubusercontent.com/defold/defold/refs/heads/dev/.clang-format

重要:この .clang-format は Defold 公式リポジトリのもので、エンジンとのコードスタイルの一貫性を保ちます。

API リファレンス

C++ SDK のドキュメントには、C++ Native Extension APIs セクション(dmExtension、dmScript、dmBuffer など)を含む defold-api-fetch スキルを使用してください。

コメント