幻蓝博客 - 孤月蓝风

奇思妙想

Think & Design

伟人之所以伟大,是因为他与别人共处逆境时,别人失去了信心,他却下决心实现自己的目标。

勇于突破

Courage & Action

每个人的一生都有许多梦想,但如果其中一个不断搅扰着你,剩下的就仅仅是行动了。

三省吾身

Reflection & Correct

人若软弱就是自己最大的敌人;人若勇敢就是自己最好的朋友。勇于认识自己的错误,并改正之。

CodingVue中全局使用Lodash的Debounce防抖函数、Throttle节流函数及其他

之前的项目中,虽然从没有用过Lodash,但也是久仰大名。现在的项目中,希望能够进行实时查询,所以Debounce防抖和Throttle节流函数就必不可少了。

之前并没有用过防抖和节流,但是之前看过掘金的一些文章,而且由于之前有游戏开发经验,所以对于这种逻辑还是非常熟悉的,只是JS中的我们是使用闭包来解决这个问题。代码其实很简单,这里不再贴出代码。这篇文章我们主要还是解决Vue中使用Lodash的问题。

如果是JS代码的话,解决的方法比较多,但是TS的话,会有一些定义问题。

一、使用VuePlugin方式

Vue的插件写起来是很容易,使用Lodash的大部分函数也很方便,但是有一个问题,就是无法使用_.debounce_.throttle函数,因为我们使用的时候使用的是this._这种方式,但是在methods中,我们无法直接使用this的方法来进行赋值,代码如下:

// Vue插件代码
import _Vue from "vue";
import * as lodash from "lodash";
function install(Vue: typeof _Vue, options?: any): void {
    Vue._ = lodash;
    Vue.prototype._ = lodash;
}
export default install;

// 在created中定义的方法,可以用作event,JS中可直接使用,TS中也可以使用,但由于无法对this进行定义,所以会有波浪线
created() {
    this.test = this._.debounce(function(){},1000);
}

// 在这种methods方式下可以用使用lodash的一些方法
methods: {
    test() {
        let _d = this._.debounce(function(){},1000);
    }
}
// global.d.ts
import Vue from "vue";
import * as lodash from "lodash";

declare module "vue/types/vue" {
    // Vue.prototype._
    interface Vue {
        _: lodash.LoDashStatic;
    }
    // Vue._
    interface VueConstructor<V extends Vue = Vue> {
        _: typeof lodash.LoDashStatic;
    }
}

二、使用WebpackProvidePlugin来进行

ProvidePluginWebpack用来自动加载模块的方法,之所以会研究全局使用方式的问题,就是因为我懒得每个vue组件中都import lodash

// webpack的配置,在VUE-CLI 3中,我们写在vue.config.js中
plugins: [
    new webpack.ProvidePlugin({
        _: "lodash"
    })
]
// vue组件中
methods: {
    delay_search: _.debounce(function(this: any) {
        this.other_function();
    }, 500),
}
// global.d.ts
import * as lodash from "lodash";
declare global {
    const _: lodash.LoDashStatic;
}

CodingJavascript中的数字类型转换:parseInt和ToNumber

前言

今天面试一位前端的时候,问到他一个问题:一个数字和一个布尔型用==比较时,类型会如何转换。开始他答的:数字会转换成布尔型,之后改成了:布尔会转换成数字型。 又引出了另一个问题,字符串和布尔进行比较,类型如何转换?

问题

我记得之前的记忆中,一个字符串转换成数字,是从第一位开始检查的,将可以转换成数字的位置转完,舍弃之后无法转换的。例如:

123abc -> 123

abc123 -> NaN

于是我很自信的认为,console.log('1true' == true);的结果是true,但我运行代码后,发现竟然是false。于是我整个人就懵逼了,难道1true的结果不是1

解决

查看文档后,发现所有数字的隐式类型转换都是用的ToNumber,而我之前记得从第一位开始转的是parseInt

