1. 项目概述与核心价值
最近在做一个Unity手游项目,需要接入微信登录和分享功能。说实话,第一次在Unity里搞微信SDK集成,踩的坑比预想的要多得多。网上资料虽然不少,但要么是官方文档语焉不详,要么是零散的代码片段,很难找到一个从零开始、能完整跑通的实战案例。所以,我决定把这次从环境搭建、SDK配置到功能调试的完整过程记录下来,整理成一个可以直接“抄作业”的Demo。这个Demo的目标很明确:让你能在一个干净的Unity工程里,快速、稳定地实现微信授权登录和分享到朋友圈/好友的功能,避开我遇到的那些“坑”。
为什么这件事值得单独写一篇?因为Unity移动端开发,尤其是对接国内安卓生态,环境配置的复杂度往往比写业务逻辑本身还高。JDK、SDK、NDK的版本兼容性,AndroidManifest的配置,微信开放平台的应用签名获取,以及Unity与Android原生代码的交互(AndroidJavaClass/AndroidJavaObject),每一个环节都可能让你卡上半天。这个Demo会把这些琐碎但关键的点都串起来,提供一个经过验证的、可复现的解决方案。无论你是独立开发者还是项目组的新成员,这份指南都能帮你省下大量摸索的时间。
2. 环境准备与前置条件解析
在动手写代码之前,我们必须把“地基”打牢。Unity与微信SDK的集成,本质上是在Unity的C#环境中调用Android平台的原生Java代码。因此,一个正确配置的跨平台开发环境是成功的前提。
2.1 基础软件安装与版本确认
首先,确保你的电脑上安装了以下软件,并且版本匹配至关重要。不匹配的版本是后续绝大多数诡异错误的根源。
- Unity Hub & Unity Editor: 建议使用稳定的LTS版本,如2021.3 LTS或2022.3 LTS。长期支持版经过了更充分的测试,与各种SDK的兼容性更好。避免使用最新的Tech Stream版本,除非你有明确的理由。
- JDK (Java Development Kit):必须使用JDK 8。更高版本的JDK(如JDK 11, 17)在Unity打包Android时可能导致
dx工具报错。你可以在命令行输入java -version来确认。如果已经安装了其他版本,建议单独安装一个JDK 8,并在Unity中指定其路径。 - Android SDK: 可以通过Unity Hub安装,或者独立安装Android Studio。关键是要安装正确的SDK Platform和Build-Tools。通常,选择API Level 30-33(对应Android 11-13)中的一个作为编译目标(Target API)是比较稳妥的。同时,必须安装对应版本的“Android SDK Build-Tools”。
- NDK (Native Development Kit): Unity打包Android IL2CPP后端时需要。同样建议通过Unity Hub安装推荐版本(如Unity 2021.3推荐NDK r19-23)。手动下载配置路径容易出错。
注意:Unity内部有自己的一套环境检测逻辑。你可以在
Edit -> Preferences -> External Tools中查看和设置JDK、SDK、NDK、Gradle的路径。这里配置的路径优先级最高,会覆盖系统环境变量。务必确保这里指向的路径是你精心配置的、版本正确的那些。
2.2 微信开放平台应用创建与配置
这是接入微信功能的法律和技术凭证来源,一步错,步步错。
- 注册与创建应用:访问微信开放平台,注册开发者账号并创建一个“移动应用”。应用包名(Bundle Identifier)和签名(MD5)是核心。
- 获取应用签名(关键且易错):
- 官方方法:下载官方的“签名生成工具”App,安装到你的测试手机上,输入包名后获取MD5签名。这个签名是基于你**最终发布签名密钥(keystore)**生成的。如果你在开发阶段使用Unity默认的调试密钥(
debug.keystore),那么用调试密钥生成的签名和用发布密钥生成的签名是不同的! - 开发期最佳实践:为了调试方便,我强烈建议为开发阶段专门生成一个Keystore,并配置到Unity的Player Settings中。这样,你在微信开放平台后台配置的签名,就可以直接使用这个开发Keystore来生成。避免混用
debug.keystore,因为它的路径和密码可能因电脑而异。 - 生成签名命令:
输入Keystore密码后,找到“证书指纹”部分的MD5值(去掉冒号,全部小写),这就是你需要填到微信后台的App Sign。keytool -list -v -keystore your_keystore_name.keystore
- 官方方法:下载官方的“签名生成工具”App,安装到你的测试手机上,输入包名后获取MD5签名。这个签名是基于你**最终发布签名密钥(keystore)**生成的。如果你在开发阶段使用Unity默认的调试密钥(
- 后台配置:在微信开放平台的应用详情页,准确填写“应用包名”(例如:
com.YourCompany.YourGame)和上面得到的“应用签名”。提交审核(通常很快)后,你会获得至关重要的AppID。这个AppID需要写在Unity工程的配置文件和Android代码中。
3. 微信SDK导入与Unity工程配置
拿到AppID后,我们就可以在Unity工程中动手了。
3.1 获取并导入微信SDK
- 下载SDK:从微信开放平台的“资源中心”下载最新的“Unity SDK”。它通常是一个
.unitypackage文件。 - 导入Unity:在Unity中,
Assets -> Import Package -> Custom Package...,选择下载的包。导入时,注意勾选所有文件,特别是Plugins/Android目录下的wxapi模块和.jar文件,这些是Android原生的桥梁代码。 - 目录结构检查:导入后,你的工程
Assets目录下应该会出现类似WXSDK或WeChatSDK的文件夹。里面包含了C#脚本、示例场景、以及最重要的Plugins/Android/WeChatSDK目录(内含AndroidManifest.xml和libammsdk.jar等)。
3.2 配置Unity Player Settings
这是将微信SDK与你的Android应用绑定的关键步骤。
- 设置包名:打开
File -> Build Settings -> Player Settings...。在Player设置面板中,找到Other Settings部分。- Bundle Identifier:必须与微信开放平台后台填写的包名完全一致(例如:
com.YourCompany.YourGame)。
- Bundle Identifier:必须与微信开放平台后台填写的包名完全一致(例如:
- 配置Keystore:在
Player Settings -> Publishing Settings下,勾选Custom Keystore。- Browse选择你之前为开发生成的Keystore文件(或使用专门的调试Keystore)。
- 填写正确的
Keystore password和Alias password。确保这个Keystore生成的签名,就是你提交给微信后台的那个MD5值。
- 设置最低API Level:在
Other Settings中,将Minimum API Level设置为至少API Level 23 (Android 6.0),因为微信SDK的一些功能需要此版本以上的支持。
3.3 处理AndroidManifest.xml
微信SDK的.unitypackage通常会包含一个预设的AndroidManifest.xml。但直接使用它可能会与你的主Manifest冲突。更可靠的做法是合并。
- 找到SDK的Manifest:通常位于
Assets/Plugins/Android/WeChatSDK/AndroidManifest.xml。 - 合并到主Manifest:Unity打包时,会使用
Assets/Plugins/Android/AndroidManifest.xml作为主清单文件。如果这个文件不存在,你需要创建一个。然后,将微信SDK Manifest中的关键内容拷贝过来。主要包括:- 权限声明:如网络权限、访问网络状态等。
- 微信Activity声明:这是用于接收微信返回结果的“回调页面”,必须声明。
- 微信AppID的Meta-data:这是将你的AppID注入Android应用的地方。
一个合并后的主AndroidManifest.xml关键部分示例如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.YourCompany.YourGame"> <!-- 基础权限 --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <application android:icon="@drawable/app_icon" android:label="@string/app_name"> <!-- 你的其他Activity... --> <!-- 微信回调Activity (必须) --> <activity android:name=".wxapi.WXEntryActivity" android:exported="true" android:launchMode="singleTop" android:taskAffinity="你的包名" android:theme="@android:style/Theme.Translucent.NoTitleBar"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="wx你的AppID" /> <!-- 例如:wx1234567890abcdef --> </intent-filter> </activity> <!-- 微信AppID配置 (必须) --> <meta-data android:name="WX_APP_ID" android:value="你的微信AppID" /> <!-- 例如:wx1234567890abcdef --> </application> </manifest>实操心得:
WXEntryActivity的android:taskAffinity属性最好设置成你的应用包名,这能避免在某些安卓机型上,从微信跳转回游戏时,游戏被重新启动(冷启动)而不是回到前台。android:theme设置为透明主题,可以让跳转过程更平滑,避免黑屏。
4. 核心功能C#代码实现与封装
环境配置妥当后,我们进入核心的C#脚本编写环节。一个好的封装应该做到:调用简单、事件清晰、与游戏逻辑解耦。
4.1 微信管理器单例设计
我们首先创建一个单例管理器WeChatManager.cs,负责SDK的初始化、功能调用和回调处理。
using UnityEngine; using System; using WeChatWASM; // 微信SDK的命名空间,根据实际导入的SDK调整 public class WeChatManager : MonoBehaviour { public static WeChatManager Instance { get; private set; } // 定义登录成功和失败的回调 public Action<string> OnLoginSuccess; // 参数可以是code或unionid public Action<string> OnLoginFailure; // 定义分享成功和失败的回调 public Action OnShareSuccess; public Action<string> OnShareFailure; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); // 常驻场景,便于全局调用 InitWeChatSDK(); } // 初始化微信SDK private void InitWeChatSDK() { var config = new WeChatWASMConfig { appId = "你的微信AppID", // 务必替换 universalLink = "你的Universal Link", // iOS必需,安卓可留空或填域名 }; WX.Init(config); Debug.Log("微信SDK初始化完成。"); // 注册全局事件监听(如果需要) WX.OnShow((res) => { /* 游戏从后台切回 */ }); WX.OnHide(() => { /* 游戏切到后台 */ }); } }4.2 微信登录功能实现
微信登录的核心是获取一个临时的code,然后将这个code发送到你自己的游戏服务器,由服务器用AppID和AppSecret去微信服务器换取用户的唯一标识(openid和session_key)。绝对不要在客户端存储或使用AppSecret!
在WeChatManager.cs中添加登录方法:
public void RequestWeChatLogin() { // 检查微信是否安装 WX.CheckIsWeChatInstalled((isInstalled) => { if (!isInstalled) { OnLoginFailure?.Invoke("未安装微信客户端"); // 可以在这里给UI提示 return; } // 发起登录请求 WX.Login(new LoginOption { success = (loginRes) => { Debug.Log($"获取到登录code: {loginRes.code}"); // 成功!将code发送给你的游戏服务器 OnLoginSuccess?.Invoke(loginRes.code); // 示例:StartCoroutine(SendCodeToServer(loginRes.code)); }, fail = (failRes) => { Debug.LogError($"微信登录失败: {failRes.errMsg}"); OnLoginFailure?.Invoke(failRes.errMsg); } }); }); } // 示例协程,将code发送到自己的服务器 private System.Collections.IEnumerator SendCodeToServer(string code) { WWWForm form = new WWWForm(); form.AddField("wx_code", code); // 可以添加其他参数,如设备ID等 using (UnityWebRequest request = UnityWebRequest.Post("https://your-server.com/api/wx-login", form)) { yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { // 处理服务器返回的token、用户信息等 var jsonResponse = JsonUtility.FromJson<ServerResponse>(request.downloadHandler.text); // ... 保存登录态,跳转场景等 } else { Debug.LogError($"服务器请求失败: {request.error}"); } } }4.3 分享功能实现(图文分享到朋友圈/好友)
分享功能相对独立,需要构造一个分享参数对象。这里实现分享到朋友圈和分享给好友两个常用功能。
在WeChatManager.cs中继续添加:
public void ShareToTimeline(string title, string imagePath, string query = "") { ShareInternal(title, imagePath, query, "timeline"); } public void ShareToFriend(string title, string imagePath, string query = "") { ShareInternal(title, imagePath, query, "session"); } private void ShareInternal(string title, string imagePath, string query, string scene) { // 1. 先处理分享图 // imagePath可以是项目内的Resources路径,也可以是网络URL,或者是临时文件路径。 // 微信对分享图片有大小限制(通常<=32KB),需要先压缩或处理。 // 这里假设imagePath是一个已处理好的本地图片路径(例如通过Texture2D.EncodeToPNG保存的) if (!System.IO.File.Exists(imagePath)) { Debug.LogError($"分享图片不存在: {imagePath}"); OnShareFailure?.Invoke("分享图片资源错误"); return; } // 2. 读取图片为byte数组 (示例,实际需考虑异步和性能) byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath); // 3. 将图片上传到微信临时素材,获取mediaId (这是分享高清图的一种方式) // 注意:微信JS-SDK或小程序分享方式与原生SDK不同,Unity SDK通常需要先将图片上传。 // 以下为逻辑示意,具体API调用请参考最新微信SDK文档。 WX.UploadImage(new UploadImageOption { filePath = imagePath, // 或使用base64等,取决于SDK支持 success = (uploadRes) => { string mediaId = uploadRes.mediaId; // 4. 使用mediaId进行分享 WX.ShareAppMessage(new ShareAppMessageOption { title = title, imageUrl = imagePath, // 低缩略图路径,用于展示 mediaId = mediaId, // 高清图ID query = query, // 自定义参数,用户点击分享卡片进入游戏时可获取 scene = scene, // "timeline" 或 "session" success = () => { Debug.Log("分享成功"); OnShareSuccess?.Invoke(); }, fail = (shareFailRes) => { Debug.LogError($"分享失败: {shareFailRes.errMsg}"); OnShareFailure?.Invoke(shareFailRes.errMsg); } }); }, fail = (uploadFailRes) => { Debug.LogError($"图片上传失败: {uploadFailRes.errMsg}"); OnShareFailure?.Invoke("图片上传失败"); } }); }注意事项:分享功能是坑最多的。第一,图片尺寸和大小必须严格遵守微信的限制(缩略图通常小于32KB),否则分享会静默失败。第二,
query参数可以携带自定义信息(如“邀请码=123”),当用户通过分享的卡片启动你的游戏时,你可以在游戏启动参数中获取到这个query,从而实现“分享拉新”的效果。第三,不同版本的微信SDK,分享API可能有差异,务必以你导入的SDK包内的示例和文档为准。
5. Android原生层回调处理与深度配置
当用户在微信客户端完成登录授权或分享操作后,微信需要将结果返回到你的游戏。在Android上,这个“返回的入口”就是我们在AndroidManifest.xml中声明的WXEntryActivity。我们需要在Unity中处理这个回调。
5.1 创建Android Java回调插件
虽然微信SDK的.unitypackage可能已经包含了必要的Java代码,但有时我们需要自定义回调逻辑。这里展示如何创建一个最简单的桥接。
- 在Unity项目中创建Android插件目录:
Assets/Plugins/Android/ - 创建Java源文件:例如
com/yourcompany/yourgame/wxapi/WXEntryActivity.java。注意包路径必须与AndroidManifest.xml中声明的完全一致。 - 编写Java代码:这个类需要继承微信SDK提供的基类(如
WXEntryActivity),并重写相关回调方法。
package com.yourcompany.yourgame.wxapi; import android.os.Bundle; import com.tencent.mm.opensdk.modelbase.BaseReq; import com.tencent.mm.opensdk.modelbase.BaseResp; import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler; // 注意:微信SDK的包名和类名可能随版本变化,请以实际导入的jar包为准 public class WXEntryActivity extends WeChatCallbackActivity implements IWXAPIEventHandler { // WeChatCallbackActivity 可能是SDK提供的基类,用于简化处理。 // 如果SDK没有提供,则需要直接继承Activity并实现IWXAPIEventHandler。 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 通常这里不需要UI,所以可以设置透明主题并尽快finish try { // 将事件交给微信API处理 if (wxApi != null) { wxApi.handleIntent(getIntent(), this); } } catch (Exception e) { e.printStackTrace(); } finish(); // 处理完立即关闭Activity } @Override public void onReq(BaseReq baseReq) { // 微信发送请求到你的应用(如要求分享内容),游戏一般用不到 } @Override public void onResp(BaseResp baseResp) { // 这是核心回调!你的应用发送请求到微信后的响应。 int errCode = baseResp.errCode; String result = ""; switch (errCode) { case BaseResp.ErrCode.ERR_OK: result = "success"; break; case BaseResp.ErrCode.ERR_USER_CANCEL: result = "cancel"; break; case BaseResp.ErrCode.ERR_AUTH_DENIED: result = "deny"; break; default: result = "unknown"; break; } // 关键步骤:将回调结果发送给Unity的C#代码 // 假设你有一个UnityPlayer的静态方法用于接收消息 com.unity3d.player.UnityPlayer.UnitySendMessage( "WeChatManager", // Unity中接收消息的GameObject名字 "OnWXResponse", // 该GameObject上脚本的公共方法名 result + ":" + baseResp.transaction // 拼接参数,用冒号分隔 ); finish(); // 确保Activity关闭 } }5.2 在Unity C#中接收回调
在WeChatManager.cs中,添加一个公共方法来接收来自Android Java层的回调消息。
// 这个方法必须为public,供Android原生代码调用 public void OnWXResponse(string responseStr) { Debug.Log($"收到微信原生回调: {responseStr}"); string[] parts = responseStr.Split(':'); if (parts.Length < 2) return; string result = parts[0]; string transaction = parts[1]; // 可用于区分是登录回调还是分享回调 // 根据transaction或result判断是登录还是分享的回调 // 这里简化处理,实际项目中需要更严谨的状态管理 if (result == "success") { // 分享成功或登录成功(登录成功主要靠之前的WX.Login回调) if (/* 判断为分享回调 */) { OnShareSuccess?.Invoke(); } } else if (result == "cancel") { Debug.Log("用户取消操作"); // 触发取消回调 } else { Debug.LogError($"微信操作失败: {result}"); // 触发失败回调 if (/* 判断为分享回调 */) { OnShareFailure?.Invoke(result); } } }实操心得:Android原生回调是调试的难点。确保三点:第一,Java文件的包名、类名与
AndroidManifest.xml中的声明一字不差。第二,UnitySendMessage调用时,第一个参数(GameObject名)必须在当前场景中存在且唯一。我通常会让WeChatManager挂载在一个永不销毁的GameObject上,并确保其名字与Java代码中发送的消息目标名一致。第三,在真机上调试时,多用adb logcat查看系统日志,可以捕捉到Java层的异常信息,这对于排查ClassNotFoundException或MethodNotFoundException非常有效。
6. 打包、测试与真机调试全流程
代码写完了,但离成功还差最关键的一步:打包测试。很多问题只有到了真机上才会暴露。
6.1 构建APK与签名对齐
- Build Settings:在Unity中,
File -> Build Settings,选择Android平台,点击Switch Platform。 - Player Settings复查:再次确认包名、Keystore、Minimum API Level。
- 构建:建议先使用
Development Build和Autoconnect Profiler,方便调试。点击Build And Run,选择一个输出路径(如YourGame.apk)。 - 签名对齐 (zipalign):Unity的构建过程通常会自动处理。但如果你手动签名,或者遇到安装失败,可能需要使用Android SDK的
zipalign工具进行对齐优化。
6.2 真机测试步骤与要点
- 安装测试包:将生成的APK文件安装到测试手机(安卓机)上。确保手机已开启“USB调试”模式。
- 检查微信安装:首次运行你的游戏,并点击登录按钮。你的代码应该会检查微信是否安装。如果没安装,应有友好提示。
- 登录测试:
- 点击微信登录,应正常调起微信客户端,并显示授权页面。
- 点击“允许”后,应能正确返回到你的游戏,并打印出获取到的
code。 - 关键检查点:如果点击后没反应,或者闪一下又回来了,99%是签名错误。请用你打包APK的Keystore重新生成一次MD5签名,与微信开放平台后台的配置进行比对。必须完全一致,包括大小写。
- 分享测试:
- 测试分享到朋友圈和好友。观察是否能正常调起微信分享界面,图片和标题是否正确。
- 分享成功后,在微信中点击你分享的卡片,应该能正确启动你的游戏(如果已安装),或者跳转到应用商店(如果未安装)。
- 注意:在Android上,通过分享卡片启动游戏,会走
WXEntryActivity这个入口,并携带query参数。你需要确保游戏能正确处理这种“冷启动带参”的场景。
6.3 常见问题排查与解决实录
以下是我在集成过程中遇到的一些典型问题及解决方法,希望能帮你快速定位。
| 问题现象 | 可能原因 | 排查与解决步骤 |
|---|---|---|
| 点击登录/分享无任何反应 | 1. 微信未安装。 2. 签名错误(最常见)。 3. AppID配置错误(Manifest或C#代码)。 4. WXEntryActivity未正确声明或实现。 | 1. 检查代码中CheckIsWeChatInstalled的返回值。2.重中之重:核对签名。用打包APK的Keystore生成MD5,与微信后台比对。 3. 检查 AndroidManifest.xml和C#初始化代码中的AppID。4. 检查 AndroidManifest.xml中WXEntryActivity的声明,以及Java文件是否存在且包名正确。 |
| 能调起微信,但授权后无法返回游戏 | 1.WXEntryActivity的android:exported未设为true。2. intent-filter中的data scheme错误(应为wx+AppID)。3. taskAffinity设置不当,导致新开了一个应用实例。 | 1. 确认AndroidManifest.xml中WXEntryActivity的exported="true"。2. 确认 data标签的android:scheme值是否正确。3. 尝试设置 taskAffinity为你的应用包名,并设置launchMode="singleTask"或"singleTop"。 |
| 分享图片失败/不显示 | 1. 图片路径错误或文件不存在。 2. 图片尺寸或文件大小超出微信限制。 3. 未正确处理图片格式(需JPEG/PNG)。 4. 未先上传图片获取 mediaId(针对高清图分享)。 | 1. 打印或调试确认图片的最终路径和文件大小。 2. 将图片压缩到微信要求范围内(如缩略图<32KB)。 3. 确保使用 Texture2D.EncodeToJPG/PNG生成图片字节流。4. 查阅当前微信SDK文档,确认分享图片的正确流程(是先上传还是直接传路径)。 |
| Unity打包时报Gradle错误 | 1. JDK版本不是8。 2. Android SDK路径错误或组件缺失。 3. Gradle版本与Unity不兼容。 4. 项目路径包含中文或特殊字符。 | 1. 在UnityPreferences -> External Tools中强制指定JDK 8路径。2. 使用Unity Hub安装推荐的Android SDK/NDK,或在External Tools中正确设置路径。 3. 尝试在Player Settings中切换Gradle版本(使用内置或自定义)。 4. 将项目移到纯英文路径下。 |
| 回调能收到,但游戏对象找不到方法 | UnitySendMessage中的GameObject名称或方法名称拼写错误,或该GameObject在当前场景中未激活/不存在。 | 1. 检查Java代码中UnitySendMessage的第一个和第二个参数。2. 确保C#中接收消息的GameObject名字匹配,且脚本中的方法为 public void。3. 确保承载 WeChatManager的GameObject在收到回调时是Active的。 |
最后再分享一个小技巧:在开发初期,可以创建一个简单的调试UI,把AppID、签名Key(从Keystore计算出的MD5)显示在屏幕上,并与微信后台的配置进行比对。同时,把所有微信SDK的回调(成功、失败、取消)都打印到UI日志面板中,这样在真机上测试时,所有状态一目了然,能极大提升排查效率。集成第三方SDK就是一个不断与环境、配置、版本作斗争的过程,耐心和细致的日志是唯一的捷径。