> 技术文档 > unity 使用蓝牙通讯(PC版,非安卓)_unity蓝牙通信

unity 使用蓝牙通讯(PC版,非安卓)_unity蓝牙通信


BlueTooth in pc with unity

最近接到的需求是在unity里面开发蓝牙功能,其实一开始我并不慌,因为据我所知,unity有丰富的插件可以使用,但是问题随之而来
1.unity里面无法直接与蓝牙通讯(后来找到了开启runtime一类的东西,但是我找了半天也没找到在哪里可以打开)
2.引入dll通过dll与蓝牙通讯,包括去微软的官网下载c++编译,但是编译过程中种种问题.而我对于c++这一套也不精通.也是放弃了
3.github上找到一个封装好的BLE的一个 这是网址 但是在我的测试中发现 会有搜不到我的蓝牙设备的问题.并且即使连上了,发出的消息也没有回应.所以也是放弃了

于是只能通过一个比较笨的办法,将蓝牙通讯在winform中或者是wpf中,然后将unity程序放在winform或者是wpf的容器中,二者通过udp协议连接传输数据.最后的效果如下:

unity 使用蓝牙通讯(PC版,非安卓)_unity蓝牙通信

那么如果是这种办法,那就变得简单的多了.winform开发不在话下.unity也是我熟悉的.其中winform支持原生开发,也就是说不用下载任何dll与插件,用到的只是windows.winmd,而这个windows系统是自带的.