当使用Number创建一个数字时,会对参数进行ToNumber转换,于是我使用console.log(Number('1true'));查看输出,结果是NaN,而parseInt1,而console.log(parseInt('1true') == true);结果也是true

查看parseInt文档后,描述是:如果第一个字符不能被转换成数字,parseInt返回NaN

而Number文档是:如果参数无法被转换为数字,则返回 NaN

所以,这就是我今天说错一个问题后的提升。

游戏微信小游戏开发笔记:基本了解及引擎选择

做任何开发,最重要的起点就是:通读官方文档!

该系列文章是开发微信小游戏时遇到问题的总结,是笔记,非教程。如有BUG,请指正。

Adpter

小游戏的运行环境在 iOS 上是 JavaScriptCore,在 Android 上是 V8,都是没有 BOM 和 DOM 的运行环境,没有全局的 document 和 window 对象。因此当你希望使用 DOM API 来创建 Canvas 和 Image 等元素的时候,会引发错误。

在微信小游戏里面,如果使用第三方库,Adapter是一个非常重要的东西。微信在文档中也有提供源码,可以自行进行修改。尝试下来,基本每款引擎都需要进行适配,包括尝试的一个物理引擎Ammo.js,里面使用了print,所以也需要适配。目前唯一遇到的不需要适配的库就是Oimo.js这个物理引擎了。

适配工作也比较简单,很多时候都是一些不需要写太多代码的修改。例如 XMLHttpRequestaddEventListener 方法,只需要加入如下代码就可以了:

{
    key: 'addEventListener',
    value: function addEventListener(name, callback) {
        this['on' + name] = callback;
    }
},
{
    key: 'removeEventListener',
    value: function removeEventListener(name) {
        this['on' + name] = null;
    }
}

全局对象 GameGlobal

在微信小游戏里,因为没有BOMDOM,所以是没有window的,但Adpater中模拟了window。微信自己提供了一个 GameGlobal 全局对象,需要放入全局的东西都可以加到这个里面。

其他不同

微信实现了自己的音频播放、文件系统、Worker。

引擎选择

2D

做2D游戏其实没有太大的问题,白鹭EgretLayaAir(LayaBox)、Cocos都可以,我尝试过Phaser3版本,发现需要改的地方有点多,就没再尝试了。国内2D引擎已经对微信小游戏做了适配,都有自己的Adapter,基本不会遇到太多问题。

在开放域中使用LayaAir制作UI时,发现了一个坑,发布微信小游戏时,LayaAir不会编译代码,而是直接使用已编译的进行生成项目代码,这个问题之前一直没有注意过。

3D,最终选择:Three.js

3D游戏引擎(框架)也有很多,除国内的LayaAirEgret,国外的有 Three.jsBabylon.jsBabylon.js可以用Unity导出场景,很是方便,物理引擎也很方便的可以使用,在电脑和安卓下调试通过,但iOS下似乎shader有问题,所以没有继续研究。

Three.js是现在最火的3D框架之一,跳一跳也是使用这款引擎开发的,而且搭配物理引擎也比较方便,虽然不像Babylon.js导出就能用,但代码量也不多。

之前3D项目使用LayaAir开发,但后来发现竟然没有内置3D物理引擎,而且与其他物理引擎结合困难,就更换了Three.js

3D物理引擎,最终选择:Oimo.js

3D物理引擎有尝试三款,Ammo.jsCannon.jsOimo.js 。最开始使用 Ammo.js 进行测试,使用的 Physijs 插件,代码写完后能够正常运行,但2个box的物理就已经卡到15帧了。

由于 Cannon.js 2年没更新,而且没有找到可用文档,所以也没使用。

Oimo.js 是在下载 Babylon.js时看到的,使用后发现效果很不错,100个盒子没有使用worker也能保持60帧,而且不需要修改任何代码直接可用,所以最终选择了这款物理引擎。

其他

