How to make custom native calls in cocos creator 3.8?

I need:

  1. call native function in C++ or ObjC from TS

Simple example:

static bool nativeCompare(int a, int b) {
    return a == b;
}

Special example:

static void animateNode(Node *n) {
    // do something with node n created in TS
}

What steps do I have to make to call this from TS?

  1. call TS from native C++ or ObjC
static void nativeSomething() {
    // call TSClass().tsMethode("some String")
}

What steps do I have to make to call TS from Native code?

Is there any step by step guide?

Somehow I’ve managed it.

First, I’ve considered swing:

but then decided for sebind:

My C++ code:

#ifndef MY_CPP_CLASS_H
#define MY_CPP_CLASS_H

#include <string>

#include "bindings/sebind/intl/common.h"
#include "bindings/sebind/sebind.h"

#include "cocos/cocos.h"
#include "core/scene-graph/Node.h"

using namespace cc;

class MyCPPClass {
public:
    MyCPPClass() = default;
    MyCPPClass(se::Object *jsThis) : _jsThis(jsThis) {}

    // for some reason this variable is not set by constructor so I set it manually from TS
    void setJsThis(se::Object *t) {
        _jsThis = t;
    }
    
    // here existing Node instance is passed
    void playNode(se::Object *n) {
        Node* node = static_cast<Node*>(n->getPrivateData());
            if (node) {
                printf("ZEN77: Received Node with name: %s", node->getName().c_str());
                // Here you can manipulate the node as you wish
                //node->setScale(Vec3(0.5, 0.5, 0.5));
            } else {
                printf("ZEN77: Node is null or invalid.");
            }
    }
    
    // this function is called from TS
    std::string greet() {
        // here JS function is called from C++
        callJSFunction("onAssesmentDone");
        return "ZEN77: C++ called from JS";
    }
    
    std::string callJSFunction(const std::string &name) {
        if (!_jsThis) {
            return "ZEN77: jsThis is not set";
        }
        se::Value prop;
        _jsThis->getProperty(name, &prop);
        if (prop.isNullOrUndefined()) {
            return "ZEN77: function found!";
        }
        se::Value ret;
        bool retVal = prop.toObject()->call({}, _jsThis, &prop);
        assert(prop.isString());
        return prop.toString();
    }
    
private:
    se::Object *_jsThis{nullptr};
};

#endif

Registered like this:

// MyCPPClass.cpp
#include "MyCPPClass.h"
#include "mc-jsb-cpp-bridge.h"
#include "bindings/sebind/intl/common.h"
#include "bindings/sebind/sebind.h"

bool registerZen77Classes(se::Object *globalThis) {
    
    sebind::class_<MyCPPClass> fruitClass("MyCPPClass");
        {
            fruitClass
                .function("greet", &MyCPPClass::greet)
                .function("setJsThis", &MyCPPClass::setJsThis)
                .function("playNode", &MyCPPClass::playNode)
                .install(globalThis);
        }
    return true;
}

Called from Game.cpp:

int Game::init() {
  _windowInfo.title = GAME_NAME;
  // configurate window size
  // _windowInfo.height = 600;
  // _windowInfo.width  = 800;

#if CC_DEBUG
  _debuggerInfo.enabled = true;
#else
  _debuggerInfo.enabled = false;
#endif
  _debuggerInfo.port = 6086;
  _debuggerInfo.address = "0.0.0.0";
  _debuggerInfo.pauseOnStart = false;

  _xxteaKey = SCRIPT_XXTEAKEY;

  se::ScriptEngine::getInstance()->addRegisterCallback(registerZen77Classes);

  BaseGame::init();
  return 0;
}

}

TS code:

test_jsb_bind ()
    {
        // <M> Make MyCPPClass gracefully degrade in non-native. Otherwise you get error in browser
        //
        MyCPPClass.prototype.onAssesmentDone = function () {
            console.log("ZEN77: log: onAssesmentDone: JS called from CPP")
            return "ZEN77: onAssesmentDone: JS called from CPP"
        }

        const myCPPInstance = new MyCPPClass();
        myCPPInstance.setJsThis(myCPPInstance)
        const greeting = myCPPInstance.greet();
        console.log(greeting); // Outputs: "Hello from C++!"

        let n = this.node
        myCPPInstance.playNode(n)
    }

Added declarations so TS knows the C++ class:

declare class MyCPPClass {
    constructor();
    //    can't make the node of type Node. It gives me error. Any hint how?
    playNode(node: object): void;
    greet(): string;
    setJsThis(t: MyCPPClass): void;
    onAssesmentDone(): string;
}

That’s all the magic.

What is missing is:

  • make node parameter of type Node instead of object (see comment above)
  • provide blank implementation for MyCPPClass when being run in non-native environment.
  • not sure why _jsThis is not set and thus I use manual call setJsThis(…)

Any hints for my missing bits?