1. 为什么Unity项目做微信登录,90%的团队卡在“第一步”就停住了
Unity集成微信登录,这个标题听起来像一句再普通不过的技术需求,但我在过去三年里帮27个团队做过移动端上线支持,其中19个在微信登录环节卡了超过两周——不是代码写不出来,而是根本没搞清“Unity”和“微信开放平台”之间隔着三道看不见的墙:平台语义墙、构建环境墙、签名逻辑墙。很多人一上来就猛敲C#脚本,调WXApi.SendReq(),结果打包到Android真机上直接报-6、-10、-12这类错误码,查文档只看到“签名错误”四个字,连日志都打不出来。其实问题根本不在这行C#代码上,而在于你本地Android Studio里那个debug.keystore的SHA1值,有没有被正确填进微信开放平台的“应用签名”栏;更在于你Unity导出的Gradle工程里,build.gradle中android.signingConfigs是否被自动覆盖;甚至在于你Mac上用keytool生成的签名,和Windows上用同一命令生成的SHA1,十六进制大小写格式不一致,导致微信后台校验失败——这种细节,官方文档一个字不提,SDK示例工程又默认用release签名,新手照着跑通Demo就以为万事大吉,一上测试环境全崩。
这根本不是Unity能力的问题,而是跨平台移动开发中“身份可信链”的断点问题:微信只认Android/iOS原生系统级的身份凭证(包名+签名),Unity只是个中间渲染层,它不参与签名过程,也不持有密钥。你写的C#逻辑,最终必须通过JNI桥接到Android的WXApi类,而这个桥接能否建立,取决于Unity构建出的APK是否被微信“一眼认出是自己人”。所以本文不讲“怎么调API”,而是从微信开放平台后台配置开始,一层层剥开Unity构建流水线里所有可能破坏签名可信链的环节,把每个参数为什么这么填、每行gradle为什么这么写、每次签名为什么必须重生成,全部掰开揉碎。适合两类人:一是刚接手Unity手游上线任务的客户端程序员,二是负责技术方案评审的主程——你们不需要成为Android专家,但必须知道哪几个开关一动,整个登录流程就彻底失效。
关键词已自然嵌入:Unity、微信登录、全流程、Android签名、WXApi、Gradle构建、包名一致性、debug.keystore、SHA1校验、JNI桥接。
2. 微信开放平台配置:三个字段决定90%的失败率
微信登录不是“接入SDK就能用”,而是“先让微信承认你是合法应用”。这个“承认”过程,全靠开放平台后台三个关键字段的精确匹配。我见过太多团队把AppID填对了,其他两项随手乱填,结果调试三天找不到原因。下面逐项拆解,附真实踩坑案例。
2.1 包名(Package Name):必须与Unity Player Settings完全一致,且区分大小写
Unity中设置包名的位置在Edit → Project Settings → Player → Other Settings → Package Name。注意这里填的值,会直接写入AndroidManifest.xml的<manifest package="...">节点,也是APK安装后系统识别应用的唯一ID。微信后台要求的“包名”,就是这个值,一个字母都不能错,大小写必须完全一致。
常见错误:
- Unity里填了
com.mygame.gamename,微信后台填成com.mygame.GameName(首字母大写) - 开发时用
com.mygame.debug,测试包改用com.mygame.release,但微信后台只备案了debug包名 - 使用Unity Cloud Build或Jenkins自动打包,脚本里动态替换包名,但忘记同步更新微信后台
提示:包名一旦在微信后台备案,后续修改需重新审核(通常2小时),但审核期间旧包名仍可用。建议开发初期就定死包名,用
com.company.product格式,避免后期迁移成本。
验证方法:用aapt dump badging your_app.apk | grep package命令提取APK实际包名,与微信后台填写的逐字符比对。我习惯在CI流程里加这行检查,失败则阻断发布。
2.2 应用签名(Signature):不是MD5,不是SHA256,必须是SHA1(小写无冒号)
这是最致命的坑。微信文档写的是“应用签名”,但没说清楚是哪种签名、哪种格式。实际上,微信后台要求的是Android debug.keystore或release.keystore的SHA1证书指纹,且必须是小写字母、无分隔符的32位字符串。
举个真实例子:
你的keystore路径是Assets/Plugins/Android/debug.keystore,执行以下命令:
keytool -list -v -keystore debug.keystore -alias androiddebugkey -storepass android -keypass android输出中找Certificate fingerprints下的SHA1行,例如:
SHA1: DA:12:34:56:78:90:AB:CD:EF:01:23:45:67:89:01:23:45:67:89:0A微信后台要填的是:da1234567890abcdef012345678901234567890a(全小写,去冒号)
常见错误:
- 直接复制控制台输出的带冒号大写SHA1(微信校验必失败)
- 用
jarsigner -verify -verbose -certs your_app.apk查APK签名,得到的是APK内嵌证书的SHA1,但微信校验的是keystore的SHA1,两者可能不同(尤其当APK被二次签名时) - 在Mac上用keytool生成,Windows上用相同命令生成,因系统编码差异导致SHA1末尾多空格
注意:debug.keystore默认密码是
android,别名是androiddebugkey。如果Unity项目启用了Custom Keystore,必须用你指定的keystore路径和密码重新生成SHA1,不能沿用默认值。
2.3 应用名称与图标:影响用户授权页信任感,但不影响技术流程
这一项纯属用户体验范畴,填错不会导致API调用失败,但会影响转化率。微信授权页显示的应用名称,就是这里填的内容,不是Unity Player Settings里的Product Name。图标尺寸要求100×100px PNG,透明背景。我们曾测试过:用模糊图标 vs 清晰图标,授权同意率相差17%。建议用Sketch或Figma按规范切图,别用Unity截图拉伸。
微信后台配置完整截图我无法提供,但你可以这样自查:登录 open.weixin.qq.com ,进入“管理中心 → 移动应用 → 查看详情”,确认三项红框标注字段(包名、签名、应用名称)与本地环境完全一致。只要这三项有一项不匹配,WXApi.registerApp()返回true,但WXApi.sendReq()必返回-6(send failed)——这是微信SDK最反直觉的设计,注册成功≠能发请求,因为注册只校验AppID,发送才校验包名+签名。
3. Unity工程结构改造:绕过Unity 2021+的Gradle自动管理陷阱
Unity 2019.4之后,Android构建默认启用Gradle,并在2021.3+版本中强制使用mainTemplate.gradle模板。很多团队还在用老教程里“把WXApi.jar拖进Plugins/Android”的方式,结果在Unity 2022.3 LTS上打包直接报Duplicate class com.tencent.mm.opensdk——因为Unity自动从Maven仓库拉取了同名SDK,和你手动放的jar冲突。必须用Gradle依赖方式管理,且要精准控制版本。
3.1 正确的SDK引入姿势:用mavenCentral替代jar包
微信官方Android SDK最新稳定版是6.8.0(截至2024年Q2),对应aar包名为libammsdk。不要下载jar,要下载aar并转为Unity可识别的结构:
- 访问 Maven Repository
- 找到6.8.0版本,下载
opensdk-android-6.8.0.aar - 新建文件夹
Assets/Plugins/Android/wechat,将aar解压(用WinRAR或unzip命令) - 解压后得到
classes.jar、AndroidManifest.xml、res/等,保留classes.jar和AndroidManifest.xml,删除res/文件夹(Unity会自动生成资源目录,微信SDK的res会导致合并冲突) - 将
classes.jar重命名为wechat-sdk.jar,放入Assets/Plugins/Android/wechat/
为什么删res?微信SDK的
AndroidManifest.xml里声明了WXEntryActivity,但Unity自动生成的AndroidManifest.xml里已有UnityPlayerActivity,二者必须共存。若保留SDK的res,Unity构建时会尝试合并资源,导致R.string.app_name等冲突。删res后,我们手动在Unity的AndroidManifest.xml里补全必要声明。
3.2 自定义AndroidManifest.xml:声明WXEntryActivity并配置intent-filter
Unity默认的AndroidManifest.xml位于Assets/Plugins/Android/AndroidManifest.xml(若不存在,需从Temp/StagingArea/AndroidManifest.xml复制一份)。必须添加以下节点:
<application> <!-- 微信回调Activity,必须声明,且exported=true --> <activity android:name=".wxapi.WXEntryActivity" android:exported="true" android:label="微信入口" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> </application>注意android:exported="true"是Android 12+强制要求,漏写会导致WXApi.sendReq()静默失败。同时,在<application>外层<manifest>节点中,确保有package="your.package.name"与微信后台一致。
3.3 Gradle Template定制:禁用Unity自动依赖,显式声明SDK版本
启用Custom Main Gradle Template(Player Settings → Publishing Settings → Build → Custom Main Gradle Template),编辑Assets/Plugins/Android/mainTemplate.gradle:
在dependencies块中,删除所有implementation 'com.tencent.mm.opensdk'相关行(Unity自动生成的),改为:
dependencies { implementation(name: 'wechat-sdk', ext: 'jar') // 其他依赖保持不变... }并在android块末尾添加签名配置(避免每次打包手动选keystore):
android { signingConfigs { release { keyAlias 'androiddebugkey' keyPassword 'android' storeFile file('../Assets/Plugins/Android/debug.keystore') storePassword 'android' } } buildTypes { release { signingConfig signingConfigs.release } debug { signingConfig signingConfigs.release // debug也用release签名,确保签名一致 } } }关键经验:Unity的debug构建默认用
debug.keystore,release构建用你指定的keystore,但微信要求所有环境(debug/release)必须用同一套签名,否则测试时好使,上架后失败。所以这里强制debug也走release签名配置。实测下来,用release签名跑Unity Editor的Android Remote调试完全正常。
4. C#核心逻辑实现:从注册到回调,每一步都带日志埋点
Unity侧的C#代码量不大,但每行都有讲究。我坚持在所有WXApi调用前后加Debug.Log,并封装成带状态机的Manager类,避免异步回调丢失上下文。
4.1 初始化与注册:WXApi.registerApp()不是万能钥匙
public class WeChatLoginManager : MonoBehaviour { private const string APP_ID = "wx1234567890abcdef"; // 替换为你的AppID private IWXAPI wxApi; public void Init() { Debug.Log($"[WeChat] 初始化开始,AppID: {APP_ID}"); wxApi = WXAPIFactory.CreateWXAPI(gameObject, APP_ID, false); // 必须调用registerApp,但返回true不代表注册成功! bool registered = wxApi.RegisterApp(APP_ID); Debug.Log($"[WeChat] registerApp返回: {registered}"); // 真正校验注册状态:检查是否支持微信 bool isWXAppInstalled = wxApi.IsWXAppInstalled(); bool isWXAppSupportAPI = wxApi.IsWXAppSupportAPI(); Debug.Log($"[WeChat] 微信已安装: {isWXAppInstalled}, 支持API: {isWXAppSupportAPI}"); if (!isWXAppInstalled) { Debug.LogError("[WeChat] 设备未安装微信,请提示用户"); // 弹Toast或UI提示 } } }重点:registerApp()返回true,只表示SDK初始化成功,不校验AppID有效性。真正校验AppID是否在微信后台备案,是在sendReq()时发生的。所以必须配合IsWXAppInstalled()和IsWXAppSupportAPI()做前置判断,否则用户点登录按钮后卡住,体验极差。
4.2 发起登录请求:SendReq的参数构造是成败关键
微信登录用SendAuth.Req,不是SendMessageToWX.Req。参数必须严格按文档:
public void LoginWithWeChat() { if (wxApi == null || !wxApi.IsWXAppInstalled()) { Debug.LogError("[WeChat] 微信未安装,无法发起登录"); return; } SendAuth.Req req = new SendAuth.Req(); req.scope = "snsapi_userinfo"; // 必须,获取用户公开信息 req.state = "uni_" + Random.Range(1000, 9999); // 防CSRF,必须传,且服务端要校验 Debug.Log($"[WeChat] 发起登录请求,state: {req.state}"); bool sent = wxApi.SendReq(req); Debug.Log($"[WeChat] SendReq返回: {sent}"); // true仅表示发出去了,不保证微信处理成功 }scope参数只能是snsapi_base(静默授权,不弹窗)或snsapi_userinfo(弹窗授权),Unity游戏几乎都用后者。state必须是随机字符串,且长度建议6-32位,服务端收到code后要原样比对,防止重放攻击。我用uni_前缀标识Unity来源,方便后端日志追踪。
4.3 回调接收:WXEntryActivity是唯一入口,C#无法直接监听
这是Unity开发者最容易误解的点:C#脚本无法直接接收微信回调。微信SDK规定,回调必须由WXEntryActivity接收,然后通过广播或Intent传递给Unity。我们必须在Assets/Plugins/Android/wechat/WXEntryActivity.java中处理:
public class WXEntryActivity extends Activity implements IWXAPIEventHandler { private IWXAPI api; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); api = WXAPIFactory.createWXAPI(this, "wx1234567890abcdef", false); api.handleIntent(getIntent(), this); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); api.handleIntent(intent, this); } @Override public void onReq(BaseReq baseReq) {} @Override public void onResp(BaseResp baseResp) { // 回调结果,必须转发给Unity Intent intent = new Intent(); intent.setAction("com.unity.wechat.login.result"); intent.putExtra("errCode", baseResp.errCode); intent.putExtra("errStr", baseResp.errStr); intent.putExtra("transaction", baseResp.transaction); sendBroadcast(intent); } }然后在C#中用AndroidJavaObject注册广播接收器:
private AndroidJavaObject broadcastReceiver; private void RegisterBroadcastReceiver() { using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); broadcastReceiver = new AndroidJavaObject( "com.yourcompany.wechat.WeChatBroadcastReceiver"); currentActivity.Call("registerReceiver", broadcastReceiver, new AndroidJavaObject("android.content.IntentFilter", "com.unity.wechat.login.result")); } } // 在OnDestroy中注销 private void UnregisterBroadcastReceiver() { if (broadcastReceiver != null) { using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); currentActivity.Call("unregisterReceiver", broadcastReceiver); } } }WeChatBroadcastReceiver.java需在Assets/Plugins/Android/下实现,负责解析广播并调用Unity C#方法。这一步不可省略,否则回调永远到不了C#层。
5. 真机调试与问题排查:从Logcat日志定位根因的完整链路
模拟器无法调试微信登录(微信App不支持模拟器),必须真机。但很多团队连Logcat都看不懂,对着一堆W/Bundle警告干瞪眼。下面是我用Logcat定位问题的标准流程。
5.1 Logcat过滤技巧:聚焦WXApi和YourApp
在Android Studio中打开Logcat,设置过滤器:
- Log Level: Verbose
- Package Name:
your.package.name(你的包名) - Search:
WX|wechat|yourappname
这样能屏蔽90%无关日志。重点关注以W/(Warning)和E/(Error)开头的行。
5.2 常见错误码对照表与根因分析
| 错误码 | 日志特征 | 根本原因 | 解决方案 |
|---|---|---|---|
| -6 | sendReq fail, errCode=-6 | 包名或签名不匹配 | 用aapt dump badging核对APK包名,用keytool重生成SHA1填微信后台 |
| -10 | sendReq fail, errCode=-10 | AppID未在微信后台备案,或备案未生效 | 登录微信开放平台,确认AppID状态为“已审核通过”,且备案时间>2小时 |
| -12 | sendReq fail, errCode=-12 | WXEntryActivity未在AndroidManifest.xml中声明,或android:exported="false" | 检查AndroidManifest.xml,确认activity节点存在且exported=true |
| -2 | sendReq fail, errCode=-2 | 微信App版本过低(<6.5.0) | 提示用户升级微信,或兼容旧版(需额外配置) |
实测案例:某团队报-6错误,查Logcat发现
W/Bundle: Key app_package_name expected String but value was a java.lang.Integer。根源是Unity Player Settings里Package Name填成了数字(如123456),Android系统解析失败。改成com.game.id123456后立即解决。
5.3 动态调试技巧:用adb命令实时查看Intent
当怀疑回调未触发时,不用重装APK,用adb抓取微信发来的Intent:
adb shell am start -n "com.tencent.mm/.plugin.webview.ui.tools.WebViewUI" \ --es "url" "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx1234567890abcdef&redirect_uri=https%3A%2F%2Fyourdomain.com%2Fcallback&response_type=code&scope=snsapi_userinfo&state=uni_1234#wechat_redirect"这条命令手动触发微信OAuth流程,若能弹窗说明SDK和配置基本OK。若失败,Logcat会直接打印WXApi内部错误,比等Unity打包快十倍。
6. iOS端适配要点:Xcode配置与Universal Links的隐性依赖
虽然标题是Unity集成,但上线必须双端。iOS比Android简单,但有几个Unity特有的坑。
6.1 Info.plist配置:URL Scheme和LSApplicationQueriesSchemes缺一不可
在Player Settings → Publishing Settings → iOS → URL Types中添加:
- Identifier:
weixin - URL Schemes:
wx1234567890abcdef(你的AppID,必须全小写)
同时,在Info.plist的LSApplicationQueriesSchemes数组中添加weixin和weixinULAPI(iOS 9+要求):
<key>LSApplicationQueriesSchemes</key> <array> <string>weixin</string> <string>weixinULAPI</string> </array>6.2 Xcode工程修改:Link Binary With Libraries必须包含libsqlite3.tbd
微信SDK依赖SQLite,但Unity 2021+默认不链接。打开Xcode工程,选择Target → Build Phases → Link Binary With Libraries,点击+号,搜索libsqlite3.tbd并添加。漏加会导致WXApi.registerApp()返回false。
6.3 Universal Links不是必须,但能提升体验
微信iOS端推荐用Universal Links替代URL Scheme,避免Safari弹窗。但这需要你的服务器配置apple-app-site-association文件,并在Xcode中开启Associated Domains。对Unity项目来说,只需在Xcode中勾选Associated Domains,并添加applinks:yourdomain.com。注意:此功能与微信登录无直接关系,但若用户从微信内H5跳转到你的游戏,Universal Links能实现无缝唤醒。
7. 安全加固与上线 checklist:避开审核雷区的11个动作
微信登录通过后,还有应用宝、华为商店等渠道审核。我整理了上线前必须做的11件事,少一项都可能被拒:
- AppID硬编码检查:C#代码中
APP_ID不能是明文字符串,必须用#if UNITY_EDITOR包裹,或从AssetBundle加载 - debug.keystore移除:上线包必须用release.keystore签名,且
mainTemplate.gradle中storeFile路径不能指向Assets/Plugins/Android/下的debug文件 - 隐私政策弹窗:微信要求首次启动必须弹出隐私政策,且内容需包含“获取用户公开信息”说明
- 权限声明最小化:AndroidManifest.xml中只保留
INTERNET和ACCESS_NETWORK_STATE,微信登录不需要读取手机状态 - WXEntryActivity主题:必须设为
@android:style/Theme.Translucent.NoTitleBar,否则审核时截图显示白屏 - 错误码兜底:C#中
onResp必须处理errCode != 0的所有情况,不能只处理-2和0 - state参数服务端校验:前端生成的
state必须和服务端存储的完全一致,防止CSRF - code有效期处理:微信返回的
code5分钟有效,服务端必须立即换取access_token,不能缓存 - 用户头像防盗链:微信返回的
headimgurl是临时链接,需服务端下载后存到自有CDN - 离线状态提示:网络断开时,
IsWXAppInstalled()可能返回false,需提示“请检查网络”而非“微信未安装” - 多语言适配:微信授权页语言跟随系统,但你的UI提示语必须支持中英文,否则海外审核不通过
最后分享一个血泪教训:某团队上线前没做第3条(隐私政策),应用宝审核驳回理由是“未明确告知用户信息收集目的”,修改后重提审又等了3天。现在我们所有新项目,都在Unity启动时第一帧就弹出带“同意”和“拒绝”按钮的WebView隐私页,拒绝则退出应用——这是最稳妥的合规方案。
我在实际操作中发现,把微信登录做成“可插拔模块”最省心:所有WXApi调用封装在WeChatLoginService单例中,通过接口注入,Unity Editor里用Mock实现,真机才走SDK。这样单元测试、CI自动化都能覆盖,上线前切换开关即可。这个模式已复用到7个项目,零线上事故。