三七互娱技术团队

多平台统一MaxSDK的设计实现

01

MaxSDK介绍

Image
Image

研发对接SDK,同一个游戏需要对接安卓、iOS等系统,在我们集团,因业务拆分,可能同游戏还需要对接37网游、37手游、37Games,不同的部门、系统对接接口存在差异,同时研发对Android的Java语言、iOS的Object-C语言不熟悉,进一步导致SDK对接成本、沟通成本飙升,对游戏研发来说,耗费了过多的精力和成本在处理对接SDK的事上。如图所示:

Image

如果有一套多平台统一SDK,直接输出游戏研发语言级接口(例如unity的c#),研发可以直接调用,方便研发对接,将大大降低对接和沟通成本。

根据以上的背景和需求,我们设计并实现了一套多平台统一的SDK:MaxSDK。

MaxSDK是跨引擎、跨平台的统一对接SDK,通过直接输出游戏研发语言级接口(例如unity的c#),来提高研发对接SDK的效率,降低对接时的认知、沟通成本,减少对接时候研发的精力损耗,让研发能把精力集中提升到游戏内容和品质上,提高竞争力。

接入MaxSDK后的情况如图所示:

Image

由上图可见,MaxSDK 屏蔽了复杂的跨语言桥接转调流程,通过统一的接口设计,统一的参数处理,统一的回调处理,灵活的参数扩展和方法扩展,实现了跨引擎、跨业务线、跨平台的封装实现。

02

如何实现MaxSDK

Image
Image

2.1、SDK整体架构设计

Image
Image

MaxSDK如何实现跨引擎和跨多平台统一,这里重点介绍其分层架构思路和具体实现 。首先我们抛开技术,从业务的核心需求出发,去调研拆解,找到各部门和研发的痛点。

目前集团现状:

  • 研发需要对接安卓、iOS等平台,后续可能覆盖鸿蒙、小游戏等

    可能需要对接37网游、37Games、37手游

    国内国外,SDK要求上存在客观差异,体现在接口不同、参数差异等

从研发调研意见来看:

  •  研发希望能直接用引擎语言实现对接(例如unity的c#),最好只有一个入口类

     希望能屏蔽跨语言的实现细节

     希望接口尽量简洁,易于理解,调用和回调逻辑清晰

     希望支持直接导入package文件,方便集成

从拆解情况来简单分层,引擎层是必不可少,涉及研发的对接,以及引擎接口的调用。而要接入平台,那平台层也不可或缺,平台层涉及具体功能的实现和回调。而多个业务线,业务逻辑区分,可体现在平台层中。总体梳理后,大体结构如图所示:

Image
  • 引擎层。包括unity、cocos等,负责call调用 MaxSDK 的相关引擎接口,处理回调结果。

    平台层。包括各业务线在不同平台SDK的具体功能实现,并将处理结果callback回调给引擎层。

2.2、引擎层架构设计

Image
Image

先从引擎层的架构设计出发,以unity引擎为例。研发需要在游戏场景中调用MaxSDK的c#接口来实现相关功能,在MaxSDK中要调用目标平台,例如安卓,就需要将c#接口桥接转调安卓的java接口,然后在安卓中回调处理结果给c#接口。整个架构如下:

Image
  • 研发接入层。研发集成MaxSDK,调用相关功能接口。

    统一接口层(c#)。实现MaxSDK,c#统一接口声明和参数定义。

    接口桥接层。实现跨语言的接口桥接转调。

    平台实现抽象层。平台层的统一接口声明和参数定义,例如安卓的接口定义层。

    平台具体实现层。平台层具体功能实现,例如安卓的登录接口的具体实现。

2.3、引擎层功能实现

Image
Image

将概要层级分好之后,我们以unity为例,可以进一步细化分层架构,按照之前调研研发的反馈,将整个类关系初步设计为:

Image
  • 研发只需要调用MaxSDK的接口,实现MaxSDKListener的回调,即可完成MaxSDK的对接。

    图中的 UnityEventHandler类 ,继承自 MonoBehaviour,可供Unity的场景绑定为对应脚本。

     MaxSDK 作为统一的调用出口,封装了不同平台的具体实现的接口调用分发。

     具体实现层,根据不同的平台SDK,实现逻辑不同。

2.3.1、环境安装

Image

类的交互架构设计好之后,就可以开始具体coding了。在unity中,编码前需要安装相关引擎环境。主要包括:

Unity(c#)  
https://unity.cn/releases 
Visual Studio
https://visualstudio.microsoft.com/zh-hans/vs/mac/
VS Code
https://code.visualstudio.com/

Image

在unity安装完成后,项目依赖的unity版本要特别注意,之间版本的差异可能导致项目报错,最好保持版本一致。

打开后,在unity 编辑器的外部工具设置中,可将code的IDE关联起来,可选VS或者VS CODE。

Image

进入unity编辑器后,我们可以通过拖拽实现一个简单的SDK demo 界面:

Image

2.3.2、核心接口

Image

开发环境准备就绪后,首先我们需要根据各业务部门的需求,拆出接口,定义出MaxSDK要支持的功能。首先定义一个接口base类 MaxSDKUnitySupportBase,其中包含了各种核心接口,例如初始化init、登录Login、登出Logout、支付Pay等。

/** *  unity调用平台base类 *   *  具体平台,可以通过继承该base类,按需复写 */namespace maxsdk{    public abstract class MaxSDKUnitySupportBase    {        public abstract int GetSDKType();        public abstract void SetListener(MaxSDKListener listener);        public abstract void Init(MaxSDKInitInfo info);        public abstract void Login(MaxSDKLoginInfo info);        public abstract void Logout(MaxSDKLogoutInfo info);        public abstract void Pay(MaxSDKPayInfo info);        public abstract void ReportRoleInfo(MaxSDKRoleInfo info);        public abstract void SwitchAccount(MaxSDKSwitchAccountInfo info);        public abstract void ReportEvent(MaxSDKReportEventInfo info); //埋点事件上报        public abstract void OpenActionExt(MaxSDKActionInfo info);        public abstract void Share(MaxSDKShareInfo info);        public abstract string DispatchSync(MaxSDKDispatchInfo info);        public abstract void DispatchASync(MaxSDKDispatchInfo info);        public abstract bool IsActionSupported(int type);        /* ----------- 我是一条华丽的分割线 ------------ */        public virtual string GetAppConfig()        {            return "";        }        public virtual void ExitGame(MaxSDKExitInfo info)        {        }        public virtual string DispatchSync(string methodName, MaxSDKBaseInfo info)        {            return "";        }        public virtual void DispatchASync(string methodName, MaxSDKBaseInfo info)        {        }    }}

2.3.3、单例入口类

Image

接口定义清楚后,在研发调用层,必须有一个统一的接口入口,此处定义一个单例类MaxSDK,方便研发调用。

/*** MaxSDK,统一接口单例类*/namespace maxsdk{    public sealed class MaxSDK    {        private static readonly MaxSDK INSTANCE = new MaxSDK();        private MaxSDKUnitySupportBase supportBase;        public static MaxSDK GetInstance()        {            return INSTANCE;        }    //........省略后续代码..............    }}

此处,我们将前面定义的核心接口类 `MaxSDKUnitySupportBase`,以组合方式集成进来,方便后续接口的调用。

注意:此处为啥用组合而不用继承?一个考虑点是后续接口有扩展需更新SDK,如果用继承的方式,即使不做具体实现,那研发必须复写新接口,对研发侵入性高。改为组合模式集成后,研发按需调用和实现接口即可,无侵入性。

2.3.4、对外接口设计

Image

研发调用对外的单例接口实现相关功能,必然要有对外的接口层和具体实现关联起来。在MaxSDK类中,直接转调 MaxSDKUnitySupportBase 接口中的方法。

/**     * 获取当前 SDK 的类型     */    public int GetSDKType()    {        if (supportBase == null)        {            return MaxSDKType.MAX_SDK_UNKNOWN;        }        return supportBase.GetSDKType();    }    public string GetAppConfig()    {        if (supportBase == null)        {            return "";        }        return supportBase.GetAppConfig();    }    /**     * 设置监听器     */    public void SetListener(MaxSDKListener listener)    {        supportBase?.SetListener(listener);    }    /**     * 初始化     */    public void Init(MaxSDKInitInfo initInfo)    {        supportBase?.Init(initInfo);    }    /**     * 登录     */    public void Login(MaxSDKLoginInfo loginInfo)    {        supportBase?.Login(loginInfo);    }    /**    * 数据上报接口    */    public void ReportRoleInfo(MaxSDKRoleInfo roleInfo)    {        supportBase?.ReportRoleInfo(roleInfo);    }    /**     * 支付     */    public void Pay(MaxSDKPayInfo orderInfo)    {        supportBase?.Pay(orderInfo);    } //........省略后续类似转调代码..............

2.3.5、参数设计

Image

接口方法的调用,不可缺少参数,参数包括入参和出参。在参数设计上,涉及不同业务部门,必须要统一标准,降低对接认知负担。

接口方法入参:

  • 统一入参规范,XXXInfo,代表入参规范类名,例如`MaxSDKLoginInfo`;

    所有接口的 XXXInfo 均继承 `MaxSDKBaseInfo`,内置`extData`,方便json参数返回扩展

    将多个不同接口参数统一封装在`MaxSDKInfo.cs`文件中

    接口参数封装和接口关联的常量和变量

/** *  XXXInfo,代表入参规范类名 */namespace maxsdk{    //通用请求参数,封装对象 MaxSDKBaseInfo    public class MaxSDKBaseInfo    {        public string extData;  //扩展字段,json格式    }    //初始化接口,请求info    public class MaxSDKInitInfo : MaxSDKBaseInfo    {        public string appId;  //应用ID    }    //登录接口,请求info    public class MaxSDKLoginInfo : MaxSDKBaseInfo    {    }    // 支付接口,订单info    public class MaxSDKPayInfo : MaxSDKBaseInfo    {        public string productId;       //商品ID        public string productName; //商品名称        public string serverId;    //区服ID        public string serverName;  //区服名称        public string roleId;      //角色ID        public string roleName;    //角色名称        public string roleType = "";    // 角色类型,战士/道士/法师…………        public int roleLevel;     // 角色等级        public string roleBronLevel = "";  //转生等级,默认传0        public string roleLevelMTime;  //角色等级变化时间(单位:秒)        public string partyName;   //帮派名        public string cpOrderId;   //研发订单号        public string orderTime;   //订单服务器时间        public string sign;    //订单签名,由服务器生成,参与sign的订单时间和orderTime保持一致        public int radio = 10; //充值比例        public float money;    //支付金额        public int gameCoin = -1; //游戏币,用于生成sign        public bool subscription;   //该商品是否为订阅制商品,默认为false     }} //........省略后续类似代码..............//

接口回调出参:

  • 参数设计,统一出参接口规范。

    统一出参规范, XXXBean,代表返回参数规范类名;例如 `MaxSDKBean`

    所有接口的返回 XXXBean 均继承 `MaxSDKBaseBean`,内置`extData`,方便json参数返回扩展

    将多个不同接口返回参数统一封装在`MaxSDKBean.cs`文件中

    接口参数封装和接口关联的常量和变量

/** *  XXXBean,代表返回参数规范类名 */namespace maxsdk{    /**     * 通用返回参数,封装对象 MaxSDKBaseBean     */    public class MaxSDKBaseBean    {        public string extData;  //扩展字段,json格式    }    // 失败信息    public class MaxSDKFailBean : MaxSDKBaseBean    {        /*         * 错误码         */        public string code;        /*         * 错误提示         */        public string msg;    }    //init初始化接口,返回bean    public class MaxSDKInitBean : MaxSDKBaseBean    {        //todo 待补充    }    // 用户信息,登录回调中使用    public class MaxSDKLoginBean : MaxSDKBaseBean    {        public string token;    //用户的token        public string puid;     //渠道侧的用户ID        public string uid;      //SDK侧的用户ID        public string uname;    //SDK侧用户帐号    }    // 支付信息,支付回调中使用    public class MaxSDKPayBean : MaxSDKBaseBean    {        /*         *  平台订单号         */        public string platformOrderID;    }} //........省略后续类似代码..............//

2.3.6、功能实现类

Image

引擎要调用多个平台不同的实现。在unity中,可利用unity的特定编译标识符来实现区分:

> UNITY_ANDROID - Android平台
> UNITY_IOS  - iOS平台
> UNITY_STANDALONE_WIN - windows
> UNITY_STANDALONE_OSX - mac OSX
> UNITY_EDITOR  - unity中编辑器

/*** MaxSDK,统一接口单例类*/namespace maxsdk{    public sealed class MaxSDK    {        private static readonly MaxSDK INSTANCE = new MaxSDK();        private MaxSDKUnitySupportBase supportBase;        public static MaxSDK GetInstance()        {            return INSTANCE;        }        //构造函数        private MaxSDK()        {            Debug.Log("开始设置Unity-平台桥接,当前平台:" + Application.platform);#if UNITY_ANDROID && !UNITY_EDITOR            supportBase = new MaxSDKUnitySupportAndroid();#elif UNITY_IOS && !UNITY_EDITOR            supportBase = new MaxSDKUnitySupportIOS();#elif UNITY_STANDALONE_WIN && !UNITY_EDITOR            supportBase = new MaxSDKUnitySupportWin();#endif        } //........省略后续类似代码..............//}

还是以安卓为例,声明了当前编译环境是导出安卓平台工程时,实例化 MaxSDKUnitySupportAndrioid:

supportBase = new MaxSDKUnitySupportAndrioid();

2.3.7、扩展接口

Image

有时候我们的核心接口并不能满足研发的需求,可能不同业务线有额外的接口需要对接,为此设计了扩展方法接口:同步的DispatchSync接口和异步 DispatchASync接口。

接口声明:

/** *  unity调用平台base类 *   *  具体平台,可以通过继承该base类,按需复写 */namespace maxsdk{    public abstract class MaxSDKUnitySupportBase    {        public virtual string DispatchSync(string methodName, MaxSDKBaseInfo info)        {            return "";        }        public virtual void DispatchASync(string methodName, MaxSDKBaseInfo info)        {        }    }}

接口实现:

Image

2.3.8、接口桥接转调

Image

在具体实现类,实现 c# --> 目标平台接口的桥接转调,这也是跨平台的核心,不同目标平台有不同的桥接实现。以上述安卓平台为例,MaxSDKUnitySupportAndrioid就是具体的核心实现:

Image

以 Login 登录为例,MaxSDKUnitySupportAndrioid在实例化时,在构造函数实例化AndroidJavaObject  (AndroidJavaObject是c#调用Java 类实例的封装)对象unityActivity,研发在调用MaxSDK的 Login接口时,通过unityActivity对象的Call方法来实现接口转调。

其他平台的实例化也类似,例如iOS的转调实现:

/** *  unity调用ios中的方法 */using System.Runtime.InteropServices;namespace maxsdk{//#if UNITY_IOS && !UNITY_EDITOR    public class MaxSDKUnitySupportIOS : MaxSDKUnitySupportBase    {        public override int GetSDKType()        {            int sdkType = MaxSDKType.MAX_SDK_UNKNOWN;            string typeStr = MaxSDK_Call_Sync("getSDKType", "");            int.TryParse(typeStr, out sdkType);            return sdkType;        }        public override void SetListener(MaxSDKListener listener)        {            Debug.Log("gameObject is " + listener.gameObject.name);            if (listener == null)            {                Debug.LogError("set SQSDKListener error, listener is null");                return;            }            string gameObjectName = listener.gameObject.name;            MaxSDK_Call_Async("setGameObject", gameObjectName);        }        public override void Init(MaxSDKInitInfo info)        {            MaxSDK_Call_Async("init", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void Login(MaxSDKLoginInfo info)        {            MaxSDK_Call_Async("login", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void Logout(MaxSDKLogoutInfo info)        {            MaxSDK_Call_Async("logout", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void Pay(MaxSDKPayInfo info)        {            MaxSDK_Call_Async("pay", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void Share(MaxSDKShareInfo info)        {            MaxSDK_Call_Async("share", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void ReportRoleInfo(MaxSDKRoleInfo info)        {            MaxSDK_Call_Async("reportRoleInfo", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void SwitchAccount(MaxSDKSwitchAccountInfo info)        {            MaxSDK_Call_Async("switchAccount", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void ReportEvent(MaxSDKReportEventInfo info)        {            MaxSDK_Call_Async("reportEvent", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void ExitGame(MaxSDKExitInfo info)        {            MaxSDK_Call_Async("exitGame", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override void OpenActionExt(MaxSDKActionInfo info)        {            MaxSDK_Call_Async("openActionExt", JsonConvert.SerializeObject(info, Formatting.Indented));        }        public override bool IsActionSupported(int type)        {            bool isSupported = false;            string resultStr = MaxSDK_Call_Sync("isActionSupported", type.ToString());            bool.TryParse(resultStr, out isSupported);            return isSupported;        }        /**        * 扩展接口(同步),支持返回值        */        public override string DispatchSync(MaxSDKDispatchInfo info)        {            return MaxSDK_Call_Sync(info.apiName, JsonConvert.SerializeObject(info, Formatting.Indented));        }        /**         * 扩展接口(异步),带统一回调方法  OnDispatchResult(MaxSDKDispatchBean bean)         */        public override void DispatchASync(MaxSDKDispatchInfo info)        {            MaxSDK_Call_Async(info.apiName, JsonConvert.SerializeObject(info, Formatting.Indented));        }        [DllImport("__Internal")]        private static extern void MaxSDK_Call_Async(string method, string data);        [DllImport("__Internal")]        private static extern string MaxSDK_Call_Sync(string method, string data);    }//#endif}

2.3.9、接口回调

Image

上述介绍了接口调用Call的流程,在完整的接口调用中,CallBack接口回调的设计也是不可或缺的重要环节。
接口回调,首要需要注册回调。基于研发接口调用性的考虑,设计了一个对外的统一设置接口:

 /**     * 设置监听器     */    public void SetListener(MaxSDKListener listener)    {        supportBase?.SetListener(listener);    }

其中MaxSDKListener实现了MonoBehaviour,方便将回调方法和UI脚本绑定相关的点击事件。MonoBehaviour 是一个基类,所有 Unity 脚本都派生自该类。

namespace maxsdk{    public abstract class MaxSDKListener : MonoBehaviour    {        /* --------- 初始化回调 ----------- */        public void CallInitSuccess(string json)        {            MaxSDKInitBean initBean = JsonConvert.DeserializeObject<MaxSDKInitBean>(json);            OnInitSuccess(initBean);        }        public void CallInitFail(string json)        {            OnInitFail(JsonToErrorInfo(json));        }        public abstract void OnInitSuccess(MaxSDKInitBean initBean);        public abstract void OnInitFail(MaxSDKFailBean bean);        /* --------- 登录回调 ----------- */        public void CallLoginSuccess(string json)        {            MaxSDKLoginBean userInfo = JsonConvert.DeserializeObject<MaxSDKLoginBean>(json);            OnLoginSuccess(userInfo);        }        public void CallLoginFail(string json)        {            OnLoginFail(JsonToErrorInfo(json));        }        public abstract void OnLoginSuccess(MaxSDKLoginBean bean);        public abstract void OnLoginFail(MaxSDKFailBean bean);        //..................省略其他的pay、logout等类似回调接口..............    }}

在上述回调接口中,实现了统一处理,包括接口名称规范统一和参数统一。
- 所有的从平台回调过来的接口名称,供平台内部跨平台使用,以Call_[interfaceName]_Success或  Call_[interfaceName]_Fail命名,而供研发实现的接口,统一定义为abstract,以OnXXX格式作为通用的回调名字命名,降低认知成本。 
- 所有的Call_[interfaceName]的接口,参数都是 json格式的字符串,这样方便跨平台参数序列化的传递,后续即使平台侧接口参数更改,也无需调整回调通信的方式,处理更灵活。

2.3.10、demo调用调试

Image

接口的call和callback都实现之后,就需要在demo中测试效果,首先在unity的编辑器中,将具体UI绑定点击事件:

Image
//SQWYUnityEventHandler.cspublic void OnClickLogin(){#if UNITY_EDITOR    EditorUtility.DisplayDialog("提示", "点击了登录", "Yes", "No");#endif#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_WIN    MaxSDKLoginInfo info = new MaxSDKLoginInfo();    MaxSDK.GetInstance().Login(info);#endif}

点击demo中的按钮,例如【登录】,将弹出提示框。整体效果如下:

Image

这里要注意,当处于播放模式时,修改不会最终生效。

最终,我们从demo的按钮点击开始,调用接口,处理回调,完成了引擎层的接口调用流程实现。

Image

2.4、平台层架构设计

Image
Image

上述完成了引擎层的接口调用,有个疑问没有解除,就是具体的SDK功能是如何实现的?实现的结果是如何回调给引擎的?这些答案都要在平台层实现里面找到。

平台层的实现,以安卓为例,在导出了安卓相关工程后,如何在一个工程里面让37网游、37手游、37Games不同的业务线调用各自的业务具体实现呢?还是从需求的拆解和分析出发,得到平台层的分层架构:

Image
  • 接口层统一声明,各接口名、接口数和作用对齐

    桥阶层统一规范,包括回调方法名,回调参数

    不同平台实现具体实现逻辑

    不同部门实现不同的demo和业务逻辑

2.5、平台层架构实现

Image
Image

2.5.1、模块设计

Image

完成平台层分层架构之后,首先进行平台层模块设计,以业务独立、功能解耦为原则,分为三部门共用的base模块和各业务线实现模块

base:
- sdkBridge - 通用接口模块
- unityLibrary - unity工程导出产物
- unitySdkLibrary - unity接口桥接实现层,包括Android接口回调通知到unity实现

sq_wy为例:
- sdkUnityDemo - unitydemo的入口类
- sdkImpl - 业务线对接现有业务SDK的具体实现类。

Image

2.5.2、平台接口设计

Image

模块设计完成,进一步需要设计先设计业务接口。和引擎层业务接口类似,在平台层将接口分为业务相关接口和生命周期接口。

业务相关接口,方法名和unity调用方法名必须保持一致。

Image
Image

回调接口调用方法名和unity层实现的方法名也必须保持一致:

Image

2.5.3、平台回调unity

Image

在平台层实现具体功能后,需要将功能的结果回调给unity。Android中调用unity方法,最终是调用unityLibrary中的 UnityPlayer.UnitySendMessage() 方法来实现:

Image
Image
Image

2.5.4、平台接口实现

Image

在SQWySDKImpl具体实现类中,功能接口和回调串联起来。

以init为例,init()接口是核心实现的业务接口,在完成初始化后,调用callback的onInitSuccess,在onInitSuccess完成对unity接口的调用,最终完成了整个接口调用流程。

Image

最终,引擎层触发接口call调用,平台层完成接口功能实现,并callback回调给引擎层,完整流程如下:

Image
03

MaxSDK在Unity中的使用

Image
Image

开发完成,一切就绪后,如何提供给研发使用呢?这涉及导出平台工程、导出unitypackage包、输出对外SDK文档给到研发对接。

1、导出目标平台工程,以安卓为例:
Image
Image
Image

注意:每次导出unity的安卓工程,都需要更新 `assets` 和 `jniLibs` 目录的资源

2、导出 unitypackage文件,提供给研发导入使用

unitypackage文件是Unity引擎中用于打包和共享项目资源和代码的文件格式。通过使用unitypackage文件,可以方便地共享和传递项目,并快速导入和使用所需的资源和代码。

导出方式很简单,选择菜单栏的 "Assets",然后选择 "Export Package",按需选择文件即可。在MaxSDK项目,选择需要导出的接口.cs文件,选中【包括依赖项】,就可以得到一个可供导入的 .unitypackage 文件。

Image
Image
3、研发导入unitypackage文件

在Unity编辑器中,选择菜单栏的 "Assets",然后选择"Import Package",再选择"Custom Package",选择 .unitypackage  文件即可完成导入。

Image
04

MaxSDK的扩展探索

Image
Image

前文引擎层以unity为例,平台层以安卓层为例,讲解了MaxSDK架构思路和实现过程,介绍了接口的完整调用过程。那MaxSDK是否支持更多引擎(例如cocos、UE),支持更多平台(例如鸿蒙、小游戏)呢?
答案是肯定的,MaxSDK从架构上支持扩展:

  • 借助Unity团结版,可以实现鸿蒙、微信小游戏的快速适配。

    基于可扩展的架构,可灵活实现引擎层的适配。

Image
05

总结

Image
Image

本文介绍了MaxSDK的背景和收益,讲解了MaxSDK的架构设计思路以及实现,包括unity引擎层使用c#实现MaxSDK的思考,以及平台层Android工程兼容多业务线的实现。最后介绍如何在unity中导出/更新安卓工程(其他平台类似),一起探索MaxSDK扩展鸿蒙、其他引擎的可行性。最终实现研发对接SDK降本增效的目的。

Image