【鸿蒙实战开发】团结引擎适配HarmonyOS NEXT

鸿蒙初辟梦创道远 2024-08-17 14:03:02 阅读 58

导读:本文主要阐述了如何在团结引擎中导出鸿蒙工程,以及游戏侧与端侧通信的范例。

软件版本及环境

Tuanjie Hub:1.1.6

Tuanjie Editor:1.2.1 | 1.2.0

鸿蒙版本:HarmonyOS NEXT Beata1 (NEXT.0.0.26) | API 12

OpenHarmony:5.0.0.25(Beta1)

DevEco Studio:5.0.3.403

环境:Windows 10

团结引擎编安装辑器

选择1.2.1版本并勾选OpenHarmony Build Support

在这里插入图片描述

创建游戏工程

在这里插入图片描述

创建并绑定游戏控件

这部分我对unity是小白所以记录下,总体感觉和QT、Android原生开发差不多,抽象出来就是;创建layout > 添加控件 > 绑定控件 > 绑定事件

创建Canvas

在这里插入图片描述

创建GameObject

在这里插入图片描述

创建游戏控件

在这里插入图片描述

创建MyBehaviourScript

在这里插入图片描述

MyBehaviourScript是什么,有什么用?

使用VS Code打开UnityHarmony游戏工程,创建的MyBehaviourScript内容如下。

<code>#Path: Assets\MyBehaviourScript.cs

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class MyBehaviourScript : MonoBehaviour

{

*// Start is called before the first frame update*

void Start()

{

}

*// Update is called once per frame*

void Update()

{

}

}

MyBehaviourScript 继承自MonoBehaviour,所以问题变成了"MonoBehaviour是什么,有什么用?"。

From Kimi: Unity中的MonoBehaviour类是Unity中所有行为脚本的基类。

MonoBehaviour是Unity中一个非常重要的类,它允许开发者编写脚本来控制游戏对象的行为。以下是MonoBehaviour的一些常见用途:

生命周期方法:MonoBehaviour提供了一系列的生命周期方法,如Start(), Update(), FixedUpdate(), LateUpdate(), OnEnable(), OnDisable(),

OnDestroy()等,开发者可以在这些方法中编写代码来响应游戏对象的不同生命周期事件。事件处理:MonoBehaviour可以用于处理各种事件,比如用户输入、碰撞检测、动画事件等。游戏逻辑:开发者可以通过继承MonoBehaviour并重写其方法来实现游戏逻辑,比如移动、跳跃、攻击等。组件交互:MonoBehaviour可以访问和控制附加到同一游戏对象上的其他组件,比如Renderer, Collider, Rigidbody等。数据持久化:MonoBehaviour支持序列化,这意味着你可以在脚本中定义变量,Unity会在编辑器中显示这些变量,并允许你保存和加载它们的值。协程支持:MonoBehaviour支持使用协程(Coroutines),这使得你可以编写非阻塞的代码来处理需要时间的操作,比如等待某个事件或动画完成。

绑定控件

在MyBehaviourScript中声明控件变量,并绑定事件。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

public class MyBehaviourScript : MonoBehaviour

