如何在Unity中结合Google Play Services的NearbyConnections实现玩家间数据传输?
我太懂你这种文档稀碎、插件还跟不上版本的痛苦了!之前做校园项目做类似Streetpass的功能时,我也在这上面摸爬滚打了好一阵,刚好把踩过的坑和可行的步骤整理给你,完全适配你要的近距离玩家数据传输需求~
一、前期准备:把Google Play Nearby Connections接入Unity
别去碰那些第三方旧插件了,直接用官方的Play Services包才是最稳的:
- 打开Unity的Package Manager,搜索并安装Google Play Services Nearby Connections包(要是搜不到,先确认Unity版本在2020及以上,手动导入官方aar包也可以,但Package Manager更省心)
- 去
Project Settings > Android > Publishing Settings,勾选「Custom Main Manifest」,然后在自动生成的AndroidManifest.xml里添加Nearby必需的权限:<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" /> - 去Google Cloud Console给你的应用配置Nearby Connections的API密钥,把对应的OAuth Client ID填到Unity的Google Play Services设置里——这步绝对不能忘!我当时就是漏了这个,两台手机死活搜不到对方。
二、核心功能实现:从发现到传数据
我把整个流程拆成了几个关键模块,你可以直接套进你的游戏逻辑里:
1. 初始化Nearby Connections
先整个单例类管理所有Nearby操作,避免重复初始化:
using GooglePlayGames.Nearby; using GooglePlayGames; using UnityEngine; public class NearbyStreetpassManager : MonoBehaviour { public static NearbyStreetpassManager Instance; private const string SERVICE_ID = "com.yourgame.streetpass.service"; // 用你的应用包名当唯一标识,两台设备必须一致! private string myPlayerTag; void Awake() { if (Instance == null) Instance = this; else Destroy(gameObject); DontDestroyOnLoad(gameObject); // 激活Play Games平台 PlayGamesPlatform.Activate(); myPlayerTag = PlayGamesPlatform.Instance.GetUserDisplayName(); // 用玩家的Google Play昵称当身份标识 } }
划重点:
SERVICE_ID是设备匹配的核心,两台设备必须用完全相同的字符串,不然互相搜不到!我当时随便写了个名字,结果卡了一上午才发现问题。
2. 广播/发现设备(Streetpass核心:互相找到对方)
要做类似Streetpass的自动感应,要么让设备同时广播自己并搜索周边,要么做个简单UI让玩家选择「等待连接」或「搜索玩家」:
// 开始广播自己,让周边设备能搜到 public void StartBroadcasting() { Nearby.Connections.StartAdvertising( myPlayerTag, SERVICE_ID, OnConnectionInvited, new AdvertisingOptions(Strategy.P2P_STAR), // 小范围设备用P2P_STAR模式最稳 () => Debug.Log("已开始广播自己"), (error) => Debug.LogError($"广播失败:{error}") ); } // 开始搜索周边的广播设备 public void StartDiscoveringPlayers() { Nearby.Connections.StartDiscovery( SERVICE_ID, OnFoundNearbyPlayer, () => Debug.Log("已开始搜索周边玩家"), (error) => Debug.LogError($"搜索失败:{error}") ); } // 搜到周边玩家时的回调 private void OnFoundNearbyPlayer(EndpointDetails endpointInfo) { Debug.Log($"发现玩家:{endpointInfo.EndpointName},设备ID:{endpointInfo.EndpointId}"); // 这里可以弹个UI让玩家选择是否发起连接 Nearby.Connections.SendConnectionRequest( myPlayerTag, endpointInfo.EndpointId, OnConnectionSuccess, (error) => Debug.LogError($"请求连接失败:{error}") ); }
坑预警:Android 12及以上必须动态申请位置权限!一定要在游戏启动时用
Permission.RequestUserPermission()弹权限请求,不然Nearby根本搜不到设备——我当时在测试机上卡到怀疑人生,最后才发现是权限没开。
3. 处理连接请求(建立双向连接)
收到别人的连接邀请,或者自己的请求被接受后,要确认连接:
// 收到其他玩家的连接邀请时 private void OnConnectionInvited(string endpointId, string playerName, byte[] payload) { // 弹UI让玩家选择是否接受连接 Nearby.Connections.AcceptConnection( endpointId, OnReceivedPlayerData, // 接收玩家数据的回调 OnPayloadTransferUpdate, OnConnectionSuccess, (error) => Debug.LogError($"拒绝连接:{error}") ); } // 连接成功的回调 private void OnConnectionSuccess(string endpointId, string playerName) { Debug.Log($"已和玩家「{playerName}」建立连接!"); // 这里触发你的游戏逻辑,比如显示「你遇到了玩家XXX」 }
4. 传输游戏数据(Streetpass核心:传玩家信息)
连接建立后,把你的游戏数据序列化成字节数组就能传输了,比如玩家ID、角色等级、自定义成就这些:
// 发送玩家数据给对方 public void SendStreetpassData(string endpointId, PlayerGameData myData) { // 用JsonUtility序列化数据,简单够用 byte[] dataBytes = System.Text.Encoding.UTF8.GetBytes(JsonUtility.ToJson(myData)); Nearby.Connections.SendPayload( endpointId, Payload.FromBytes(dataBytes) ); } // 收到对方数据的回调 private void OnReceivedPlayerData(string endpointId, Payload payload) { if (payload.Type == Payload.Type.BYTES) { string dataStr = System.Text.Encoding.UTF8.GetString(payload.AsBytes()); PlayerGameData receivedData = JsonUtility.FromJson<PlayerGameData>(dataStr); Debug.Log($"收到玩家数据:{receivedData.playerName},等级:{receivedData.playerLevel}"); // 把收到的数据存到本地,之后可以做解锁道具、触发剧情这些逻辑 } } // 自定义的玩家数据类,根据你的游戏需求改 [System.Serializable] public class PlayerGameData { public string playerName; public int playerLevel; public string favoriteCharacter; public bool hasUnlockedSecretItem; }
小技巧:如果要传头像这类二进制数据,用
Payload.FromFile()或者Payload.FromStream()就行,但Streetpass一般都是小数据,字节数组完全够用。
5. 断开连接和资源清理
当玩家离开范围或退出游戏时,记得清理资源:
public void StopAllStreetpassActions() { Nearby.Connections.StopAdvertising(); Nearby.Connections.StopDiscovery(); Nearby.Connections.DisconnectFromAllEndpoints(); } void OnApplicationQuit() { StopAllStreetpassActions(); }
三、避坑指南(我踩过的那些破雷)
- 权限问题:Android 12+除了Manifest里的权限,必须动态申请
ACCESS_FINE_LOCATION、BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE,不然Nearby直接罢工。 - 测试环境:一定要用两台真实的Android设备测试!模拟器不支持Nearby的蓝牙/WiFi直连功能,我用模拟器测了半天没反应,换真机一秒就连上了。
- 序列化一致性:两台设备的序列化/反序列化逻辑必须完全一致,比如你用JsonUtility,对方也得用同样的方式解析,不然会出现数据乱码或者解析失败。
- 版本兼容:如果遇到API调用报错,先检查Nearby包的版本和Unity版本是否兼容,有时候降一个包版本就能解决问题。
最后
其实核心逻辑就是「初始化→找设备→连设备→传数据」,虽然官方文档确实拉胯,但把这些步骤跑通后,再套上你的游戏UI和剧情逻辑,完全能实现你要的Streetpass效果!要是中间卡在哪一步,随时喊我,我再给你捋细节~