做微信小游戏开发很重要的一点是,要知道哪些东西在微信小游戏里面可用,哪些不可用。

游戏Unity与iOS交互(四):使用XUPorter在构建XCode工程时自动添加Framework、编辑Info.plist、OC代码等

开发环境:XCode 9.2 , Unity 2017.1.3,XUPorter 2018.02.24

XUPorter是一个可以帮助你在导出XCode工程时,自动添加Framework、自动编辑Info.plist、编辑OC代码的工具,建议先阅读XUPorter作者对该项目的简介。这是一个开源项目,目前最后一次修改是在2016年4月6日,英文文档已更新,但是雨松MOMO的文章和作者的介绍都已经过时,有些方法已被修改,这篇文章主要介绍该项目目前版本该如何使用。

该项目Github地址:https://github.com/onevcat/XUPorter

安装(放入工程中)

首先下载该项目,并放到Unity工程的Editor文件夹中,安装完成了

目录结构

XUPorter项目结构
XUPorter项目结构

projmods使用

.projmods文件你可以理解为一个SDK的配置的文件,我们每需要接一个SDK,就可以创建一个该类型的文件,当然你也可以全部放在一个文件中,但推荐分开存放,防止混淆。

我们先看之前接入微信支付时的配置文件,该配置文件内容是JSON格式:

微信支付projmods配置文件
微信支付projmods配置文件

group:组名称,可以简单理解为该配置的名称

libs:需要加入XCode工程的dylib文件,这里我全部放入了frameworks里,该字段并没有放入内容

frameworks:需要加入工程的各种Framework,这里是系统的Framework,并不是第三方Framework

headerpaths:需要加入搜索的头文件路径

files:需要加入到工程中的文件名,例如第三方Framework是一个当前配置文件所在目录的相对地址,不支持通配符

folders:需要加入到工程中的文件夹名,是一个当前配置文件所在目录的相对地址,不支持通配符

excludes:排除的文件类型,支持正则表达式

compiler_flags:需要加入的编译标签

linker_flags:需要加入的链接标签,微信SDK需要加入-Objc-all_load两个,其他SDK也会有类似要求

plist:需要修改的Info.plist的内容,这个功能是之后的贡献者提交的,所以在中文介绍中并没有提到,在Github上的版本中,只支持urltype的操作。因为微信支付SDK需要添加LSApplicationQueriesSchemes,所以我通过修改代码,支持了这个字段,但因为并不是通用解决办法,所以没有提交 pull request

当了解了上面的这些内容后,接入SDK也就非常简单了,每次 不可Append 构建后不再需要修改这些内容了。因为可以直接扩展UnityAppController,所以我们不再需要通过XUPorter来修改UnityAppController.mm的代码。

另外,修改plist文件的方法也不存在了,如果需要的话你可能需要自行修改代码来实现。上面修改代码支持修改plist的LSApplicationQueriesSchemes的代码为XCPlist.cs文件中AddPlistItems函数,代码如下:

修改XCPlist的AddPlistItems函数
修改XCPlist的AddPlistItems函数

某些情况下,我们可能需要修改其他文件,例如接入极光推送时,需要修改 Classes/Preprocessor.h 里的 UNITY_USES_REMOTE_NOTIFICATIONS 0来使程序支持极光推送,我们可以通过如下代码来修改:

修改Preprocessor.h文件
修改Preprocessor.h文件

以上就是目前使用到的XUPorter功能,在我接入微信支付SDK和极光推送时,通过上面介绍到的功能已经可以完成全部的修改了,如果有遗漏,欢迎反馈。

游戏Unity与iOS交互(三):扩展UnityAppController来接入SDK

开发环境:XCode 9.2 , Unity 2017.1.3