{

// 声明控件变量

public Button toastBtn, initSDKBtn, loginBtn, payBtn, doActionBtn, getDataBtn;

public InputField actionCodeInputField, actionDataInputField, actionResultInputField, getDataKeyInputField, getDataValueInputField, getDataResultInputField;

// Awake 初始化绑定控件,设置点击监听事件

void Awake()

{

Debug.Log("当前Unity版本: " + Application.unityVersion);

doActionBtn.onClick.AddListener(doAction);

getDataBtn.onClick.AddListener(getData);

toastBtn.onClick.AddListener(showToastRegister);

initSDKBtn.onClick.AddListener(initSDK);

loginBtn.onClick.AddListener(login);

payBtn.onClick.AddListener(pay);

Debug.Log("Unity 初始化游戏控件完成");

}

// Start is called before the first frame update

void Start()

{

Debug.Log("Unity start");

}

// Update is called once per frame

void Update()

{

}

/*

* 调用端侧doAction方法

*/

private void doAction()

{

Debug.Log("Unity MyBehaviourScript doAction >");

string actionCode = actionCodeInputField.text;

string actionData = actionDataInputField.text;

string actionResult = MyGameSDK.getInstance().doAction(int.Parse(actionCode), actionData);

actionResultInputField.text = actionResult;

Debug.Log("Unity MyBehaviourScript actionCode:" + int.Parse(actionCode) + " actionData:" + actionData + " actionResult:" + actionResult);

}

/*

* 调用端侧getData方法

*/

private void getData()

{

Debug.Log("Unity MyBehaviourScript getData >");

string dataKey = getDataKeyInputField.text;

string dataValue = getDataValueInputField.text;

string dataResult = MyGameSDK.getInstance().getData(dataKey, dataValue);

getDataResultInputField.text = dataResult;

Debug.Log("Unity MyBehaviourScript getData dataKey:" + dataKey + " dataValue:" + dataValue + " dataResult:" + dataResult);

}

/*

* 调用端侧Toast方法

*/

private void showToastRegister()

{

this.showToast("Click toast");

}

/*

* 调用端侧Toast方法

*/

private void showToast(string msg)

{

Debug.Log("Unity MyBehaviourScript toast >");

if (msg == null) msg = "msg is empty";

MyGameSDK.getInstance().showToast(msg);

}

/*

* 调用端侧初始化SDK

*/

private void initSDK()

{

Debug.Log("Unity MyBehaviourScript initSDK >");

MyGameSDK.getInstance().initSDK();

}

/*

* 调用端侧登录

*/

private void login()

{

Debug.Log("Unity MyBehaviourScript login >");

string loginStyle = "loginStyle";

MyGameSDK.getInstance().login(loginStyle);

}

/*

* 调用端侧支付

*/

private void pay()

{

Debug.Log("Unity MyBehaviourScript pay >");

string payStyle = "payStyle";

MyGameSDK.getInstance().pay(payStyle);

}

}

// 为了便于阅读和理解,此处将MyGameSDK类定义在同一个文件中

//

// 在实际应用中,建议将各个类定义在不同的文件中

class MyGameSDK

{

private static MyGameSDK instance;

// 团结引擎提供的C#与JS交互的类

// 更多用法参考团结引擎官方文档:https://docs.unity.cn/cn/tuanjiemanual/Manual/OpenHarmonyTypeScriptSourcePlugins.html

private OpenHarmonyJSClass harmonyBridgeClass;

private OpenHarmonyJSObject harmonyBridge;

private OpenHarmonyJSCallback messageCallback;

public static MyGameSDK getInstance()

{

if (instance == null)

{

instance = new MyGameSDK();

}

return instance;

}

private MyGameSDK()

{

Debug.Log("Unity MyGameSDK create ...");

harmonyBridgeClass = new OpenHarmonyJSClass("MyHarmonyBridge");

harmonyBridge = harmonyBridgeClass.CallStatic<OpenHarmonyJSObject>("getInstance");

messageCallback = new OpenHarmonyJSCallback(messageCallbackFun);

Debug.Log("Unity MyGameSDK created successfully!");

}

/*

* HarmonyOS端侧回调给Unity

*/

private void messageCallbackFun(params OpenHarmonyJSObject[] args)

{

Debug.Log("Unity MyGameSDK messageCallbackFun > args:" + args);

// 业务逻辑...

}

/*

* 端侧doAction

*/

public string doAction(int actionCode, string actionData)

{

if (harmonyBridge == null) return null;

try

{

Debug.Log("Unity MyGameSDK call doAction()");

return harmonyBridge.Call<string>("doAction", actionCode, actionData);

}

catch (System.Exception e)

{

Debug.Log("Unity MyGameSDK doAction error: " + e);

return null;

}

}

/*

* 端侧getData

*/

public string getData(string dataKey, string dataValue)

{

if (harmonyBridge == null) return null;

try

{

Debug.Log("Unity MyGameSDK call getData()");

return harmonyBridge.Call<string>("getData", dataKey, dataValue);

}

catch (System.Exception e)

{

Debug.Log("Unity MyGameSDK getData error: " + e);

return null;

}

}

/*

* 端侧Toast

*/

public void showToast(string msg)

{

if (harmonyBridge == null) return;

try

{

Debug.Log("Unity MyGameSDK call showToast()");

harmonyBridge.Call("showToast", msg);

}

catch (System.Exception e)

{

Debug.Log("Unity MyGameSDK showToast error: " + e);

}

}

/*

* 端侧初始化SDK

*/

public void initSDK()

{

if (harmonyBridge == null || messageCallback == null) return;

try

{

Debug.Log("Unity MyGameSDK call initSDK()");

harmonyBridge.Call("initSDK", messageCallback);

}

catch (System.Exception e)

{

Debug.Log("Unity MyGameSDK initSDK error: " + e);

}

}

/*

* 端侧登录

*/

public void login(string loginStyle)

{

if (harmonyBridge == null) return;

try

{

Debug.Log("Unity MyGameSDK call login()");

harmonyBridge.Call("login", loginStyle);

}

catch (System.Exception e)

{

Debug.Log("Unity MyGameSDK login error: " + e);

}

}

/*

* 端侧支付

*/

public void pay(string payInfo)

{

if (harmonyBridge == null) return;

try

{

Debug.Log("Unity MyGameSDK call pay()");

harmonyBridge.Call("pay", payInfo);

}

catch (System.Exception e)

{

Debug.Log("Unity MyGameSDK pay error: " + e);

}

}

}

