实现 creator native dragonBones 换肤

1,038 阅读3分钟

native使用的是c++版本的DragonBones,js引擎则使用的是js版本的dragonBones

适配DragonBones native

dragonBones.ObjectDataParser

native并没有这个属性,所以这种修复方式只适合js平台,如果native也遇到相关的bug,需要修改c++

dragonBones.ObjectDataParser

slot.displayList

也只有js层有,native没有导出

const slotArray: dragonBones.Slot[] = armatureDisplay.armature().getSlots();
const frames = spAtlas.getSpriteFrames();
for (let i = 0; i < slotArray.length; i++) {
    const slot = slotArray[i];
    slot.displayList.forEach((display) => { // slot.displayList是数组类型
        if (display instanceof dragonBones.Armature) {
          if (display.name === armatureName) {
            frames.forEach((item) => {
              const s: any = display.getSlot(item.name); // display.getSlot也没有导出
              if (s) {
                s.setSpriteFrame(item);
              } else {
                cc.log(`can't find any slot use sprite frame: ${item.name}`);
              }
            });
          }
        }
    })
}  

image.png

查阅binding工具的python相关导出逻辑,尝试导出getDisplayList()

image.png

match的原因,会导致即使从skip中删除getDisplayList,仍旧无法导出,需要把getDisplay也从skip中删了,正常导出后,发现生成的代码并没有对类型转换做处理,所以仍旧无法正常使用,这也是存在manual的原因。

还需要另找 image.png

有些api是手动导出的

我明明定义的是function

__jsb_dragonBones_Slot_proto->defineFunction("getDisplayList", _SE(js_cocos2dx_dragonbones_Slot_getDisplayList));

为啥slot.displayList效果和它一样呢?

设置texture宽高

static bool js_gfx_Texture2D_init(se::State& s)
{
    cocos2d::renderer::Texture2D* cobj = (cocos2d::renderer::Texture2D*)s.nativeThisObject();
    SE_PRECONDITION2(cobj, false, "js_gfx_Texture2D_init : Invalid Native Object");
    const auto& args = s.args();
    size_t argc = args.size();
    CC_UNUSED bool ok = true;
    if (argc == 2) {
        cocos2d::renderer::DeviceGraphics* arg0 = nullptr;
        cocos2d::renderer::Texture::Options arg1; // js层传递过来的宽高
        ok &= seval_to_native_ptr(args[0], &arg0);
        ok &= seval_to_TextureOptions(args[1], &arg1);
        SE_PRECONDITION2(ok, false, "js_gfx_Texture2D_init : Error processing arguments");
        bool result = cobj->init(arg0, arg1);
        ok &= boolean_to_seval(result, &s.rval());
        SE_PRECONDITION2(ok, false, "js_gfx_Texture2D_init : Error processing arguments");
        return true;
    }
    SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2);
    return false;
}
SE_BIND_FUNC(js_gfx_Texture2D_init)

native龙骨的渲染

还是有点区别,触发渲染的逻辑堆栈

image.png

void ModelBatcher::commitIA(NodeProxy* node, CustomAssembler* assembler, int cullingMask)
{
    auto customIA = assembler->getIA(0);
    _ia.setVertexBuffer(customIA->getVertexBuffer());
    _ia.setIndexBuffer(customIA->getIndexBuffer());
    _ia.setStart(customIA->getStart());
    _ia.setCount(0);
}

image.png

追到了设置customIA的buffer的逻辑,有一个非常相似的逻辑traverseArmature

void CCArmatureDisplay::traverseArmature(Armature* armature, float parentOpacity)
{
    middleware::MeshBuffer* mb = mgr->getMeshBuffer(VF_XYUVC);
    IOBuffer& vb = mb->getVB();
    IOBuffer& ib = mb->getIB();

    // 中间省略了很多的代码
    for (std::size_t i = 0, len = slots.size(); i < len; i++)
    {
        slot = (CCSlot*)slots[i];
        texture = slot->getTexture();// 换肤其实hack这个逻辑即可,给slot增加一个设置纹理的接口

        middleware::V2F_T2F_C4B* worldTriangles = slot->worldVerts;
        for (int v = 0, w = 0, vn = triangles.vertCount; v < vn; ++v, w += 2)
        {
            // 都是在计算更新x、y,需要知道更新uv的位置
            middleware::V2F_T2F_C4B* vertex = triangles.verts + v;
            middleware::V2F_T2F_C4B* worldVertex = worldTriangles + v;
            worldVertex->vertex.x = vertex->vertex.x * worldMatrix->m[0] + vertex->vertex.y * worldMatrix->m[4] + worldMatrix->m[12];
            worldVertex->vertex.y = vertex->vertex.x * worldMatrix->m[1] + vertex->vertex.y * worldMatrix->m[5] + worldMatrix->m[13];
            
            worldVertex->color.r = (GLubyte)r;
            worldVertex->color.g = (GLubyte)g;
            worldVertex->color.b = (GLubyte)b;
            worldVertex->color.a = (GLubyte)a;
        }
        middleware::V2F_T2F_C4B* worldTriangles = slot->worldVerts;
        auto vertexOffset = vb.getCurPos() / sizeof(middleware::V2F_T2F_C4B);
        vb.writeBytes((char*)worldTriangles, vbSize); // 写入vertexBuffer
    }
}

更新slot->worldVerts的地方在slot._updateFrame,接下来其实就是将_updateFrame的部分逻辑迁移到我自己写的setSpriteFrame

龙骨的节点在设置Assembler setAssembler

image.png

jsb-adapter/jsb-engine.js有在修改Assembler

image.png

构建jsb-engine.jsCC_NATIVERENDERER有关系

image.png

armatureDisplayProto

image.png

这个工程是用来生成适配jsb的,对应的仓库: github.com/cocos-creat…

感觉和jsb-dragonbones.js有关系,最后发现和这个关联不是很大

不同DisplayType对应的数据类型

image.png

image.png

image.png