在接入很多SDK时,都会被要求在 UnityAppController 中加入代码,而如果每次都在构建 XCode 工程后再修改的话,会是一件很麻烦的事情。Unity给我们提供了扩展UnityAppController的方法,我们可以通过继承UnityAppController来实现自己的AppController,并放入Unity工程的 Assets / Plugins / iOS 文件夹中,这样每次构建时,都会被自动加入XCode工程中。

首先我们来写一个AppController,我推荐在XCode中编写代码,完成后放入Unity工程中。因为在XCode中有代码提示、代码检查,可以避免问题无法被发现。

在构建后的XCode工程中新建一个 MyAppController.m 文件,这里可以不需要新建 .h 头文件,直接在 .m 中定义:

@interface MyAppController : UnityAppController<WXApiDelegate>
@end

我们只需要继承 UnityAppController 即可, <WXApiDelegate> 是我们接入微信支付SDK时添加的,如果某些SDK要求加入协议,都可以在这里加入,例如 <UIApplicationDelegate,WXApiDelegate> 使用逗号分割。

之后我们就可以加入SDK要求的代码,一般的SDK都会要求在 didFinishLaunchingWithOptions 中(这只是OC中方法签名的一部分,这并不是一个方法,我们只是通过这个字符串来查找方法位置,如果不理解建议学习OC基础教程)初始化SDK,我们可以直接重写 UnityAppController 中定义过的这个方法,加入SDK的代码,例如:

扩展并重写UnityAppController的方法
扩展并重写UnityAppController的方法

OC中的成员方法是需要放在 @implementation MyAppController 中的,很多SDK要求的代码,例如回调函数等,都可以放在这里。

将代码全部编写完成后,我们需要在构建的XCode工程中,默认使用此文件为程序入口,Unity也给我们提供了相应的方法:

IMPL_APP_CONTROLLER_SUBCLASS(MyAppController)

我们可以直接使用这个宏来完成构建时自动修改入口,参数为你编写的类,注意这里并不是字符串。

一切完成之后,你就可以将文件放入Unity工程的 Assets / Plugins / iOS 文件夹中了,构建后会自动生成到XCode工程中并设置为入口,这样就避免了我们在每次构建后手动修改UnityAppController中的代码,接多个SDK时更加方便。

同样,如果你对这篇文章中的部分内容不懂的话,建议阅读基本的OC开发教程。

游戏Unity与iOS交互(二):接入微信支付SDK

开发环境:XCode 9.2 , Unity 2017.1.3

在开发项目时需要接入的SDK中,微信的SDK是最……坑……的,微信的SDK包括微信分享、微信登录、微信支付。接入的方式是一样的,坑的点在于,微信开放平台官方文档的极其不完善。其他SDK例如ShareSDK或极光推送等,都有专门的UnityPackage包,而且有文档教程,虽然也有些坑的地方,但还是可以很容易的接入。但微信的SDK只有原生接入方式,而且文档中的XCode版本是很古老的,这个也不是大问题,大问题是他的示例代码也是很古老且不完整的。

这篇文章就介绍如何接入微信支付SDK(微信分享SDK方式相同),你可以在微信开放平台的资源中心中找到SDK的文档及下载,阅读“iOS接入指南”来开始SDK的接入,从手动接入部分开始。

你可以将SDK的文件放入到Unity工程的 Assets / Plugins / iOS 文件夹中,到构建 XCode 工程后,会直接存放于 XCode 工程的 Libraries 文件夹中。或者,你可以构建 XCode 工程后,再将 SDK 文件导入,直接将三个文件 libWeChatSDK.a,WXApi.h,WXApiObject.h 拖入到 XCode 工程中,并勾选 Copy items if needed :

将SDK加入到XCode工程中
将SDK加入到XCode工程中

之后,我们进行将需要的 Frameworks 手动加入(自动加入请阅读后续的XUPorter教程)等步骤,这些步骤都可以按照官方文档中的3.2.3到3.2.6直接进行,为了防止官方文档更新后导致步骤对不上号,我这里简单列出这些步骤:

3.2.3:将Framework加入到工程中,在 XCode 工程的 Build Phases -> Link Binary With Libraries 中点击下面的 + 号添加:

添加Framework到XCode工程中
添加Framework到XCode工程中

3.2.4:在你的工程文件中选择Build Setting,在”Other Linker Flags”中加入”-Objc -all_load”,在Search Paths中添加 libWeChatSDK.a ,WXApi.h,WXApiObject.h

3.2.5:在XCode中,选择你的工程设置项,选中 TARGETS 一栏,在 info 标签栏的 URL type 添加 URL scheme 为你所注册的应用程序id

3.2.6:在XCode中,选择你的工程设置项,选中 TARGETS 一栏,在 info 标签栏的 LSApplicationQueriesSchemes 添加 weixin

接下来是3.2.7:在你需要使 用微信终端API的文件中import WXApi.h 头文件,并增加 WXApiDelegate 协议,这个操作我们在 UnityAppController.h 中完成(我们同样可以扩展UnityAppController.mm来进行这个操作,避免每次打包后重新修改UnityAppController,参考后续的扩展UnityAppController文章),查看下图中画线的地方:

UnityAppController.h中加入WXApiDelegate协议
UnityAppController.h中加入WXApiDelegate协议

接下来的在代码中使用工具包这一步的4.1我们在 UnityAppController.mm 中完成,首先找到文件中 didFinishLaunchingWithOptions 函数的位置,加入 [WXApi registerApp:@"wxa72a15850b99c773"];代码。

4.2中重写AppDelegate的handleOpenURL和openURL方法,Unity生成的代码中并不包含这两个函数,按文档中的代码直接增加即可。

4.3中实现WXApiDelegate协议的两个方法 onReq 和 sendReq 。这两个方法可以参考官方支付demo的代码,这里提供最简单的实现:

实现WXApiDelegate协议的两个方法 onReq 和 sendReq
实现WXApiDelegate协议的两个方法 onReq 和 sendReq

至此,XCode中 微信支付SDK 的接入就全部完成了,而且这里我们已经在 XCode 中使用 UnitySendMessage 向 Unity 端发送支付结果,Unity中接收后根据结果进行处理即可。但我们还没有完成调用支付的功能,我们新建一个 .h 和 .m 文件,文件名随意,我这里使用 NativeBridge 来命名,在里面实现原生的调用微信支付的代码。这两个文件我们可以在构建后 XCode 中引入,也可以直接存放于 Unity 项目的 Assets / Plugins / iOS 中,构建时会自动存放于 XCode 工程的 Libraries 文件夹中。

在 NativeBridge.h 中写入代码:

NativeBridge.h中的代码
NativeBridge.h中的代码

在 NativeBridge.m 中,写入代码:

NativeBridge.m中的代码
NativeBridge.m中的代码

_WXPay是我们定义的调用微信支付的函数,参数全部使用 char *name 来传入,使用 [NSString stringWithUTF8String:name] 来转换为 NSString ,这里需要注意两点

  1. request.openID 必须赋值,这一条在官方文档中没有说明,是在百度其他教程时发现的,android端也是如此。另外iOS端没有appid,只有android端有。
  2. timeStamp 必须是整形且为10位,精度是秒,上面代码中有转换方法。

如果调用时提示签名错误,那么很有可能是上面2点造成的。

调用的函数已经写好了,现在我们需要在 Unity 里面进行调用了,先引入原生代码中定义的调用函数:

引入原生代码中的调用函数
引入原生代码中的调用函数

之后在需要的地方传入参数并进行调用,参数应由服务器返回:

Unity中调起微信支付
Unity中调起微信支付

在微信支付处理完成或取消之后,会调用之前在 UnityAppController.mm 中定义的回调函数 OnResp ,再使用 UnitySendMessage 向 Unity 发送支付结果,UnitySendMessage 的使用方式可以参考Unity官方文档。