将控件与GameObject关联。

在这里插入图片描述

在GameObject中绑定的MyBehaviourScript绑定Canvas中的游戏控件。

在这里插入图片描述

控件全部绑定完成,在团结引擎中运行Console无报错。

编写通信Plugin

团结引擎官方文档推荐在Assets下Plugins文件中创建通信插件。

官方文档:TypeScript source plug-ins

<code>├─Assets

│ ├─Plugins

│ │ └─OpenHarmony

在这里插入图片描述

这里的MyHarmonyBridge.ets其实不会参与游戏侧编译,会原样输出到导出的鸿蒙工程ets目录下。

<code>.\entry\src\main\ets\MyHarmonyBridge.ets

导出鸿蒙工程

控件全部绑定完成,在团结引擎中运行Console无报错。File > Build Settings选中OpenHarmony项,勾选Export Project > Switch Platfrom 设置导出鸿蒙工程的应用包名(bundleName):File > Build Settings > Player Settings bundleName规范:bundleName规范

在这里插入图片描述

目前团结引擎导出的鸿蒙工程还是API 11,升级到API 12可通过DevEco Studio中的MergeAssistant工具将工程升级。也可直接创建一个API 12的鸿蒙工程(HarmonyUnity),将团结引擎导出API 11工程中必要的文件替换到新创建的鸿蒙工程中。本文直接创建新的API12鸿蒙工程,将团结引擎导出的必要文件替换进来。

主要替换entry下文件,以及添加导出的libs文件。

在这里插入图片描述

注册plugin通信ts类,在TuanjieJSScriptRegister中完善registerJSScriptToCSharp()函数。

<code>// .\entry\src\main\ets\common\TuanjieJSScriptRegister.ets

import { TuanjieLog } from '../common/TuanjieLog';

import { RegisterMyHarmonyBridge } from '../MyHarmonyBridge';

function register(tuanjieJSClasses:Record<string, Object>, functionName :Function) {

let exportObj:Record<string, Object> = functionName();

for (let key of Object.keys(exportObj)) {

if(tuanjieJSClasses[key] !== undefined){

TuanjieLog.error('Duplicate keys detected when exporting TypeScript module to C#: ' + key + '. This might cause overwrite exists module.');

}

tuanjieJSClasses[key] = exportObj[key];

}

}

export function registerJSScriptToCSharp() {

let tuanjieJSClasses:Record<string, Object> = {};

register(tuanjieJSClasses, RegisterMyHarmonyBridge)

return tuanjieJSClasses;

}

测试通信

游戏启动,查看日志输出。

在这里插入图片描述

在这里插入图片描述

执行initSDK()后调用getData()查看日志输出。

在这里插入图片描述

其他

Bug: 目前游戏画面方向与手机传感器感知方向相反,解决方法是修改.\entry\src\main\ets\utils\WindowUtils.ts中nativeToOpenHarmonyOrientationMap的ScreenOrientation.kLandscapeLeft、ScreenOrientation.kLandscapeRight方向对调。

