using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using ArduinoBluetoothAPI; using DG.Tweening; using UnityEngine.SceneManagement; using System.Linq; using BestHTTP.WebSocket; using SmartBowSDK; /* 射箭检测 */ public class ShootCheck : MonoBehaviour { [SerializeField] Text text; public CMD cmd = new CMD(); bool locked = false; float maxAcc = 0; Queue keyAccList = new Queue(); Queue keyTimeList = new Queue(); public float shootSpeed; public static ShootCheck ins; [SerializeField] InputField ipInputField = default; public WebSocket webSocket; void Awake() { ins = this; } //用户输入时的变化 public void ChangedValue(string value) { ipInputField.ActivateInputField(); Debug.Log("输入了" + value); } public void EndValue(string value) { Debug.Log("最终内容" + value); } //socket public void StartSocket() { //socket string ipStr = ipInputField.text;//ipInputField.GetComponentInChildren(); string serverIP = ipStr; // serverIP = "172.16.20.57"; if (serverIP.Length == 0) serverIP = "192.168.101.14"; string address = "ws://" + serverIP + ":8088/Ble/"; webSocket = new WebSocket(new Uri(address)); #if !UNITY_WEBGL webSocket.StartPingThread = true; #endif // Subscribe to the WS events webSocket.OnOpen += OnOpen; webSocket.OnMessage += OnMessageRecv; webSocket.OnBinary += OnBinaryRecv; webSocket.OnClosed += OnClosed; webSocket.OnError += OnError; // Debug.Log("OnOpen: "); // Start connecting to the server webSocket.Open(); } public void Destroy() { if (webSocket != null) { webSocket.Close(); webSocket = null; } } void OnOpen(WebSocket ws) { Debug.Log("OnOpen: "); webSocket.Send("unity"); } void OnMessageRecv(WebSocket ws, string message) { Debug.LogFormat("OnMessageRecv: msg={0}", message); } void OnBinaryRecv(WebSocket ws, byte[] data) { Debug.LogFormat("OnBinaryRecv: len={0}", data.Length); } void OnClosed(WebSocket ws, UInt16 code, string message) { Debug.LogFormat("OnClosed: code={0}, msg={1}", code, message); webSocket = null; } void OnError(WebSocket ws, Exception ex) { string errorMsg = string.Empty; #if !UNITY_WEBGL || UNITY_EDITOR if (ws.InternalRequest.Response != null) { errorMsg = string.Format("Status Code from Server: {0} and Message: {1}", ws.InternalRequest.Response.StatusCode, ws.InternalRequest.Response.Message); } #endif Debug.LogFormat("OnError: error occured: {0}\n", (ex != null ? ex.Message : "Unknown Error " + errorMsg)); webSocket = null; } //socket [SerializeField] InputField ArmBowInputField = default; public void SetShootBackTime() { ArmBow.ins.shootBackTime = int.Parse(ArmBowInputField.text); } public void OnBluetoothReady(BluetoothShoot bluetoothShoot) { // if (LoginMgr.myUserInfo.arrowAccValue == 16) // { // cmd.a = "y"; // } // else // { // cmd.a = "x"; // } // bluetoothShoot.WriteData(JsonUtility.ToJson(cmd).Replace("\"", "")); // Sequence sequence = DOTween.Sequence(); // sequence.PrependInterval(1).AppendCallback(delegate() { // canAdjustNormalOrHightMode = true; // AdjustNormalOrHightMode(); // }); // sequence.SetUpdate(true); } //===普通模式和高速模式的切换=== public bool canAdjustNormalOrHightMode = false; public int transportMode = 0; public void AdjustNormalOrHightMode() { if (!canAdjustNormalOrHightMode) return; try { // string sceneName = SceneManager.GetActiveScene().name; // if (sceneName == "Game") // { // transportMode = 1; // BluetoothShoot.ins.WriteData("HA"); // } // else // { // transportMode = 0; // BluetoothShoot.ins.WriteData("NA"); // } transportMode = 0; BluetoothShoot.ins.WriteData("NA"); } catch (Exception) { } } public void OnDataReceived(byte[] bytes) { string str1 = "byte="; if (webSocket != null) { for (int i = 0; i < bytes.Length - 1; i++) { str1 += bytes[i]; } // webSocket.Send(str2); } string str2 = ""; if (transportMode == 0) { for (int i = 0; i < bytes.Length / 6; i++) { float acc = ToAcceleratedSpeed(bytes[i * 6 + 4], bytes[i * 6 + 5]); string t = "(采样时间:" + (int)bytes[i * 6 + 0] + "分" + (int)bytes[i * 6 + 1] + "秒" + TwoByteToInt(bytes[i * 6 + 2], bytes[i * 6 + 3]) + "毫秒)"; str2 += "加速度:" + acc + t + "\n"; if (ins.check(0, acc, 0, t) && ArmBow.ins) { ArmBow.ins.ADS_fire(true); } } } else if (transportMode == 1) { for (int i = 0; i < bytes.Length / 11; i++) { float ax = ToAcceleratedSpeed(bytes[i * 11 + 5], bytes[i * 11 + 6]); float ay = ToAcceleratedSpeed(bytes[i * 11 + 7], bytes[i * 11 + 8]); float az = ToAcceleratedSpeed(bytes[i * 11 + 9], bytes[i * 11 + 10]); string t = "(采样时间:" + (int)bytes[i * 11 + 3] + "分" + (int)bytes[i * 11 + 4] + "秒" + TwoByteToInt(bytes[i * 11 + 1], bytes[i * 11 + 2]) + "毫秒)"; str2 += "加速度:" + ay + t + "\n"; if (ins.check(ax, ay, az, t) && ArmBow.ins) { ArmBow.ins.ADS_fire(true); } } } if (webSocket != null) { string str3 = str1 + "\n" + str2; webSocket.Send(str3); } } float ToAcceleratedSpeed(byte b1, byte b2) { int value = TwoByteToInt(b1, b2); // return (float)value / 32768 * LoginMgr.myUserInfo.arrowAccValue; return (float)value / 32768; } int TwoByteToInt(byte b1, byte b2) { ushort twoByte = (ushort)(b1 * 256 + b2); short shortNum = (short)twoByte; return (int)shortNum; } bool check(float ax, float ay, float az, string t) { float acc = ay; DebugLine.show(acc); //这个不需要注释,静态函数内置判断 if (locked) { return false; } if (acc > cmd.getAcc()) { if (acc > maxAcc) { maxAcc = acc; } // if (acc > 15.9f && LoginMgr.myUserInfo.arrowAccValue == 16) { // double p1 = -1.56729339506415; // double p2 = 0.0397744840580165; // double p3 = 4.73453844008481; // float x = (keyAccList.Count + 1) * 2; //单位毫秒 // double y = 1.0 / (p1+p2*Mathf.Pow(x, 0.5f)*Mathf.Log(x)+p3/Mathf.Pow(x, 0.5f)); // acc = (float) y; // } Vector3 keyAcc = new Vector3(ax, acc, az); keyAccList.Enqueue(keyAcc); keyTimeList.Enqueue(t); return false; } else if (acc < cmd.getAcc() && maxAcc != 0) { //积分求初速度 shootSpeed = 0; float lasKeytAcc = 0; int keyAccIndex = 0; float timeInterval = 0.002f; float totalAx = 0; float totalAy = 0; float totalAz = 0; foreach (var keyAcc in keyAccList) { totalAx += Mathf.Abs(keyAcc.x); totalAy += Mathf.Abs(keyAcc.y); totalAz += Mathf.Abs(keyAcc.z); if (keyAccIndex > 0) { shootSpeed += keyAcc.y * timeInterval; shootSpeed -= (keyAcc.y - lasKeytAcc) * timeInterval / 2; } else if (keyAccIndex == 0 && keyAccList.Count == 1) { shootSpeed = keyAcc.y * timeInterval; } lasKeytAcc = keyAcc.y; keyAccIndex++; } //是不是合法射出 bool isLegalShoot = totalAy > totalAx && totalAy > totalAz; if (isLegalShoot) { //加速度acc的单位是g,最后需要乘上 shootSpeed *= 9.80665f; string strShootSpeed = "弓轨速度: " + shootSpeed + " 帧数: " + keyAccList.Count + "\n"; // shootSpeed = Mathf.Sqrt(shootSpeed * shootSpeed * arrowWeight / LoginMgr.myUserInfo.actualArrowWeight); strShootSpeed += "箭的速度: " + shootSpeed + "\n"; string str1 = strShootSpeed + "/////////检测到射出的数据////////////:\n"; for (int i = 0; i < keyAccList.Count; i++) { float keyAcc = keyAccList.ElementAt(i).y; string time = keyTimeList.ElementAt(i); str1 += "加速度:" + keyAcc + time + "\n"; } Debug.LogWarning(str1); if (webSocket != null) { webSocket.Send(str1 + "/////////检测到射出的数据////////////:\n"); } } //本轮计算结束 keyAccList.Clear(); keyTimeList.Clear(); maxAcc = 0; Dolock(); Invoke("Unlock", 1.8f); return isLegalShoot; } return false; } [NonSerialized] public byte byteTime1; [NonSerialized] public byte byteTime2; //目前只处理m4 private int _lastHandledId = -1; private float _lastHandledTime = 0f; private const float duplicateIgnoreWindow = 0.1f; // 可调:忽略窗口(单位秒) /**通过红外线数据进行射击 */ public void ShootByInfrared(byte[] bytes) { int id = bytes[1]; //序号 float now = Time.time; // 判断是否是重复射击 if (id == _lastHandledId && (now - _lastHandledTime) < duplicateIgnoreWindow) { // 相同序号 + 窗口时间内,不处理 return; } // 记录本次处理状态 _lastHandledId = id; _lastHandledTime = now; byteTime1 = bytes[2]; byteTime1 = bytes[3]; float time1 = bytes[2] * 0.1f; //时区1耗时 float time2 = bytes[3] * 0.1f; //时区2耗时 float totalTime = time1 + time2; // if (totalTime <= 0) { // totalTime = 0.3f; // } //校验和 int sumCheck = bytes[0] + bytes[1] + bytes[2] + bytes[3]; sumCheck &= 0xff; //校验和比较结果 bool sumCheckRes = sumCheck == bytes[4]; //弓轨速度 float speed = 0.05f / (totalTime / 1000f); //通过动能定理求箭的速度(实体箭质量*实体箭速度^2=游戏中箭的质量*游戏中箭的速度^2) shootSpeed = Mathf.Sqrt(speed * speed * CommonConfig.arrowWeight / UserSettings.ins.actualArrowWeight); //打印 string logTxt = $"序号{id},时区1:{time1}毫秒,时区2:{time2}毫秒,校验:{sumCheckRes},弓轨速度:{speed}m/s,箭的速度:{shootSpeed}m/s"; if (DebugForDevice.ins) DebugForDevice.ins.LogInfrared(logTxt); //Debug.Log(logTxt); //收到射箭数据,就回复硬件,否则n毫秒后硬件会认为丢包进行重传 //try { // if (sumCheckRes) BluetoothAim.ins.ReplyInfraredShoot(); //如果数据正确,则回复硬件 //} catch (Exception) { } //打印到nodejs try { ShootCheck.ins.webSocket.Send(logTxt); } catch (Exception) { } //调用游戏中的射箭接口 AimHandler.ins.NotifyAxisOnShot(); //OnArmBowFire,用于新手提示 GameRuleView //OnArmBowFire?.Invoke(shootSpeed); if (SB_EventSystem.ins && SB_EventSystem.ins.simulateMouseIsAwaked) { SB_EventSystem.ins.ClickMouse(); } else if (ArmBow.ins) { ArmBow.ins.ADS_fire(true); } else if (DuckHunter.SmartBowController.Instance) { //野鸭射击 DuckHunter.SmartBowController.Instance.OnShooting(shootSpeed); } else if (WildAttack.SmartBowController.Instance) { //荒野射击 WildAttack.SmartBowController.Instance.OnShooting(shootSpeed); } else if (GameController.ins && GameController.ins.GetArmBowDoublePlayer(PlayerType.FirstPlayer) != null) { //本地双人模式下处理1p,2P 在 BluetoothAim 类处理 GameController.ins.GetArmBowDoublePlayer(PlayerType.FirstPlayer).ADS_fire(true, shootSpeed); } else if (GeneratingTarget.gm != null) { //移动目标 GeneratingTarget.gm.Shooting(true); } else { OnGameShoot?.Invoke(shootSpeed); } } public Action OnGameShoot; //分离弹夹事件 public Action OnGameUpdateTheMagazine; //弹夹状态 public BluetoothDeviceStatus bluetoothDeviceStatus = BluetoothDeviceStatus.MagazineLoading; /// /// 0x00 - 弹夹分离 0x01 - 弹夹上膛 /// /// public void UpdateTheMagazine(byte[] bytes) { BluetoothDeviceType temp = bluetoothDeviceType != BluetoothDeviceType.NONE ? bluetoothDeviceType : BluetoothDeviceType.Pistol1; if (bytes[1] == 0x00) { //添加一个同步状态 BluetoothAim.ins.InvokeOnBleDevice(temp, BluetoothDeviceStatus.MagazineSeparation); Debug.Log("弹夹分离:" + BitConverter.ToString(bytes)); bluetoothDeviceStatus = BluetoothDeviceStatus.MagazineSeparation; if (GeneratingTarget.gm) { //Hyperspace GeneratingTarget.gm.OnSeparation(); } } else if ( bytes[1] == 0x01){ //添加一个同步状态 BluetoothAim.ins.InvokeOnBleDevice(temp, BluetoothDeviceStatus.MagazineLoading); //播放上弹夹时候的声音 AudioMgr.ins.PlayBeLoaded(); Debug.Log("弹夹上膛:" + BitConverter.ToString(bytes)); bluetoothDeviceStatus = BluetoothDeviceStatus.MagazineLoading; if (Billboard.ins) { Billboard.ins.bulletManager.ResetBullets(); //game } else if (GeneratingTarget.gm) { //Hyperspace GeneratingTarget.gm.OnLoading(); } else { OnGameUpdateTheMagazine?.Invoke(); //水果在gamingmanager } } } //public Action OnArmBowFire; void Dolock() { locked = true; } void Unlock() { locked = false; } //记录当前的蓝牙设备信息 private BluetoothDeviceType bluetoothDeviceType = BluetoothDeviceType.NONE; //记录连接的设备系统信息 private ConnectPlatform connectPlatform = ConnectPlatform.NONE; /// /// 0x00 未上膛,0x01 已上膛 /// /// public void UpdateChamberState(byte[] bytes) { Debug.Log("操作上膛状态:" + BitConverter.ToString(bytes)); BluetoothDeviceType temp = bluetoothDeviceType != BluetoothDeviceType.NONE ? bluetoothDeviceType : BluetoothDeviceType.Pistol1; if (bytes[1] == 0x00) { BluetoothAim.ins.InvokeOnBleDevice(temp, BluetoothDeviceStatus.ChamberEmpty); } else if (bytes[1] == 0x01) { BluetoothAim.ins.InvokeOnBleDevice(temp, BluetoothDeviceStatus.Chambered); } } /// /// 记录当前获得的设备连接数据 /// /// public void UpdateDeviceAndSysInfo(byte[] bytes) { //Debug.Log("接收到系统数据:" + System.BitConverter.ToString(bytes)); //设备类型 switch (bytes[1]) { case 0x01: bluetoothDeviceType = BluetoothDeviceType.HOUYIPro; break; case 0x02: bluetoothDeviceType = BluetoothDeviceType.ARTEMISPro; break; case 0x03: bluetoothDeviceType = BluetoothDeviceType.PistolM9; break; case 0x04: bluetoothDeviceType = BluetoothDeviceType.APOLLO; break; case 0x05: bluetoothDeviceType = BluetoothDeviceType.PistolM17; break; case 0x06: bluetoothDeviceType = BluetoothDeviceType.RifleM416; break; } // 系统类型 switch (bytes[2]) { case 0x01: connectPlatform = ConnectPlatform.PHONE; break; case 0x02: connectPlatform = ConnectPlatform.PC; break; case 0x03: connectPlatform = ConnectPlatform.VR; break; } Debug.Log("设备类型:" + bluetoothDeviceType.ToString()); Debug.Log("系统类型:" + connectPlatform.ToString()); //初始化时候回调数据 BluetoothAim.ins.InvokeOnDeviceAndSystemInfoEvent(connectPlatform, bluetoothDeviceType); } void Log(string text) { if (this.text) { this.text.text = text; } else { Debug.Log(text); } } } [Serializable] public class CMD { // public string ax = "y"; // public int a = 6000; // public int r = 2; public string a = "y"; public int a1 = 3; public int a2 = -3; public int r = 2; public float getAcc() { return a1; } }