到这里,所有的微信支付的步骤就全部完成了,如有遗漏,欢迎反馈。

如果在 XCode 中添加的 OC 代码看不懂的话,强烈建议先阅读 OC 的基本教程。

游戏Unity与iOS交互(一):构建XCode工程

开发环境:XCode 9.2 , Unity 2017.1.3

在使用Unity开发游戏时,我们经常会被要求接入各种SDK,例如微信支付、微信分享、推送等。而大部分Unity开发人员并没有iOS相关开发经验,所以接原生SDK一直以来都是一件很头疼的事情,不过当我查阅各种文档,看了各种教程之后,发现接iOS的SDK还是比较简单的一件事情。

所以这个系列文章将会介绍我接入SDK过程中学到的知识、要点及过程。

构建过程

在Unity官方文档中有专门的iOS平台开发文档,阅读这些文档是非常有帮助的。这一篇我将先介绍iOS平台的构建及生成的XCode工程结构。这两点都是Unity官方文档中的内容。

在点击Build构建iOS工程时,如果不是首次构建,则会弹出一个警告:

iOS构建警告
iOS构建警告

当我们不勾选Enable replacesshi时,Replace按钮是不可用的。官方文档中也有介绍ReplaceAppend的区别

  • replace – 所有目标文件夹的文件将被移除,并且生成新的内容
  • append – “Data”,”Libraries”和项目根目录将被清理并放入新生成的文件。XCode项目文件将会根据Unity中的更改被更新。XCode工程中的”Classes”文件夹是存放本机代码的安全场所,但建议定期进行备份。追加模式仅支持使用相同Unity iOS版本生成的现有XCode项目。

所以在我们构建XCode工程后,我们所有的Info.plist、Build Settings等的修改,在Append模式下都不会被覆盖,在后续接SDK时,也会少很多麻烦。但如果你使用了不同版本的Unity来构建项目,那么构建时将只能Replace,之后所有的修改需要重新再来一遍,如果SDK多或者修改操作多的话,这会是一件很麻烦的事情。不过,我们还有别的办法来解决这个麻烦:使用XUPorter。XUPorter的使用会在后面的文章中介绍。

工程结构

构建后的XCode工程应该是下面这样的:

Unity构建后的XCode工程结构
Unity构建后的XCode工程结构

对我们来讲,”Classes”、”Libraries”文件夹和Info.plist文件是最常用到的。其余目录及文件可以参考Unity官方文档中XCode项目结构的介绍

在”Classes”文件夹中,有一个名为UnityAppController.mm的文件,这个文件是main.mm中设置的程序的入口。通常接入微信SDK或极光推送时,都需要在这个文件中加入代码,初始化SDK等。而官方文档中指出,我们可以扩展这个类,在下一篇文档中我会介绍扩展UnityAppController.mm的方法。

“Libraries”文件夹中,存放着我们在Unity中放入的原生代码,当在Unity中你将原生代码放入Plugins/iOS文件夹中时,这些代码会被拷贝到”Libraries”文件夹中。而在Plugins/iOS中放入原生代码文件,也是Unity官方文档中介绍的iOS插件使用方法。

Info.plist中存放的项目配置,接入SDK时也会需要修改的,可以按照SDK的文档来修改此文件,也可以阅读之后的XUPorter的使用教程来在Unity的代码中修改此文件。

通过这篇文章我们可以看到,Unity官方文档中有各种你需要的知识,不过遗憾的是文档没有中文化,而且各种知识在不同的文章里,很容易忽略,这个专题的目的,也就是为了帮助大家使用这些只是来更加容易的接入SDK。

游戏Unity与Android交互(三):Android App调用Unity打包的AAR文件

开发环境:Android Studio 2.3.3 , Unity 2017.1.2

我们打开在第一篇文章中建立的 Android App 项目,新建一个模块,并导入第二篇文章中打包的 AAR 文件:File > New Module > Import .JAR/.ARR Package ,选择第二篇文章中打包的 testunity-debug.aar 文件。

