using System; using System.Collections; using System.Collections.Generic; using Kill.UI.Components; using UnityEngine; using UnityEngine.UI; namespace Kill.UI.Pages { /// /// 二维码扫描器 - 调用摄像头扫描二维码 /// public class ScanQRcode : MonoBehaviour { [Header("UI组件")] public RawImage cameraPreview; // 摄像头预览 [Header("扫描设置")] public float scanInterval = 0.2f; // 扫描间隔(秒) // 摄像头相关 private WebCamTexture webCamTexture; private bool isScanning = false; // 扫描结果回调 public event Action OnQRCodeScanned; void OnEnable() { StartScan(); } void OnDisable() { StopScan(); } /// /// 开始扫描 /// public void StartScan() { if (isScanning) return; StartCoroutine(InitializeCamera()); } /// /// 停止扫描 /// public void StopScan() { isScanning = false; StopAllCoroutines(); if (webCamTexture != null && webCamTexture.isPlaying) { webCamTexture.Stop(); webCamTexture = null; } } /// /// 关闭扫码界面 /// public void Close() { StopScan(); gameObject.SetActive(false); } /// /// 初始化摄像头 /// private IEnumerator InitializeCamera() { // Android 使用 Permission API 请求权限 #if UNITY_ANDROID && !UNITY_EDITOR if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.Camera)) { UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.Camera); // 等待权限请求结果 yield return new WaitUntil(() => UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.Camera)); } if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.Camera)) { UpdateStatus("100085"); yield break; } #else // 其他平台使用传统方式 yield return Application.RequestUserAuthorization(UserAuthorization.WebCam); if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) { UpdateStatus("100085"); yield break; } #endif // 获取后置摄像头 WebCamDevice[] devices = WebCamTexture.devices; if (devices.Length == 0) { UpdateStatus("100086"); yield break; } // 优先使用后置摄像头 string deviceName = devices[0].name; for (int i = 0; i < devices.Length; i++) { if (!devices[i].isFrontFacing) { deviceName = devices[i].name; break; } } Vector2 size=GetComponent().sizeDelta; // 创建摄像头纹理 webCamTexture = new WebCamTexture(deviceName, (int)size.x, (int)size.y, 30); cameraPreview.texture = webCamTexture; // 开始播放 webCamTexture.Play(); // 等待摄像头启动 yield return new WaitUntil(() => webCamTexture.width > 100); // 调整预览画面比例 AdjustPreviewAspect(); isScanning = true; // 开始扫描 StartCoroutine(ScanCoroutine()); } /// /// 调整预览画面比例 /// private void AdjustPreviewAspect() { if (webCamTexture == null) return; float videoRatio = (float)webCamTexture.width / webCamTexture.height; float screenRatio = (float)Screen.width / Screen.height; // 根据摄像头方向调整 int rotation = webCamTexture.videoRotationAngle; cameraPreview.rectTransform.localEulerAngles = new Vector3(0, 0, -rotation); // 调整缩放以适应屏幕 if (rotation == 90 || rotation == 270) { videoRatio = 1f / videoRatio; } // 保持比例填充 if (videoRatio > screenRatio) { cameraPreview.rectTransform.localScale = new Vector3(screenRatio / videoRatio, 1, 1); } else { cameraPreview.rectTransform.localScale = new Vector3(1, videoRatio / screenRatio, 1); } } /// /// 扫描协程 /// private IEnumerator ScanCoroutine() { // 等待一帧确保摄像头已准备好 yield return null; while (isScanning && webCamTexture != null && webCamTexture.isPlaying) { // 获取当前帧 Texture2D snapshot = GetSnapshot(); if (snapshot != null) { // 解析二维码 string result = DecodeQRCode(snapshot); Destroy(snapshot); if (!string.IsNullOrEmpty(result)) { // 从结果中提取 MAC 地址 string macAddress = ExtractMacAddress(result); if (!string.IsNullOrEmpty(macAddress)) { // 扫描成功,仅回传 MAC 地址 OnQRCodeScanned?.Invoke(macAddress); yield break; } else { // 二维码不符合要求,显示提示 UpdateStatus("100084"); } } } yield return new WaitForSeconds(scanInterval); } } /// /// 获取摄像头当前帧 /// private Texture2D GetSnapshot() { if (webCamTexture == null || !webCamTexture.isPlaying) return null; try { Texture2D snapshot = new Texture2D(webCamTexture.width, webCamTexture.height); snapshot.SetPixels(webCamTexture.GetPixels()); snapshot.Apply(); return snapshot; } catch { return null; } } /// /// 解析二维码 /// private string DecodeQRCode(Texture2D texture) { try { int width = texture.width; int height = texture.height; // 获取像素数据 Color32[] pixels = texture.GetPixels32(); // 转换为灰度图 byte[] grayscale = new byte[width * height]; for (int i = 0; i < pixels.Length; i++) { // 使用标准亮度公式: Y = 0.299R + 0.587G + 0.114B grayscale[i] = (byte)(0.299f * pixels[i].r + 0.587f * pixels[i].g + 0.114f * pixels[i].b); } // 创建 LuminanceSource var luminanceSource = new ZXing.PlanarYUVLuminanceSource( grayscale, width, height, 0, 0, width, height, false ); // 创建 BinaryBitmap var binarizer = new ZXing.Common.HybridBinarizer(luminanceSource); var binaryBitmap = new ZXing.BinaryBitmap(binarizer); // 解码 var reader = new ZXing.QrCode.QRCodeReader(); var hints = new Dictionary { { ZXing.DecodeHintType.TRY_HARDER, true } }; var result = reader.decode(binaryBitmap, hints); return result?.Text; } catch (Exception ex) { Debug.LogError($"二维码解析失败: {ex.Message}"); return null; } } /// /// 从字符串中提取 MAC 地址(格式如 98:EA:A0:02:4E:06) /// private string ExtractMacAddress(string input) { if (string.IsNullOrEmpty(input)) return null; // MAC 地址正则表达式:匹配 XX:XX:XX:XX:XX:XX 格式(X 为十六进制字符) System.Text.RegularExpressions.Regex macRegex = new System.Text.RegularExpressions.Regex(@"[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}"); System.Text.RegularExpressions.Match match = macRegex.Match(input); if (match.Success) { // 返回大写的 MAC 地址 return match.Value.ToUpper(); } return null; } /// /// 更新状态文字 /// private void UpdateStatus(string code) { ToastUI.Show(code); } void OnDestroy() { StopScan(); gameObject.SetActive(false); } } }