游戏与 ArkTS 接口交互(Cocos 2d-x) 原创

游戏技术分享
发布于 2025-5-7 15:37
浏览
0收藏

游戏点亮后,由于HarmonyOS NEXT是一个全新的系统,因此游戏原有的系统方法(电量、振动器、网络等),在HarmonyOS NEXT平台可能不支持,需要根据当前游戏实际使用情况进行替换适配,为向玩家提供快捷、安全的登录、支付等功能,游戏需集成登录、支付等应用服务。官方文档都有相应样例,本篇主要介绍接入过程中涉及的以下两点:

游戏与 ArkTS 接口交互(Cocos 2d-x)-鸿蒙开发者社区cke_5762.png

C++与ArkTS交互

当前HarmonyOS NEXT提供的系统接口主要包括如下两类:

游戏与 ArkTS 接口交互(Cocos 2d-x)-鸿蒙开发者社区cke_9009.png

下面主要介绍的是使用ArkTS接口时通过NAPI框架进行C/C++和JS跨语言调用。如下以两个系统能力为例,演示了如何基于NAPI框架进行C++和JS的相互调用。更多示例及NAPI原理可以参考HarmonyOS NEXT版本引擎样例工程及​​NAPI文档​​。

C++调用JS示例

游戏与 ArkTS 接口交互(Cocos 2d-x)-鸿蒙开发者社区cke_23728.png

此处以获取当前语言为例。

  • 声明JS方法。

// DeviceUtils.ts:
import i18n from '@ohos.i18n';

export class DeviceUtils {
    static getSystemLanguage(): string {
        return i18n.System.getSystemLanguage();
    }
}
  • 注册JS方法(该方法会在游戏拉起的时候注册到Worker线程中)。

// CocosWorker.ts:
...
NapiHelper.registerFunctions(napiContext.registerFunction);
...

// NapiHelper.ts:
import { DeviceUtils } from '/system/device/DeviceUtils';
export class NapiHelper {
    static registerFunctions(registerFunc : Function) {
        NapiHelper.registerDeviceUtils(registerFunc);
    }
    private static registerDeviceUtils(registerFunc : Function) {
        registerFunc('DeviceUtils.getSystemLanguage', DeviceUtils.getSystemLanguage);
    }
}
  • C++调用JS方法。

// CCApplication-ohos.cpp:
#include "napi/helper/NapiHelper.h"

const char * Application::getCurrentLanguageCode() {
    static char code[3] = {0};
    std::string systemLanguage = JSFunction::getFunction("DeviceUtils.getSystemLanguage").invoke<std::string>();
    strncpy(code, systemLanguage.c_str(), 2);
    code[2] = '\0';
    return code;
}

JS调用C++示例

游戏与 ArkTS 接口交互(Cocos 2d-x)-鸿蒙开发者社区cke_26755.png

此处以调用editbox回调函数为例。

  • 将C++方法注册到NAPI中,暴露给JS。

// InputNapi.cpp:
napi_value InputNapi::editBoxOnFocusCB(napi_env env, napi_callback_info info) {
    ...
}
...

// plugin_manager.cpp:
OHOS_LOGD("NapiManager::GetContext INPUT_NAPI");
napi_property_descriptor desc[] = {
    DECLARE_NAPI_FUNCTION("editBoxOnFocusCB", InputNapi::editBoxOnFocusCB)
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
...
  • JS调用C++方法。

// CocosWorker.ts:
...
const inputNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.INPUT_NAPI);
...
workerPort.onmessage = function(e) : void {
    let data = e.data:
    switch(data.type) {
        case 'editBoxOnFocus':
            inputNapi.editBoxOnFocusCB(data.viewTag);
            break;
        ...

线程模型说明

当前HarmonyOS NEXT线程模型为线程间隔离,内存不共享,导致Ark层开发的基础模块和业务需要按照单线程模式进行设计,所以会涉及到一些跨线程的接口调用、数据访问和线程切换的场景。

如下以调用应用服务登录初始化接口为例,介绍如何调用一些只能在UI线程执行的系统方法及如何将UI线程结果返回给子线程的,具体的线程原理可参考​​官方文档​​。

游戏与 ArkTS 接口交互(Cocos 2d-x)-鸿蒙开发者社区image.png

Worker线程调用UI线程方法

  • 游戏启动时先将相应ts代码中Worker线程初始化。

// CocosWorker.ts:
...
workerPort.onmessage = function(e) : void {
    let data = e.data:
    switch(data.type) {
        case "onXCload":
            LoginSDK.init(workerPort);
            ...
        ...

// SDKManager.ts:
export class LoginSDK {
    ...
    static init(workerPort: ThreadWorkerGlobalScope) : void {
        LoginSDK.workerPort= workerPort;
    }
    ...
}
  • Worker线程发送消息给UI线程。

// SDKManager.ts:
...
static loginInit(): string {
    LoginSDK.workerPort.postMessage({
        'module': LoginSDK.MODULE_NAME, 'function': 'loginInit', 'viewTag': "1"
    });
    ...
}
  • UI线程接收消息并处理。

// Index.ets:
...
.onLoad((context) => {
    ...
    cocosWorker.onmessage = async (event: MessageEvents) => {
       let eventData: EventData = event.data;
       switch (eventData.module) {
            case 'LoginSDK':
                let result: string;
                try {
                    result = await sdkManagerMsg.handlePullSDK(eventData as EventData);
                } catch(e) {
                    ...
                }
                ...
// SDKManagerMsg.ts:
...
async handlePullSDK(eventData: EventData): Promise<string> {
    let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT);
    switch (eventData.function) {
      case "loginInit":
        return await this.handleLoginInit(context);
      ...
     }
}

将UI线程结果返回给子线程

  • 登录初始化结果返回,需要通知Worker线程执行native层的回调函数。

// Index.ets:
...
.onLoad((context) => {
    ...
    cocosWorker.onmessage = async (event: MessageEvents) => {
        let eventData: EventData = event.data;
       switch (eventData.module) {
            case 'LoginSDK':
                ...
            if (result != null || result != undefined) {
                  try{
                    cocosWorker.postMessage({type:"syncLoginSDKResult", data: result});
                  }catch (e){
                    ...
                  }
            }
            ...
  • Worker线程接收消息并做出处理。

// CocosWorker.ts:
...
workerPort.onmessage = function(e) : void {
    let data = e.data;
    switch(data.type) {
        case "syncLoginSDKResult":
            loginSDKNapi.syncLoginSDKResult(data.data);
            break;
        ...

 更多问题可关注:

鸿蒙游戏官方网站:​​已有游戏移植-鸿蒙游戏-华为开发者联盟​

公开课:​​华为开发者学堂​

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
收藏
回复
举报
回复
    相关推荐