之后右键点击 Project 面板中的 app 文件夹,选择 Open Module Settings 的 Dependencies 选项卡,添加 :testunity-debug 模块,如果会发生错误,我们先确保修改 build.gradle (Module: app) 文件中的 minSdkVersion 为与 Unity 中一致的 API 版本。

添加Unity的AAR后报错
添加Unity的AAR后报错

修改minSdkVersion与Unity中一致
修改minSdkVersion与Unity中一致

API 版本修改完成后,这里还有一个 Icon 和 Theme 相关的报错:

Icon和Theme的报错
Icon和Theme的报错

我们打开 App 的 AndroidManifest.xml 文件,告诉 merge 合并程序使用哪一个 Icon 和 Theme ,分别在  manifest 标签和 application 标签下添加代码:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.test.androidapp">
    <application
        tools:replace="android:icon,android:theme"
        ...
        android:theme="@style/AppTheme">
        ...
    </application>
</manifest>

manifest和application标签下添加代码
manifest和application标签下添加代码

保存,点击: Sync Project with Gradle Files ,Gradle 会对当前程序进行编译,这个时候就会成功编译了。

接下来我们像第一篇文章一样,开始调用 Unity 的 Activity ,我们在 MainActicity 中先引用导出的 AAR 包的 Unity 的 Activity:

import com.test.testunity.UnityPlayerActivity;

在 Button 的 click 事件中调用显示 Unity 的 Activity :

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this, UnityPlayerActivity.class);
        startActivity(intent);
    }
});

到这里,我们就完成了在 Android App 中调用并显示 Unity 程序的所有步骤。

注意:我们这里并没有在 Unity 中添加返回键事件,所以你点击手机的返回键时不会有任何反应,如果你使用 Application.Quit(); 你会发现会把整个 Android App 也关掉,这一部分的处理可以参考网上的其他文章。

如果在文中发现了 BUG ,或发现某些地方写的不够清楚,欢迎在评论中告诉我,我会完善。

项目Git地址:https://gitee.com/trlanfeng/UnityAndroid/

游戏Unity与Android交互(二):Unity项目打包为AAR供Android App调用

开发环境:Android Studio 2.3.3 , Unity 2017.1.2

这一篇我们讲如何将 Unity 程序打包为 AAR 文件,使 Android App 可以调用。

在 Unity 5.x 时, Unity 的 Build System 中就加入了使用 Gradle 方式输出项目。

Unity中使用Gradle输出
Unity中使用Gradle输出

当使用 Gradle 方式时,就只有 Export 按钮了,而不是 Build 和 Build And Run 了。

导出之前,要记得更改 Bundle Identifier , Export 项目之后,使用 Android Studio 打开这个项目,File > New > Import project > 选择你刚才导出的目录,并点击 OK 。之后会提示你 Gradle 尚未配置,是否自动配置,点击 OK 会自动配置,点击 Cancel 需要你之后手动配置。

Unity导出Gradle打开后的提示
Unity导出Gradle打开后的提示

我们这里使用自动配置,之后 Android Studio 会运行 Gradle 进行编译,这里可能会报错:

Gradle自动配置时的报错信息
Gradle自动配置时的报错信息

我们打开 Project 面板,双击打开 Gradle Scripts 中的 build.gradle ,修改 Gradle 插件版本到 2.3.3 ,和 Android Studio 版本保持一致。

修改Gradle插件版本
修改Gradle插件版本

修改之后,点击工具栏中的 Sync Project with Gradle Files , Gradle 会重新编译,并且编译通过,不会再遇到问题。

Sync Project with Gradle Files
Sync Project with Gradle Files

打开 Project 面板中的 manifests 文件夹中的 AndroidManifest.xml 文件,注释掉 intent-filter 标签:

注释intent-filter
注释intent-filter

现在,我们继续修改 build.gradle 文件中的内容:

