导读:本文主要阐述了如何在团结引擎中导出鸿蒙工程,以及游戏侧与端侧通信的范例。
软件版本及环境
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内容如下。
#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是什么,有什么用?"。
绑定控件
在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无报错。
团结引擎官方文档推荐在Assets下Plugins文件中创建通信插件。
官方文档:TypeScript source plug-ins
├─Assets
│ ├─Plugins
│ │ └─OpenHarmony
.\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()函数。
// .\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方向对调。
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.目前游戏侧的变动只需将新导出项目中的如下文件替换到现有鸿蒙工程中就行。
.\entry\libs
.\entry\src\main\resources\rawfile
2.游戏侧与端侧通信主要依靠OpenHarmonyJSClass、OpenHarmonyJSObject、OpenHarmonyJSCallback等类实现。具体可参考团结引擎官方文档:Developing for OpenHarmony
这份鸿蒙(HarmonyOS NEXT)文档包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
OpenHarmony北向、南向开发环境搭建
●ArkTS语言
●安装DevEco Studio
●运用你的第一个ArkTS应用
●ArkUI声明式UI开发
.……
●Stage模型入门
●网络管理
●数据管理
●电话服务
●分布式应用开发
●通知与窗口管理
●多媒体技术
●安全技能
●任务管理
●WebGL
●国际化开发
●应用测试
●DFX面向未来设计
●鸿蒙系统移植和裁剪定制
……
●ArkTS实践
●UIAbility应用
●网络案例
……