将winmd类型文件引入后,编写开发工具类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Windows.Devices.Bluetooth;using Windows.Devices.Bluetooth.Advertisement;using Windows.Devices.Bluetooth.GenericAttributeProfile;using Windows.Devices.Enumeration;using Windows.Foundation;using Windows.Networking;using Windows.Networking.Proximity;using Windows.Networking.Sockets;using Windows.Security.Cryptography;using Windows.Storage.Streams;namespace Bluetooth{ internal class BleCore { private Boolean asyncLock = false; ///  /// 搜索蓝牙设备对象 ///  private BluetoothLEAdvertisementWatcher watcher; ///  /// 当前连接的蓝牙设备 ///  public BluetoothLEDevice CurrentDevice { get; set; } ///  /// 特性通知类型通知启用 ///  private const GattClientCharacteristicConfigurationDescriptorValue CHARACTERISTIC_NOTIFICATION_TYPE = GattClientCharacteristicConfigurationDescriptorValue.Notify; ///  /// 存储检测到的设备 ///  private List<BluetoothLEDevice> DeviceList = new List<BluetoothLEDevice>(); ///  /// 搜索蓝牙设备委托 ///  public delegate void DeviceScanEvent(BluetoothLEDevice bluetoothLEDevice); ///  /// 搜索蓝牙事件 ///  public event DeviceScanEvent DeviceScan; ///  /// 提示信息委托 ///  public delegate void MessAgeLogEvent(MsgType type, string message, byte[] data = null); ///  /// 提示信息事件 ///  public event MessAgeLogEvent MessAgeLog; ///  /// 接收通知委托 ///  public delegate void ReceiveNotificationEvent(GattCharacteristic sender, byte[] data); ///  /// 接收通知事件 ///  public event ReceiveNotificationEvent ReceiveNotification; ///  /// 获取服务委托 ///  public delegate void DeviceFindServiceEvent(DeviceService deviceService); ///  /// 获取服务事件 ///  public event DeviceFindServiceEvent DeviceFindService; ///  /// 蓝牙状态委托 ///  public delegate void DeviceConnectionStatusEvent(BluetoothConnectionStatus status); ///  /// 蓝牙状态事件 ///  public event DeviceConnectionStatusEvent DeviceConnectionStatus; ///  /// 当前连接的蓝牙Mac ///  private string CurrentDeviceMAC { get; set; } public BleCore() { } ///  /// 搜索蓝牙设备 ///  public void StartBleDeviceWatcher() { watcher = new BluetoothLEAdvertisementWatcher(); watcher.ScanningMode = BluetoothLEScanningMode.Active; //只有在接收到数值大于等于 -80 的情况下才激活监视器 watcher.SignalStrengthFilter.InRangeThresholdInDBm = -80; // 如果数值低于 -90(用户离开),则停止监测 watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -90; // 注册回调函数,以便在我们看到广播时触发(执行)相关操作 watcher.Received += OnAdvertisementReceived; // 等待 5 秒钟以确保设备确实超出范围 watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000); watcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000); // starting watching for advertisements this.DeviceList.Clear(); watcher.Start(); string msg = \"开始搜索蓝牙设备...\"; this.MessAgeLog(MsgType.NotifyTxt, msg); } ///  /// 停止搜索蓝牙 ///  public void StopBleDeviceWatcher() { this.watcher.Stop(); } private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs) { //this.MessAgeChanged(MsgType.NotifyTxt, \"发现设备FR_NAME:\"+ eventArgs.Advertisement.LocalName + \"BT_ADDR: \" + eventArgs.BluetoothAddress); BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress).Completed = (asyncInfo, asyncStatus) => { if (asyncStatus == AsyncStatus.Completed) {  if (asyncInfo.GetResults() != null)  { BluetoothLEDevice currentDevice = asyncInfo.GetResults(); Boolean contain = false; foreach (BluetoothLEDevice device in DeviceList)//过滤重复的设备 { if (device.BluetoothAddress == currentDevice.BluetoothAddress) { contain = true; } } if (!contain) { byte[] _Bytes1 = BitConverter.GetBytes(currentDevice.BluetoothAddress); Array.Reverse(_Bytes1); this.DeviceList.Add(currentDevice); string str; if (currentDevice.DeviceInformation.Name != \"\") { str = \"发现设备:\" + currentDevice.DeviceInformation.Name + \" - MAC: \\r\\n\" + BitConverter.ToString(_Bytes1, 2, 6).Replace(\'-\', \':\').ToUpper() + \" - ID: /r/n\" + currentDevice.DeviceInformation.Id; } //舍弃没有名字的设备(匿名设备) else { // str = \"发现设备:\" + BitConverter.ToString(_Bytes1, 2, 6).Replace(\'-\', \':\').ToUpper(); return; } this.MessAgeLog(MsgType.NotifyTxt, str); this.DeviceScan(currentDevice); }  } } }; } ///  /// 匹配 ///  ///  public void StartMatching(BluetoothLEDevice Device) { this.CurrentDevice?.Dispose(); this.CurrentDevice = Device; Connect(); FindService(); } ///  /// 获取蓝牙服务 ///  public async void FindService() { this.MessAgeLog(MsgType.NotifyTxt, \"开始获取服务列表\"); this.CurrentDevice.GetGattServicesAsync().Completed = async (asyncInfo, asyncStatus) => { if (asyncStatus == AsyncStatus.Completed) {  var services = asyncInfo.GetResults().Services;  //this.MessAgeChanged(MsgType.NotifyTxt, \"GattServices size=\" + services.Count);  foreach (GattDeviceService ser in services)  { FindCharacteristic(ser);  } } }; } ///  /// 获取特性 ///  public async void FindCharacteristic(GattDeviceService gattDeviceService) { IAsyncOperation<GattCharacteristicsResult> result = gattDeviceService.GetCharacteristicsAsync(); result.Completed = async (asyncInfo, asyncStatus) => { if (asyncStatus == AsyncStatus.Completed) {  var characters = asyncInfo.GetResults().Characteristics;  List<GattCharacteristic> characteristics = new List<GattCharacteristic>();  foreach (GattCharacteristic characteristic in characters)  { characteristics.Add(characteristic); this.MessAgeLog(MsgType.NotifyTxt, \"服务UUID:\" + gattDeviceService.Uuid.ToString() + \" --- 特征UUID:\" + characteristic.Uuid.ToString());  }  DeviceService deviceService = new DeviceService();  deviceService.gattDeviceService = gattDeviceService;  deviceService.gattCharacteristic = characteristics;  this.DeviceFindService(deviceService); } }; } ///  /// 获取操作 ///  ///  public async Task SetNotify(GattCharacteristic gattCharacteristic) { GattCharacteristic CurrentNotifyCharacteristic; if ((gattCharacteristic.CharacteristicProperties & GattCharacteristicProperties.Notify) != 0) { CurrentNotifyCharacteristic = gattCharacteristic; CurrentNotifyCharacteristic.ProtectionLevel = GattProtectionLevel.Plain; CurrentNotifyCharacteristic.ValueChanged += Characteristic_ValueChanged; await this.EnableNotifications(CurrentNotifyCharacteristic); } } ///  /// 连接蓝牙 ///  ///  private async Task Connect() { byte[] _Bytes1 = BitConverter.GetBytes(this.CurrentDevice.BluetoothAddress); Array.Reverse(_Bytes1); this.CurrentDeviceMAC = BitConverter.ToString(_Bytes1, 2, 6).Replace(\'-\', \':\').ToLower(); string msg = \"正在连接设备:\" + this.CurrentDeviceMAC.ToUpper() + \" ...\"; this.MessAgeLog(MsgType.NotifyTxt, msg); this.CurrentDevice.ConnectionStatusChanged += this.CurrentDevice_ConnectionStatusChanged; } ///  /// 搜索到的蓝牙设备 ///  ///  private async Task Matching(string Id) { try { BluetoothLEDevice.FromIdAsync(Id).Completed = async (asyncInfo, asyncStatus) => {  if (asyncStatus == AsyncStatus.Completed)  { BluetoothLEDevice bleDevice = asyncInfo.GetResults(); this.DeviceList.Add(bleDevice); this.DeviceScan(bleDevice); this.CurrentDevice = bleDevice; FindService();  } }; } catch (Exception e) { string msg = \"没有发现设备\" + e.ToString(); this.MessAgeLog(MsgType.NotifyTxt, msg); this.StartBleDeviceWatcher(); } } ///  /// 主动断开连接 ///  ///  public void Dispose() { CurrentDeviceMAC = null; CurrentDevice?.Dispose(); CurrentDevice = null; MessAgeLog(MsgType.NotifyTxt, \"主动断开连接\"); } private void CurrentDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args) { this.DeviceConnectionStatus(sender.ConnectionStatus); if (sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected && CurrentDeviceMAC != null) { string msg = \"设备已断开,自动重连...\"; MessAgeLog(MsgType.NotifyTxt, msg); if (!asyncLock) {  asyncLock = true;  this.CurrentDevice.Dispose();  this.CurrentDevice = null;  SelectDeviceFromIdAsync(CurrentDeviceMAC); } } else { string msg = \"设备已连接!\"; MessAgeLog(MsgType.NotifyTxt, msg); } } ///  /// 按MAC地址直接组装设备ID查找设备 ///  public async Task SelectDeviceFromIdAsync(string MAC) { CurrentDeviceMAC = MAC; CurrentDevice = null; BluetoothAdapter.GetDefaultAsync().Completed = async (asyncInfo, asyncStatus) => { if (asyncStatus == AsyncStatus.Completed) {  BluetoothAdapter mBluetoothAdapter = asyncInfo.GetResults();  byte[] _Bytes1 = BitConverter.GetBytes(mBluetoothAdapter.BluetoothAddress);//ulong转换为byte数组  Array.Reverse(_Bytes1);  string macAddress = BitConverter.ToString(_Bytes1, 2, 6).Replace(\'-\', \':\').ToLower();  string Id = \"BluetoothLE#BluetoothLE\" + macAddress + \"-\" + MAC;  await Matching(Id); } }; } ///  /// 设置特征对象为接收通知对象 ///  ///  ///  public async Task EnableNotifications(GattCharacteristic characteristic) { if (CurrentDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) { this.MessAgeLog(MsgType.NotifyTxt, \"蓝牙未连接!\"); return; } characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(CHARACTERISTIC_NOTIFICATION_TYPE).Completed = async (asyncInfo, asyncStatus) => { Console.WriteLine(\"asyncStatus:\" + asyncStatus); if (asyncStatus == AsyncStatus.Completed) {  GattCommunicationStatus status = asyncInfo.GetResults();  asyncLock = false;  string msg = \"Notify(\" + characteristic.Uuid.ToString() + \"):\" + status;  this.MessAgeLog(MsgType.NotifyTxt, msg); } else {  Console.WriteLine(asyncInfo.ErrorCode.ToString()); } }; } ///  /// 接受到蓝牙数据 ///  private void Characteristic_ValueChanged(GattCharacteristic characteristic, GattValueChangedEventArgs args) { byte[] data; CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data); string str = System.Text.Encoding.UTF8.GetString(data); this.ReceiveNotification(characteristic, data); this.MessAgeLog(MsgType.BleData, \"收到数据(\" + characteristic.Uuid.ToString() + \"):\" + str); } ///  /// 发送数据接口 ///  ///  public async Task Write(GattCharacteristic writeCharacteristic, byte[] data) { if (writeCharacteristic != null) { string str = \"发送数据(\" + writeCharacteristic.Uuid.ToString() + \"):\" + BitConverter.ToString(data); this.MessAgeLog(MsgType.BleData, str, data); IAsyncOperation<GattCommunicationStatus> async = writeCharacteristic.WriteValueAsync(CryptographicBuffer.CreateFromByteArray(data), GattWriteOption.WriteWithResponse); async.Completed = async (asyncInfo, asyncStatus) => {  if (asyncStatus == AsyncStatus.Completed)  { this.MessAgeLog(MsgType.BleData, \"数据发送成功!\");  }  else  { this.MessAgeLog(MsgType.BleData, \"数据发送失败:\" + asyncInfo.ErrorCode.ToString());  } }; } } } public enum MsgType { NotifyTxt, BleData, BleDevice } public class DeviceService { public GattDeviceService gattDeviceService; public List<GattCharacteristic> gattCharacteristic; }}

然后对于页面的按钮进行赋值,并将unity开发的exe放在合适的位置,其中通讯部分只需要简单的通讯即可,不需要考虑什么分包问题.因为本机通讯很稳定.
winform部分的udp:

using System;using System.Diagnostics;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;namespace VincentUDP{ public class UdpServer { private UdpClient udpClient; private IPEndPoint remoteEndPoint; public void Init() { int port = 8888; // 选择一个端口 udpClient = new UdpClient(); remoteEndPoint = new IPEndPoint(IPAddress.Parse(\"127.0.0.1\"), port); // 目标IP和端口 } public void Send(byte[] message) { // byte[] data = Encoding.UTF8.GetBytes(message); udpClient.Send(message, message.Length, remoteEndPoint); } public void Close() { udpClient.Close(); } }}

unity接收部分:

using System;using System.Collections;using System.Net;using System.Net.Sockets;using UnityEngine;public class RecvUDPMsg : MonoBehaviour{ private UdpClient udpClient; //消息队列 private Queue queue = new Queue(); private void Start() { // 初始化UDP客户端 udpClient = new UdpClient(8888); // 监听的端口 StartReceiving(); } private void StartReceiving() { udpClient.BeginReceive(ReceiveCallback, null); } private void ReceiveCallback(IAsyncResult ar) { IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] receivedBytes = udpClient.EndReceive(ar, ref remoteIpEndPoint); // string receivedText = Encoding.UTF8.GetString(receivedBytes); //Debug.Log(\"Received: \" + receivedText); queue.Enqueue(receivedBytes); // 继续监听 StartReceiving(); } private void OnDestroy() { // 关闭UDP客户端 udpClient.Close(); }}

那么最后的运行如下:
unity 使用蓝牙通讯(PC版,非安卓)_unity蓝牙通信
最后附上下载地址: 点击这里
PS:如果下载不了(非csdn会员)请私信我.