fix: 修复多处UI返回逻辑、优化扫码对焦与UI布局
1. 优化HomePage加载逻辑,修复加载弹窗重复显示问题 2. 重构蓝牙分享页、重命名页的返回事件绑定逻辑 3. 优化iOS/Android扫码对焦功能,支持多后置摄像头、软触发对焦 4. 调整扫码预览布局与参数,提升扫码识别率与适配性 5. 删除冗余的Android状态栏管理脚本与无用文件 6. 新增返回回调调试日志
This commit is contained in:
parent
e84d72dbf7
commit
432b80dfa3
File diff suppressed because it is too large
Load Diff
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd01dd9ee3681b4479d61a61f861ca73
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -4,16 +4,21 @@
|
||||
/// 获取当前正在使用的后置摄像头设备
|
||||
static AVCaptureDevice *GetActiveRearCamera(void)
|
||||
{
|
||||
// 遍历所有正在运行的 AVCaptureSession,找到后置摄像头的输入
|
||||
// 遍历所有 AVCaptureDevice,找到后置摄像头
|
||||
// 注意:Unity WebCamTexture 内部的 AVCaptureSession 不会暴露给原生代码,
|
||||
// 但 AVCaptureDevice 本身可以独立 lockForConfiguration 设置对焦模式,
|
||||
// 不需要成为当前 session 的输入设备。
|
||||
if (@available(iOS 10.0, *)) {
|
||||
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
|
||||
discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera]
|
||||
discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera,
|
||||
AVCaptureDeviceTypeBuiltInTelephotoCamera,
|
||||
AVCaptureDeviceTypeBuiltInDualCamera,
|
||||
AVCaptureDeviceTypeBuiltInDualWideCamera,
|
||||
AVCaptureDeviceTypeBuiltInTripleCamera]
|
||||
mediaType:AVMediaTypeVideo
|
||||
position:AVCaptureDevicePositionBack];
|
||||
for (AVCaptureDevice *d in session.devices) {
|
||||
if (d.deviceType == AVCaptureDeviceTypeBuiltInWideAngleCamera) {
|
||||
return d;
|
||||
}
|
||||
return d; // 拿到任意后置摄像头即可
|
||||
}
|
||||
}
|
||||
return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
@ -68,4 +73,11 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
|
||||
/// 周期性重新触发对焦(与 Android softTrigger 思路一致)
|
||||
/// 通过"点对焦 → 1.5s 后恢复连续对焦"的方式让镜头做一次完整对焦循环,无黑屏
|
||||
void _TriggerContinuousRefocus(void)
|
||||
{
|
||||
_TriggerAutoFocus(0.5f, 0.5f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1190,10 +1190,10 @@ RectTransform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1027440964492730408}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 345, y: -20.5}
|
||||
m_SizeDelta: {x: 640, y: 1}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 1}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3091012864071486640
|
||||
CanvasRenderer:
|
||||
@ -1272,7 +1272,7 @@ RectTransform:
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 61}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 1}
|
||||
--- !u!222 &3494079613403270933
|
||||
CanvasRenderer:
|
||||
@ -2219,6 +2219,7 @@ GameObject:
|
||||
- component: {fileID: 3037912135858668214}
|
||||
- component: {fileID: 7322899035469920849}
|
||||
- component: {fileID: 595539671003003557}
|
||||
- component: {fileID: 6470133651671008070}
|
||||
m_Layer: 5
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
@ -2294,14 +2295,65 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
cameraPreview: {fileID: 7322899035469920849}
|
||||
scanType: 0
|
||||
enableUpscale: 1
|
||||
upscaleFactor: 2
|
||||
scanResolutionWidth: 640
|
||||
useAsyncScan: 1
|
||||
centerCropRatio: 0.5
|
||||
enableAutoFocus: 1
|
||||
focusInterval: 1
|
||||
centerCropRatio: 0.4
|
||||
previewUpdateInterval: 0.03
|
||||
--- !u!114 &6470133651671008070
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2945323903071304015}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 0
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 7322899035469920849}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls:
|
||||
- m_Target: {fileID: 595539671003003557}
|
||||
m_TargetAssemblyTypeName: Kill.UI.Pages.ScanQRcode, Assembly-CSharp
|
||||
m_MethodName: TriggerRefocus
|
||||
m_Mode: 1
|
||||
m_Arguments:
|
||||
m_ObjectArgument: {fileID: 0}
|
||||
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
|
||||
m_IntArgument: 0
|
||||
m_FloatArgument: 0
|
||||
m_StringArgument:
|
||||
m_BoolArgument: 0
|
||||
m_CallState: 2
|
||||
--- !u!1 &2952991085794770192
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -4697,10 +4749,10 @@ RectTransform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 5409940142953436106}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 79, y: -24}
|
||||
m_SizeDelta: {x: 48, y: 48}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3550239897604221497
|
||||
CanvasRenderer:
|
||||
@ -6700,10 +6752,10 @@ RectTransform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 5409940142953436106}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 262, y: -24}
|
||||
m_SizeDelta: {x: 262, y: 42}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &9174153800022959653
|
||||
CanvasRenderer:
|
||||
@ -8222,7 +8274,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6952404845597796920
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@ -22,31 +22,17 @@ namespace Kill.UI.Pages
|
||||
[Header("UI组件")]
|
||||
public RawImage cameraPreview; // 摄像头预览
|
||||
[Header("扫描设置")]
|
||||
float scanInterval = 0.1f; // 扫描间隔(秒)
|
||||
float scanInterval = 0.05f; // 扫描间隔(秒),密集扫描提升远距离识别率
|
||||
public ScanType scanType = ScanType.ConnectDevice; // 扫描类型
|
||||
|
||||
[Header("小二维码优化")]
|
||||
[Tooltip("启用图像放大(推荐用于小二维码)")]
|
||||
public bool enableUpscale = true;
|
||||
[Tooltip("放大倍数")]
|
||||
public float upscaleFactor = 2f;
|
||||
|
||||
[Header("性能优化")]
|
||||
[Tooltip("降低扫描分辨率(推荐值:640或480)")]
|
||||
public int scanResolutionWidth = 1080;
|
||||
[Tooltip("扫描处理线程数")]
|
||||
public bool useAsyncScan = true;
|
||||
bool useAsyncScan = true;
|
||||
|
||||
[Header("中心裁切放大")]
|
||||
[Tooltip("截取画面中心区域的比例(0-1),值越小放越大,推荐0.55~0.7")]
|
||||
[Tooltip("截取画面中心区域的比例(0-1),值越小放越大。\n推荐:近距离 0.55~0.65;远距离扫码 0.8~0.9")]
|
||||
[Range(0.3f, 1.0f)]
|
||||
public float centerCropRatio = 0.65f;
|
||||
|
||||
[Header("自动对焦")]
|
||||
[Tooltip("启用自动对焦")]
|
||||
public bool enableAutoFocus = true;
|
||||
[Tooltip("对焦间隔(秒)")]
|
||||
public float focusInterval = 1f;
|
||||
float centerCropRatio = 1f;
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
[DllImport("__Internal")]
|
||||
@ -54,6 +40,16 @@ namespace Kill.UI.Pages
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _TriggerAutoFocus(float normalizedX, float normalizedY);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _TriggerContinuousRefocus();
|
||||
#endif
|
||||
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
// 通过反射拿到的 WebCamTexture 内部 Camera 实例(Unity 2022 LTS 一般拿不到,详见 EnableAndroidContinuousAutoFocus)
|
||||
private AndroidJavaObject androidCameraInstance;
|
||||
// 标记是否成功接管对焦控制
|
||||
private bool androidFocusControlAvailable = false;
|
||||
#endif
|
||||
|
||||
// 摄像头相关
|
||||
@ -75,19 +71,38 @@ namespace Kill.UI.Pages
|
||||
StopScan();
|
||||
}
|
||||
|
||||
void Update()
|
||||
// 手动重新对焦防抖时间:0.6s 内的重复点击只触发一次,避免过于频繁触发对焦
|
||||
private float lastManualRefocusTime = -10f;
|
||||
private const float ManualRefocusCooldown = 0.6f;
|
||||
|
||||
/// <summary>
|
||||
/// 用户手动点击预览时调用:触发一次软重新对焦(无黑屏,体验好)。
|
||||
/// 已接管 Camera 的设备走点对焦;未接管(Unity 2022 LTS 默认情况)走 autoFocusPoint 重新设置。
|
||||
/// </summary>
|
||||
public void TriggerRefocus()
|
||||
{
|
||||
if (!isScanning) return;
|
||||
if (Time.unscaledTime - lastManualRefocusTime < ManualRefocusCooldown) return;
|
||||
|
||||
lastManualRefocusTime = Time.unscaledTime;
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
if (isScanning && Input.touchCount > 0)
|
||||
// iOS:调用原生插件触发一次点对焦(1.5s 后原生自动恢复连续对焦,无黑屏)
|
||||
_TriggerContinuousRefocus();
|
||||
#elif UNITY_ANDROID && !UNITY_EDITOR
|
||||
if (androidFocusControlAvailable && androidCameraInstance != null)
|
||||
{
|
||||
Touch touch = Input.GetTouch(0);
|
||||
if (touch.phase == TouchPhase.Began)
|
||||
// 已接管:走正常的点对焦
|
||||
TriggerAndroidAutoFocus(0.5f, 0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float normX = touch.position.x / Screen.width;
|
||||
float normY = touch.position.y / Screen.height;
|
||||
_TriggerAutoFocus(normX, normY);
|
||||
}
|
||||
// 未接管:走软触发(无黑屏)
|
||||
Debug.Log("[ScanQRcode] 用户点击预览,触发软重新对焦");
|
||||
SoftTriggerAndroidFocus();
|
||||
}
|
||||
#else
|
||||
// 编辑器/其他平台:无操作
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -121,6 +136,16 @@ namespace Kill.UI.Pages
|
||||
Destroy(previewTexture);
|
||||
previewTexture = null;
|
||||
}
|
||||
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
// 反射拿到的 Camera 实例由 WebCamTexture 内部管理,仅清空引用即可
|
||||
if (androidCameraInstance != null)
|
||||
{
|
||||
androidCameraInstance.Dispose();
|
||||
androidCameraInstance = null;
|
||||
}
|
||||
androidFocusControlAvailable = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -182,8 +207,9 @@ namespace Kill.UI.Pages
|
||||
}
|
||||
}
|
||||
RectTransform cameraPreviewRect=cameraPreview.GetComponent<RectTransform>();
|
||||
// 使用 1080p 分辨率
|
||||
webCamTexture = new WebCamTexture(deviceName, 1920, 1080, 30);
|
||||
// 尝试更高分辨率(4K);WebCamTexture 在不支持时会自动回退到设备最大值
|
||||
// 注意:高分辨率下解码 CPU 压力大,需要配合性能较好的设备
|
||||
webCamTexture = new WebCamTexture(deviceName, 3840, 2160, 30);
|
||||
|
||||
// 开始播放
|
||||
webCamTexture.Play();
|
||||
@ -191,11 +217,30 @@ namespace Kill.UI.Pages
|
||||
// 等待摄像头启动
|
||||
yield return new WaitUntil(() => webCamTexture.width > 100);
|
||||
|
||||
// 输出实际分辨率,方便确认设备用的最大值
|
||||
Debug.Log($"[ScanQRcode] 摄像头实际分辨率: {webCamTexture.width}x{webCamTexture.height}");
|
||||
|
||||
// 等 0.3s 再触发对焦,避免系统未准备好
|
||||
yield return new WaitForSeconds(0.3f);
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
// iOS: 通过原生插件开启连续自动对焦(autoFocusPoint 在 iOS 无效)
|
||||
_EnableContinuousAutoFocus();
|
||||
// 定期维护对焦状态,防止被系统重置
|
||||
// 周期维护对焦:每 3s 触发一次点对焦(无黑屏),让镜头持续对焦
|
||||
StartCoroutine(IOsFocusMaintainCoroutine());
|
||||
#elif UNITY_ANDROID && !UNITY_EDITOR
|
||||
// Android:WebCamTexture 默认不会自动对焦,需主动通过 Camera API 触发
|
||||
EnableAndroidContinuousAutoFocus();
|
||||
// 仅当我们拿到 Camera 实例时才启动对焦维护协程(Unity 2022 LTS Camera2 后端一般拿不到)
|
||||
if (androidFocusControlAvailable)
|
||||
{
|
||||
StartCoroutine(AndroidFocusMaintainCoroutine());
|
||||
}
|
||||
else
|
||||
{
|
||||
// 兜底:未接管到 Camera 时,周期性触发重新对焦(部分只对焦一次的设备)
|
||||
StartCoroutine(AndroidFallbackFocusMaintainCoroutine());
|
||||
}
|
||||
#endif
|
||||
webCamTexture.autoFocusPoint = new Vector2(0.5f, 0.5f);
|
||||
// 调整预览画面比例
|
||||
@ -208,25 +253,59 @@ namespace Kill.UI.Pages
|
||||
}
|
||||
|
||||
|
||||
// 预览纹理长边基准(按 RawImage 实际宽高比生成,避免变形)
|
||||
// 保持 UI RectTransform 比例,内部纹理按比例放大到接近 1080 长边
|
||||
// 但不超过相机实际分辨率的 1.5 倍,避免低端设备过度放大导致画质损失
|
||||
private const int PreviewTextureLongSide = 1080;
|
||||
private const float MaxUpscaleRatio = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// 调整预览画面比例 - 从相机画面中心截取最大适合预览画面比例的画面
|
||||
/// 手机竖着拿,摄像头默认横向输出,需要旋转并截取中间区域
|
||||
/// 预览框是横向的(1080x680),显示旋转后的竖向画面
|
||||
/// 调整预览画面比例 - 从相机画面中心截取并旋转,生成与 UI 框同比例的高清纹理。
|
||||
/// 手机竖着拿,摄像头默认横向输出,需要旋转 90 度。
|
||||
/// 预览纹理宽高比跟随 RawImage RectTransform 宽高比,长边固定 1080,
|
||||
/// 既保证远距离扫码识别率,又避免画面变形。
|
||||
/// 对低端设备(720p 摄像头)会自适应降低纹理尺寸,避免过度放大导致画质损失。
|
||||
/// </summary>
|
||||
private void AdjustPreviewAspect()
|
||||
{
|
||||
if (webCamTexture == null || cameraPreview == null) return;
|
||||
|
||||
// 获取相机分辨率(摄像头默认横向,如 1920x1080)
|
||||
int cameraWidth = webCamTexture.width; // 1920
|
||||
int cameraHeight = webCamTexture.height; // 1080
|
||||
// 获取相机分辨率(摄像头默认横向,如 3840x2160 / 1920x1080 / 1280x720)
|
||||
int cameraWidth = webCamTexture.width;
|
||||
int cameraHeight = webCamTexture.height;
|
||||
|
||||
// 获取预览区域尺寸(横向预览框,比如 1080x680)
|
||||
// 获取预览区域尺寸(用于按比例生成纹理,避免变形)
|
||||
RectTransform previewRect = cameraPreview.GetComponent<RectTransform>();
|
||||
int previewWidth = (int)previewRect.rect.width; // 1080
|
||||
int previewHeight = (int)previewRect.rect.height; // 680
|
||||
float rectW = Mathf.Max(1, previewRect.rect.width);
|
||||
float rectH = Mathf.Max(1, previewRect.rect.height);
|
||||
|
||||
// 创建预览纹理(与预览框相同尺寸)
|
||||
// 相机短边(旋转后对应预览宽边),用于限制过度放大
|
||||
int cameraShortSide = Mathf.Min(cameraWidth, cameraHeight);
|
||||
|
||||
// 按 RectTransform 比例计算纹理尺寸(长边固定 1080)
|
||||
int previewWidth, previewHeight;
|
||||
if (rectW >= rectH)
|
||||
{
|
||||
previewWidth = PreviewTextureLongSide;
|
||||
previewHeight = Mathf.Max(1, Mathf.RoundToInt(PreviewTextureLongSide * (rectH / rectW)));
|
||||
}
|
||||
else
|
||||
{
|
||||
previewHeight = PreviewTextureLongSide;
|
||||
previewWidth = Mathf.Max(1, Mathf.RoundToInt(PreviewTextureLongSide * (rectW / rectH)));
|
||||
}
|
||||
|
||||
// 自适应:如果纹理长边超过相机短边的 1.5 倍,按相机分辨率等比缩小(保护低端设备画质)
|
||||
int previewLongSide = Mathf.Max(previewWidth, previewHeight);
|
||||
int maxAllowedLong = Mathf.RoundToInt(cameraShortSide * MaxUpscaleRatio);
|
||||
if (previewLongSide > maxAllowedLong)
|
||||
{
|
||||
float scale = (float)maxAllowedLong / previewLongSide;
|
||||
previewWidth = Mathf.Max(1, Mathf.RoundToInt(previewWidth * scale));
|
||||
previewHeight = Mathf.Max(1, Mathf.RoundToInt(previewHeight * scale));
|
||||
}
|
||||
|
||||
// 创建预览纹理(按 UI 比例)
|
||||
if (previewTexture == null || previewTexture.width != previewWidth || previewTexture.height != previewHeight)
|
||||
{
|
||||
if (previewTexture != null)
|
||||
@ -237,8 +316,6 @@ namespace Kill.UI.Pages
|
||||
}
|
||||
|
||||
// 从相机画面中裁切并旋转
|
||||
// 手机竖着拿,相机横向输出,需要顺时针旋转90度
|
||||
// 从相机画面中间截取适合预览比例的竖向区域
|
||||
CutAndRotateCameraFrame(cameraWidth, cameraHeight, previewWidth, previewHeight);
|
||||
|
||||
// 设置预览纹理
|
||||
@ -253,14 +330,249 @@ namespace Kill.UI.Pages
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// iOS 对焦维护协程 — 定期重新设置连续自动对焦,防止被系统重置
|
||||
/// iOS 对焦维护协程 — 每 3s 触发一次点对焦(无黑屏),让镜头持续重新对焦
|
||||
/// 与 Android softTrigger 策略保持一致
|
||||
/// </summary>
|
||||
private IEnumerator IOsFocusMaintainCoroutine()
|
||||
{
|
||||
// 首次等待 3s 让初始对焦完成
|
||||
yield return new WaitForSeconds(3f);
|
||||
|
||||
while (isScanning)
|
||||
{
|
||||
// 触发一次点对焦(无黑屏),1.5s 后原生会自动恢复连续对焦
|
||||
_TriggerContinuousRefocus();
|
||||
yield return new WaitForSeconds(3f);
|
||||
_EnableContinuousAutoFocus();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// 启用 Android 自动对焦。
|
||||
/// Unity 2022 LTS 在 Android 上使用 Camera2 API 内部创建 CameraDevice,
|
||||
/// 无法直接反射拿到 Camera 对象,本方法仅尝试拿到旧 Camera API 的实例。
|
||||
/// 失败时仅打印日志,不阻塞扫码流程(依赖系统默认对焦)。
|
||||
/// </summary>
|
||||
private void EnableAndroidContinuousAutoFocus()
|
||||
{
|
||||
androidCameraInstance = GetAndroidCameraFromWebCamTexture(webCamTexture);
|
||||
if (androidCameraInstance != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetCameraFocusMode(androidCameraInstance, "FOCUS_MODE_CONTINUOUS_PICTURE");
|
||||
androidFocusControlAvailable = true;
|
||||
Debug.Log("[ScanQRcode] Android 自动对焦已接管(FOCUS_MODE_CONTINUOUS_PICTURE)");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[ScanQRcode] 设置 Android 自动对焦模式失败: {e.Message}");
|
||||
androidFocusControlAvailable = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
androidFocusControlAvailable = false;
|
||||
// 尝试让 WebCamTexture 自身使用自动对焦(部分 ROM 支持)
|
||||
try { webCamTexture.autoFocusPoint = new Vector2(0.5f, 0.5f); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发 Android 单次自动对焦(指定点)
|
||||
/// </summary>
|
||||
private void TriggerAndroidAutoFocus(float normX, float normY)
|
||||
{
|
||||
if (!androidFocusControlAvailable || androidCameraInstance == null)
|
||||
{
|
||||
// 没接管到对焦,触摸对焦无效(保持安静,不打日志避免刷屏)
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 屏幕坐标 -> camera 坐标系(Camera API 的 focusAreas 使用 -1000~1000 坐标系)
|
||||
int focusX = Mathf.RoundToInt((normX - 0.5f) * 2000f);
|
||||
int focusY = Mathf.RoundToInt((normY - 0.5f) * 2000f);
|
||||
|
||||
using (var rectClass = new AndroidJavaClass("android.graphics.Rect"))
|
||||
using (var area = rectClass.CallStatic<AndroidJavaObject>("new", focusX - 100, focusY - 100, focusX + 100, focusY + 100))
|
||||
using (var areasList = new AndroidJavaObject("java.util.ArrayList"))
|
||||
{
|
||||
areasList.Call<bool>("add", area);
|
||||
androidCameraInstance.Call("setFocusAreas", areasList);
|
||||
androidCameraInstance.Call("setMeteringAreas", areasList);
|
||||
}
|
||||
|
||||
// 先切到 AUTO 模式才能执行单次对焦,对焦完再切回 CONTINUOUS
|
||||
SetCameraFocusMode(androidCameraInstance, "FOCUS_MODE_AUTO");
|
||||
|
||||
// AndroidJavaProxy 不是 IDisposable,不能 using;也不需要 Dispose,GC 即可回收
|
||||
var callback = new AutoFocusCallbackProxy(this);
|
||||
androidCameraInstance.Call("autoFocus", callback);
|
||||
|
||||
// 1.5s 后切回连续模式(对焦回调可能未触发,需要兜底)
|
||||
StartCoroutine(RestoreContinuousFocusAfter(1.5f));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[ScanQRcode] Android 触摸对焦失败: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator RestoreContinuousFocusAfter(float seconds)
|
||||
{
|
||||
yield return new WaitForSeconds(seconds);
|
||||
if (androidFocusControlAvailable && androidCameraInstance != null)
|
||||
{
|
||||
SetCameraFocusMode(androidCameraInstance, "FOCUS_MODE_CONTINUOUS_PICTURE");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 周期性重新设置连续对焦,防止 Android 镜头因失焦后停止自动对焦
|
||||
/// </summary>
|
||||
private IEnumerator AndroidFocusMaintainCoroutine()
|
||||
{
|
||||
while (isScanning)
|
||||
{
|
||||
yield return new WaitForSeconds(2.5f);
|
||||
if (androidFocusControlAvailable && androidCameraInstance != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetCameraFocusMode(androidCameraInstance, "FOCUS_MODE_CONTINUOUS_PICTURE");
|
||||
}
|
||||
catch { /* 实例可能已被释放,下次循环重试 */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兜底对焦策略:未接管到 Camera 实例时(Unity 2022 LTS 默认情况),
|
||||
/// 每 3s 通过 softTrigger(autoFocusPoint 重新设置,几乎无感)让镜头重新对焦。
|
||||
/// 用户也可通过调用 TriggerRefocus() 主动触发。
|
||||
/// </summary>
|
||||
private IEnumerator AndroidFallbackFocusMaintainCoroutine()
|
||||
{
|
||||
// 首次等待 3s 让初始对焦完成
|
||||
yield return new WaitForSeconds(3f);
|
||||
|
||||
while (isScanning && webCamTexture != null)
|
||||
{
|
||||
// 如果在运行中已经接管到对焦控制(理论上不会变),停止兜底
|
||||
if (androidFocusControlAvailable) break;
|
||||
|
||||
// 软触发:重新设置 autoFocusPoint(无黑屏)
|
||||
SoftTriggerAndroidFocus();
|
||||
|
||||
yield return new WaitForSeconds(3f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 软触发对焦:通过重新设置 autoFocusPoint 让手机做一次快速对焦,无黑屏。
|
||||
/// 部分手机响应后会重新对焦,部分手机忽略此属性(需要走硬重启兜底)。
|
||||
/// </summary>
|
||||
private void SoftTriggerAndroidFocus()
|
||||
{
|
||||
if (webCamTexture == null || !webCamTexture.isPlaying) return;
|
||||
try
|
||||
{
|
||||
// 在 (0.5, 0.5) 和 (0.5, 0.51) 之间切换,强制触发 setter
|
||||
// 相同值部分 ROM 不响应,需要做点变化
|
||||
webCamTexture.autoFocusPoint = new Vector2(0.5f, 0.51f);
|
||||
webCamTexture.autoFocusPoint = new Vector2(0.5f, 0.5f);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[ScanQRcode] softTrigger 失败: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过停止+重新播放 WebCamTexture 强制镜头重新对焦。
|
||||
/// 注意:会有约 200~500ms 的预览黑屏闪烁,权衡使用。
|
||||
/// </summary>
|
||||
private void RestartWebCamTextureForFocus()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (webCamTexture == null) return;
|
||||
webCamTexture.Stop();
|
||||
webCamTexture.Play();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[ScanQRcode] 重启 WebCamTexture 失败: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射从 WebCamTexture 拿到内部的 android.hardware.Camera 实例。
|
||||
/// 注意:Unity 2022 LTS 在 Android 上使用 Camera2 API,此反射在 2022 LTS 上几乎拿不到值。
|
||||
/// </summary>
|
||||
private AndroidJavaObject GetAndroidCameraFromWebCamTexture(WebCamTexture tex)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] candidates = { "m_Camera", "m_NativeCamera", "camera", "mCamera" };
|
||||
foreach (var name in candidates)
|
||||
{
|
||||
var f = typeof(WebCamTexture).GetField(name,
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (f != null)
|
||||
{
|
||||
var v = f.GetValue(tex);
|
||||
if (v is AndroidJavaObject ajo) return ajo;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[ScanQRcode] 反射获取 Camera 失败: {e.Message}");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置 Camera 对焦模式(Camera API 官方推荐通过 setParameters 设置)
|
||||
/// </summary>
|
||||
private void SetCameraFocusMode(AndroidJavaObject camera, string modeName)
|
||||
{
|
||||
if (camera == null) return;
|
||||
using (var parameters = camera.Call<AndroidJavaObject>("getParameters"))
|
||||
{
|
||||
parameters.Call("setFocusMode", modeName);
|
||||
camera.Call("setParameters", parameters);
|
||||
}
|
||||
// 部分旧设备直接调用 setFocusMode(String) 也生效,兼容一下
|
||||
camera.Call("setFocusMode", modeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AutoFocusCallback 的 C# 实现(AndroidJavaProxy)
|
||||
/// </summary>
|
||||
private class AutoFocusCallbackProxy : AndroidJavaProxy
|
||||
{
|
||||
private readonly ScanQRcode _owner;
|
||||
public AutoFocusCallbackProxy(ScanQRcode owner) : base("android.hardware.Camera$AutoFocusCallback")
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
// Java 签名: void onAutoFocus(boolean success, Camera camera)
|
||||
public void onAutoFocus(bool success, AndroidJavaObject camera)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_owner != null && _owner.androidFocusControlAvailable && _owner.androidCameraInstance != null)
|
||||
{
|
||||
_owner.SetCameraFocusMode(_owner.androidCameraInstance, "FOCUS_MODE_CONTINUOUS_PICTURE");
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -270,16 +582,11 @@ namespace Kill.UI.Pages
|
||||
/// <summary>
|
||||
/// 从相机画面裁切并旋转,生成预览画面
|
||||
/// 支持中心裁切放大:只截取画面中心区域,放大到预览尺寸
|
||||
/// 性能优化:按行复用 float 增量,避免重复乘法
|
||||
/// </summary>
|
||||
private void CutAndRotateCameraFrame(int camW, int camH, int targetW, int targetH)
|
||||
{
|
||||
// 相机横向 1920x1080,需要顺时针旋转90度变成 1080x1920
|
||||
// 预览框横向 1080x680
|
||||
// 从旋转后的 1080x1920 中截取中间区域并放大
|
||||
|
||||
// 计算中心裁切区域大小
|
||||
// centerCropRatio=1.0 时等于原始裁切(取 targetH 列 / 所有行),无缩放
|
||||
// centerCropRatio<1.0 时取更小区域放大,值越小放大倍数越大
|
||||
int sourceCropH = Mathf.Max(1, Mathf.RoundToInt(targetH * centerCropRatio));
|
||||
int sourceCropW = Mathf.Max(1, Mathf.RoundToInt(targetW * centerCropRatio));
|
||||
int cropY = (camW - sourceCropH) / 2;
|
||||
@ -293,17 +600,17 @@ namespace Kill.UI.Pages
|
||||
Color32[] targetPixels = new Color32[targetW * targetH];
|
||||
|
||||
// 顺时针旋转90度 + 中心裁切 + 缩放
|
||||
// 目标像素 (x, y) 对应源坐标:
|
||||
// srcX = cropY + (targetH-1-y) * scale (裁切区域内纵向缩放)
|
||||
// srcY = cropX + x * scale (裁切区域内横向缩放)
|
||||
for (int y = 0; y < targetH; y++)
|
||||
{
|
||||
float srcXBase = cropY + (targetH - 1 - y) * scale; // 整行的 srcX
|
||||
float srcYBase = cropX; // 起始 srcY
|
||||
|
||||
int rowStart = y * targetW;
|
||||
for (int x = 0; x < targetW; x++)
|
||||
{
|
||||
float srcX = cropY + (targetH - 1 - y) * scale;
|
||||
float srcY = cropX + x * scale;
|
||||
float srcX = srcXBase;
|
||||
float srcY = srcYBase + x * scale;
|
||||
|
||||
// 双线性插值采样
|
||||
int sx = (int)srcX;
|
||||
int sy = (int)srcY;
|
||||
float fx = srcX - sx;
|
||||
@ -325,7 +632,7 @@ namespace Kill.UI.Pages
|
||||
float g = (1-fx)*(1-fy)*c00.g + fx*(1-fy)*c10.g + (1-fx)*fy*c01.g + fx*fy*c11.g;
|
||||
float b = (1-fx)*(1-fy)*c00.b + fx*(1-fy)*c10.b + (1-fx)*fy*c01.b + fx*fy*c11.b;
|
||||
|
||||
targetPixels[y * targetW + x] = new Color32(
|
||||
targetPixels[rowStart + x] = new Color32(
|
||||
(byte)Mathf.Clamp(r, 0, 255),
|
||||
(byte)Mathf.Clamp(g, 0, 255),
|
||||
(byte)Mathf.Clamp(b, 0, 255),
|
||||
@ -485,12 +792,6 @@ namespace Kill.UI.Pages
|
||||
snapshot.SetPixels(webCamTexture.GetPixels());
|
||||
snapshot.Apply();
|
||||
|
||||
// // 如果启用放大,对图像进行放大处理
|
||||
// if (enableUpscale && upscaleFactor > 1f)
|
||||
// {
|
||||
// snapshot = UpscaleTexture(snapshot, upscaleFactor);
|
||||
// }
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
catch
|
||||
|
||||
@ -24,6 +24,7 @@ namespace Kill.UI.Pages
|
||||
private Action<string> onConfirmCallback;
|
||||
private int type = 0;//0设备重命名 1用户名重命名
|
||||
private string deviceMac;// 待重命名设备的蓝牙MAC(为空时取当前选中设备)
|
||||
private Action closeMenuPageCallback;
|
||||
/// <summary>
|
||||
/// 初始化页面
|
||||
/// </summary>
|
||||
@ -33,6 +34,7 @@ namespace Kill.UI.Pages
|
||||
/// <param name="deviceMac">设备蓝牙MAC(重命名非选中设备时传入)</param>
|
||||
public void Init(string name, Action<string> onConfirm, int type = 0, string deviceMac = null)
|
||||
{
|
||||
closeMenuPageCallback=UIManager.Instance.GetBackAction();
|
||||
currentName = name;
|
||||
onConfirmCallback = onConfirm;
|
||||
this.type = type;
|
||||
@ -172,6 +174,11 @@ namespace Kill.UI.Pages
|
||||
/// </summary>
|
||||
private void Back()
|
||||
{
|
||||
if (closeMenuPageCallback != null)
|
||||
{
|
||||
UIManager.Instance.RegisterBackAction(closeMenuPageCallback);
|
||||
}
|
||||
else
|
||||
UIManager.Instance.ClearBackAction();
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
@ -24,13 +24,14 @@ namespace Kill.UI.Pages
|
||||
/// </summary>
|
||||
/// <param name="deviceMac">设备蓝牙MAC(分享非选中设备时传入)</param>
|
||||
/// <param name="onClose">关闭页面回调</param>
|
||||
public void Init(string deviceMac = null, Action onClose = null)
|
||||
public void Init(string deviceMac = null)
|
||||
{
|
||||
this.deviceMac = deviceMac;
|
||||
this.onCloseAction = onClose;
|
||||
}
|
||||
private Action closeCallback;
|
||||
void Start()
|
||||
{
|
||||
closeCallback=UIManager.Instance.GetBackAction();
|
||||
UIManager.Instance.RegisterBackAction(Back);
|
||||
backButton.onClick.RemoveAllListeners();
|
||||
backButton.onClick.AddListener(Back);
|
||||
@ -53,23 +54,12 @@ namespace Kill.UI.Pages
|
||||
}
|
||||
public void Back()
|
||||
{
|
||||
if (onCloseAction != null)
|
||||
if (closeCallback != null)
|
||||
{
|
||||
// 恢复上层页面的返回事件
|
||||
UIManager.Instance.RegisterBackAction(onCloseAction);
|
||||
UIManager.Instance.RegisterBackAction(closeCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
var infoPage = GetComponentInParent<DeviceInfoPage>();
|
||||
if (infoPage != null)
|
||||
{
|
||||
UIManager.Instance.RegisterBackAction(infoPage.Back);
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.Instance.ClearBackAction();
|
||||
}
|
||||
}
|
||||
Destroy(gameObject);
|
||||
}
|
||||
public async Task Share()
|
||||
|
||||
@ -75,12 +75,14 @@ namespace Kill.UI.Pages
|
||||
|
||||
async Task Start()
|
||||
{
|
||||
LoadingUI.Show();
|
||||
if(string.IsNullOrEmpty(DataManager.Instance.userInfo.country_code))
|
||||
{
|
||||
var page= Instantiate(selectCountryPage,transform);
|
||||
page.gameObject.SetActive(true);
|
||||
page.Init(null,false);
|
||||
page.onConfirm=(code)=>{UIManager.Instance.OpenMainPage(UIManager.PageName.homePage);};
|
||||
LoadingUI.Hide();
|
||||
return;
|
||||
}
|
||||
// 订阅蓝牙事件
|
||||
@ -870,7 +872,7 @@ namespace Kill.UI.Pages
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
LoadingUI.Show();
|
||||
|
||||
if (DataManager.Instance.userInfo.device_count == 0 || (OwnedDevices.Count == 0 && SharedDevices.Count == 0))
|
||||
{
|
||||
noDevicePage.SetActive(true);
|
||||
|
||||
@ -196,7 +196,6 @@ namespace Kill.UI.Pages
|
||||
item.menuButtons[3].onClick.AddListener(() => ToggleTop(item, true));
|
||||
item.menuButtons[4].onClick.AddListener(() => ToggleTop(item, false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开设备分享页面
|
||||
/// </summary>
|
||||
@ -208,7 +207,7 @@ namespace Kill.UI.Pages
|
||||
currentShareDevicePage = Instantiate(shareDevicePagePrefab, transform).GetComponent<ShareDevicePage>();
|
||||
currentShareDevicePage.gameObject.SetActive(true);
|
||||
// 关闭分享页时恢复设备列表页的返回事件
|
||||
currentShareDevicePage.Init(item.deviceInfo.ble_mac, () => UIManager.Instance.RegisterBackAction(ClosePage));
|
||||
currentShareDevicePage.Init(item.deviceInfo.ble_mac);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -305,6 +304,7 @@ namespace Kill.UI.Pages
|
||||
if(lastSelectedDevice==selectedDevice)
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
UIManager.Instance.ClearBackAction();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -115,6 +115,7 @@ namespace Kill.UI
|
||||
if (backAction != null)
|
||||
{
|
||||
Debug.Log("执行注册返回");
|
||||
Debug.Log(backAction.Method.Name);
|
||||
var action = backAction;
|
||||
action.Invoke();
|
||||
return;
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
public void ShowTransparentSystemUI()
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
try
|
||||
{
|
||||
Debug.Log("尝试显示透明Android系统UI");
|
||||
|
||||
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
|
||||
using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
|
||||
{
|
||||
// 检查 activity 是否为 null
|
||||
if (activity == null)
|
||||
{
|
||||
Debug.LogError("当前活动对象为空,无法显示导航栏");
|
||||
// 添加延迟重试机制
|
||||
Invoke("ShowTransparentSystemUI", 0.5f);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 runOnUiThread 确保在主线程执行
|
||||
activity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
|
||||
{
|
||||
using (var window = activity.Call<AndroidJavaObject>("getWindow"))
|
||||
{
|
||||
// 清除全屏标志
|
||||
window.Call("clearFlags", 0x00000400); // WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
|
||||
// 添加半透明状态栏和导航栏标志 (适用于API 19及以上版本)
|
||||
if (IsAndroidApiLevelAtLeast(19))
|
||||
{
|
||||
window.Call("addFlags", 0x04000000); // WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
|
||||
window.Call("addFlags", 0x08000000); // WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
|
||||
}
|
||||
|
||||
// 对于API 21及以上版本,设置状态栏和导航栏颜色为透明
|
||||
if (IsAndroidApiLevelAtLeast(21))
|
||||
{
|
||||
window.Call("setStatusBarColor", 0x00000000); // 透明
|
||||
window.Call("setNavigationBarColor", 0x00000000); // 透明
|
||||
}
|
||||
|
||||
// 设置系统UI可见性
|
||||
using (var decorView = window.Call<AndroidJavaObject>("getDecorView"))
|
||||
{
|
||||
var systemUiVisibility = decorView.Call<int>("getSystemUiVisibility");
|
||||
systemUiVisibility &= ~(0x00000004 | 0x00000002); // 清除SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN
|
||||
|
||||
// 对于API 23及以上版本,设置浅色状态栏图标(深色背景下可见)
|
||||
if (IsAndroidApiLevelAtLeast(23))
|
||||
{
|
||||
systemUiVisibility |= 0x00002000; // SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||
}
|
||||
|
||||
decorView.Call("setSystemUiVisibility", systemUiVisibility);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Screen.fullScreen = false;
|
||||
Debug.Log("已完成显示透明Android系统UI的尝试");
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError("显示透明系统UI时发生错误: " + e.Message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user