<code>static nativeToOpenHarmonyOrientationMap: Map<number, window.Orientation> = (() => {

let orientationMap = new Map<number, window.Orientation>();

orientationMap.set(ScreenOrientation.kScreenOrientationUnknown, window.Orientation.UNSPECIFIED);

orientationMap.set(ScreenOrientation.kPortrait, window.Orientation.PORTRAIT);

orientationMap.set(ScreenOrientation.kPortraitUpsideDown, window.Orientation.PORTRAIT_INVERTED);

// orientationMap.set(ScreenOrientation.kLandscapeLeft, window.Orientation.LANDSCAPE_INVERTED);

// orientationMap.set(ScreenOrientation.kLandscapeRight, window.Orientation.LANDSCAPE);

// Todo: 2024/7/18 darren 经与华为确认是HarmonyOS NEXT 与 团结引擎兼容适配问题,暂时对调两个方向设定,后续团结引擎会适配修复

orientationMap.set(ScreenOrientation.kLandscapeLeft, window.Orientation.LANDSCAPE);

orientationMap.set(ScreenOrientation.kLandscapeRight, window.Orientation.LANDSCAPE_INVERTED);

orientationMap.set(ScreenOrientation.kAutoRotation, window.Orientation.AUTO_ROTATION);

return orientationMap;

})();

支持x86_64架构模拟器:

团结引擎中:File > Build Settings > Player Settings > OtherSettings > Target Architectures 勾选x86-64,然后重新导出。 目前游戏侧的变动只需将新导出项目中的如下文件替换到现有鸿蒙工程中就行。

text 代码解读复制代码.\entry\libs

.\entry\src\main\resources\rawfile

鸿蒙工程中:entry > build-profile.json5 > abiFilters添加编译架构,其他编译架构参考 使用DevEco Studio模板构建NDK工程 。

{

"apiType": "stageMode",

"buildOption": {

"externalNativeOptions": {

"abiFilters": [

"arm64-v8a",

// 添加 x86_64 架构支持电脑模拟器

// 测试模拟器能运行,但是UI尺寸显示不正确

"x86_64"

],

}

},

"buildOptionSet": [

{

"name": "release",

"arkOptions": {

"obfuscation": {

"ruleOptions": {

"enable": true,

"files": [

"./obfuscation-rules.txt"

]

}

}

}

},

],

"targets": [

{

"name": "default"

}

]

}

选中创建的模拟器运行。

在这里插入图片描述

在这里插入图片描述

总结

1.目前游戏侧的变动只需将新导出项目中的如下文件替换到现有鸿蒙工程中就行。

<code>.\entry\libs

.\entry\src\main\resources\rawfile

2.游戏侧与端侧通信主要依靠OpenHarmonyJSClass、OpenHarmonyJSObject、OpenHarmonyJSCallback等类实现。具体可参考团结引擎官方文档:Developing for OpenHarmony

写在最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。

这份鸿蒙(HarmonyOS NEXT)文档包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习文档能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档

鸿蒙(HarmonyOS NEXT)5.0最新学习路线

在这里插入图片描述

有了路线图,怎么能没有学习文档呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档

《鸿蒙 (OpenHarmony)开发入门教学视频》

在这里插入图片描述

《鸿蒙生态应用开发V3.0白皮书》

在这里插入图片描述

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

在这里插入图片描述

《鸿蒙开发基础》

●ArkTS语言

●安装DevEco Studio

●运用你的第一个ArkTS应用

●ArkUI声明式UI开发

.……

在这里插入图片描述

《鸿蒙开发进阶》

●Stage模型入门

●网络管理

●数据管理

●电话服务

●分布式应用开发

●通知与窗口管理

●多媒体技术

●安全技能

●任务管理

●WebGL

●国际化开发

●应用测试

●DFX面向未来设计

●鸿蒙系统移植和裁剪定制

……

在这里插入图片描述

《鸿蒙进阶实战》

●ArkTS实践

●UIAbility应用

●网络案例

……

在这里插入图片描述

获取以上完整鸿蒙HarmonyOS学习文档,请点击→纯血版全套鸿蒙HarmonyOS学习文档



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。