修改Gradle编译方式
修改Gradle编译方式

将编译方式改为 com.android.library ,并注释掉 applicationId ,项目才能被打包成 AAR 。点击菜单栏 Build > Make Project ,Android Studio 会将项目打包成 AAR 文件,并放入 build/outputs/aar 文件夹中。

至此,我们就完成了 Unity 工程打包为 AAR 的所有步骤。

注意,这里 Android Studio 和 Gradle 插件必须是 2.3.3 版本,最新的 Android Studio 3.0.1 使用时会遇到错误,包括导入 AAR 时会提示 AAR 文件过大等错误。由于我不是 Android 开发人员,不知如何解决。

如果在文中发现了 BUG ,或发现某些地方写的不够清楚,欢迎在评论中告诉我,我会完善。

项目Git地址:https://gitee.com/trlanfeng/UnityAndroid/

游戏Unity与Android交互(一):在Android App中加载Android Library中的Activity

开发环境:Android Studio 2.3.3

这篇文章是作为Unity与Android交互系列的第一篇,只讲解如何在Android App中加载一个Android Library中的Activity,并传递参数。在这之前,你需要了解Android Application开发基础。网上有很多入门的Android开发教程。

我们先新建一个Android App工程,命名为AndroidApp,使用Empty Activity作为主Activity。

在菜单栏中点击 File > New > New Module ,在弹出的对话框中选择 Android Library,并命名为AndroidLib,在 Android Studio 左侧的 Project 面板中,右键点击 androidlib ,并选择 New > Activity > Empty Activity ,将新建的 Activity 命名为 LibActivity 以作区分。

我们将 LibActivity 中的 TextView 的 id 改为 lib_textview , text 改为“这是Android Library的Activity”并添加一个 id 为 lib_button 的按钮, text 改为“关闭并传回参数:456”。

在 Project 面板中,右键点击 app ,打开 Open Module Settings ,并在 Dependencies 选项卡中,点击右边的加号,选择 :androidlib ,之后点击 OK 。这时便可以从 App 中调用 Library 中的 Activity 了。

在 MainActivity 添加按钮的 click 事件,用于打开 Android Library 中的 Activity ,在 OnCreate 中加入如下代码:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this,com.test.androidlib.LibActivity.class);
        String message = "123";
        intent.putExtra("com.test.androidapp.message",message);
        startActivityForResult(intent,1);
    }
});

上面的代码用于启动 Android Library 中的 LibActivity 并传递了参数 123 ,参数名为 com.test.androidapp.message (官方推荐参数名前加包名以示区分),值为 123 。

我们需要在 MainActivity 中接收由 LibActivity 返回的数据,在类中加入如下代码:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    TextView main_textview = (TextView) findViewById(R.id.main_textview);
    main_textview.setText(data.getStringExtra("com.test.androidlib.message"));
}

之后在 LibActivity 中接收传递的参数并显示在 TextView 中,并为关闭按钮添加事件并回传参数 456 :

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_lib);

    Intent intent = getIntent();
    String message = intent.getStringExtra("com.test.androidapp.message");
    TextView lib_textview = (TextView) findViewById(R.id.lib_textview);
    lib_textview.setText(message);

    Button button = (Button) findViewById(R.id.lib_button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent();
            String message = "456";
            intent.putExtra("com.test.androidlib.message",message);
            setResult(1,intent);
            finish();
        }
    });
}

至此我们就完成在 Android App 中加载一个 Android Library 中的 Activity ,并进行参数传递。目前我们是在 App 项目中新建了一个 Android Library ,我们也可以将 Android Library 导出为 aar 包,并通过 New Module > Import .JAR/.AAR Package 来加入到项目中。

如果在文中发现了 BUG ,或发现某些地方写的不够清楚,欢迎在评论中告诉我,我会完善。

项目Git地址:https://gitee.com/trlanfeng/UnityAndroid/