Unity引擎Quest,Pico,HoloLens等XR平台开发经验总结

Unity引擎XR平台开发经验总结

一、XR开发概述与平台差异

主要XR平台特性对比

特性Meta QuestPicoHoloLens
类型VR (沉浸式)VR (沉浸式)AR (混合现实)
定位追踪6DoF 无需外部传感器6DoF 无需外部传感器6DoF 空间映射
控制方式手柄控制器+手势追踪手柄控制器+手势追踪手势识别+语音+眼动追踪
处理能力中等 (骁龙XR2)中等 (骁龙XR2)中等 (Holographic Processing Unit)
开发特色社交功能、应用商店成熟国内生态、B端应用全息投影、空间理解
技术限制功耗热量限制,图形性能有限功耗热量限制,生态相对不足视场角小,重量,电池续航

统一XR架构设计

// XRPlatformManager.cs - 统一平台适配管理器
using UnityEngine;
using UnityEngine.XR;

public enum XRPlatformType
{
    Quest,
    Pico,
    HoloLens,
    Other
}

public class XRPlatformManager : MonoBehaviour
{
    public static XRPlatformManager Instance { get; private set; }
    
    [SerializeField] private XRPlatformType defaultPlatform = XRPlatformType.Quest;
    
    public XRPlatformType CurrentPlatform { get; private set; }
    
    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
            DetectAndInitializePlatform();
        }
        else
        {
            Destroy(gameObject);
        }
    }
    
    private void DetectAndInitializePlatform()
    {
        string deviceName = SystemInfo.deviceName.ToLower();
        string deviceModel = SystemInfo.deviceModel.ToLower();
        
        // 检测Quest
        if (deviceName.Contains("quest") || deviceModel.Contains("quest") || 
            XRSettings.loadedDeviceName.Contains("oculus"))
        {
            CurrentPlatform = XRPlatformType.Quest;
        }
        // 检测Pico
        else if (deviceName.Contains("pico") || deviceModel.Contains("pico") || 
                 XRSettings.loadedDeviceName.Contains("pico"))
        {
            CurrentPlatform = XRPlatformType.Pico;
        }
        // 检测HoloLens
        else if (deviceName.Contains("hololens") || deviceModel.Contains("hololens") || 
                 XRSettings.loadedDeviceName.Contains("windowsmr"))
        {
            CurrentPlatform = XRPlatformType.HoloLens;
        }
        else
        {
            Debug.Log("未识别的XR平台,使用默认平台设置");
            CurrentPlatform = defaultPlatform;
        }
        
        Debug.Log($"检测到XR平台: {CurrentPlatform}");
        InitializePlatform();
    }
    
    private void InitializePlatform()
    {
        switch (CurrentPlatform)
        {
            case XRPlatformType.Quest:
                InitializeQuestPlatform();
                break;
            
            case XRPlatformType.Pico:
                InitializePicoPlatform();
                break;
                
            case XRPlatformType.HoloLens:
                InitializeHoloLensPlatform();
                break;
                
            default:
                Debug.LogWarning("未实现的平台初始化");
                break;
        }
    }
    
    private void InitializeQuestPlatform()
    {
        // Quest特定初始化
        Debug.Log("初始化Quest平台");
        
        #if UNITY_ANDROID && !UNITY_EDITOR
        // Quest特定API调用
        // OVRManager.instance.SetupInsightPassthrough();
        #endif
    }
    
    private void InitializePicoPlatform()
    {
        // Pico特定初始化
        Debug.Log("初始化Pico平台");
        
        #if UNITY_ANDROID && !UNITY_EDITOR
        // Pico特定API调用
        #endif
    }
    
    private void InitializeHoloLensPlatform()
    {
        // HoloLens特定初始化
        Debug.Log("初始化HoloLens平台");
        
        #if WINDOWS_UWP && !UNITY_EDITOR
        // HoloLens特定API调用
        #endif
    }
}

跨平台项目配置

// 定义预处理器指令
// 在Player Settings -> Other Settings -> Scripting Define Symbols中添加
// QUEST_SDK, PICO_SDK, HOLOLENS_SDK

// CrossPlatformSettings.cs - 跨平台设置器
using UnityEngine;

public class CrossPlatformSettings : MonoBehaviour
{
    [Header("通用XR设置")]
    [SerializeField] private bool enableVRMode = true;
    [SerializeField] private bool enableMixedRealityMode = false;
    [Range(60, 120)]
    [SerializeField] private int targetFrameRate = 72;
    
    [Header("Quest特定设置")]
    [SerializeField] private bool enableQuestHandTracking = true;
    [SerializeField] private bool enableQuestPassthrough = false;
    
    [Header("Pico特定设置")]
    [SerializeField] private bool enablePicoEyeTracking = false;
    
    [Header("HoloLens特定设置")]
    [SerializeField] private bool enableHoloLensSpatialMapping = true;
    [SerializeField] private bool enableHoloLensVoiceCommands = true;
    
    private void Awake()
    {
        ApplyCommonSettings();
        ApplyPlatformSpecificSettings();
    }
    
    private void ApplyCommonSettings()
    {
        // 设置目标帧率
        Application.targetFrameRate = targetFrameRate;
        
        // 防止休眠
        Screen.sleepTimeout = SleepTimeout.NeverSleep;
    }
    
    private void ApplyPlatformSpecificSettings()
    {
        XRPlatformType platform = XRPlatformManager.Instance.CurrentPlatform;
        
        switch(platform)
        {
            case XRPlatformType.Quest:
                ApplyQuestSettings();
                break;
                
            case XRPlatformType.Pico:
                ApplyPicoSettings();
                break;
                
            case XRPlatformType.HoloLens:
                ApplyHoloLensSettings();
                break;
        }
    }
    
    private void ApplyQuestSettings()
    {
        #if QUEST_SDK
        Debug.Log("应用Quest特定设置");
        
        // 示例:设置手部追踪
        if (enableQuestHandTracking)
        {
            // OVRPlugin.HandTrackingEnabled = true;
        }
        
        // 示例:设置Passthrough
        if (enableQuestPassthrough)
        {
            // OVRManger.instance.isInsightPassthroughEnabled = true;
        }
        #endif
    }
    
    private void ApplyPicoSettings()
    {
        #if PICO_SDK
        Debug.Log("应用Pico特定设置");
        
        // 示例:设置眼动追踪
        if (enablePicoEyeTracking)
        {
            // Pvr_UnitySDKAPI.EyeTracking.StartEyeTracking();
        }
        #endif
    }
    
    private void ApplyHoloLensSettings()
    {
        #if HOLOLENS_SDK
        Debug.Log("应用HoloLens特定设置");
        
        // 示例:设置空间映射
        if (enableHoloLensSpatialMapping)
        {
            // 启用空间映射组件
        }
        
        // 示例:启用语音命令
        if (enableHoloLensVoiceCommands)
        {
            // 初始化语音识别
        }
        #endif
    }
}

二、Quest平台开发

性能优化策略

// QuestPerformanceOptimizer.cs - Quest平台性能优化
using UnityEngine;

public class QuestPerformanceOptimizer : MonoBehaviour
{
    [Header("自动性能优化")]
    [SerializeField] private bool enableDynamicResolution = true;
    [SerializeField] private bool enableFixedFoveatedRendering = true;
    [SerializeField] private bool enableAdaptiveQuality = true;
    
    [Header("性能等级")]
    [SerializeField] private int cpuLevel = 2; // 0-4
    [SerializeField] private int gpuLevel = 3; // 0-4
    
    private void Start()
    {
        ApplyPerformanceSettings();
    }
    
    private void ApplyPerformanceSettings()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        try
        {
            // 设置CPU/GPU性能等级
            OVRManager.cpuLevel = cpuLevel;
            OVRManager.gpuLevel = gpuLevel;
            
            // 开启固定注视点渲染
            if (enableFixedFoveatedRendering)
            {
                OVRManager.fixedFoveatedRenderingLevel = OVRManager.FixedFoveatedRenderingLevel.High;
                OVRManager.useDynamicFixedFoveatedRendering = true;
            }
            
            // 启用动态分辨率
            if (enableDynamicResolution)
            {
                OVRManager.eyeTextureResolutionScale = 1.0f;
                OVRManager.eyeTextureResolutionScaleUsesLodBias = true;
            }
            
            // 自适应品质
            if (enableAdaptiveQuality)
            {
                OVRManager.AdaptivePerformance = true;
            }
            
            Debug.Log("Quest性能优化设置已应用");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"设置Quest性能参数时出错: {e.Message}");
        }
        #endif
    }
    
    // 监控性能并动态调整
    private void Update()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        if (enableAdaptiveQuality)
        {
            // 获取当前帧率
            float fps = 1.0f / Time.smoothDeltaTime;
            
            // 如果帧率下降,降低质量
            if (fps < 60)
            {
                ReduceQuality();
            }
            // 如果帧率稳定,可以尝试提高质量
            else if (fps > 80)
            {
                IncreaseQuality();
            }
        }
        #endif
    }
    
    private void ReduceQuality()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        // 降低渲染分辨率
        float currentScale = OVRManager.eyeTextureResolutionScale;
        OVRManager.eyeTextureResolutionScale = Mathf.Max(0.7f, currentScale - 0.05f);
        
        // 提高注视点渲染等级
        if (OVRManager.fixedFoveatedRenderingLevel < OVRManager.FixedFoveatedRenderingLevel.High)
        {
            OVRManager.fixedFoveatedRenderingLevel += 1;
        }
        #endif
    }
    
    private void IncreaseQuality()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        // 提高渲染分辨率
        float currentScale = OVRManager.eyeTextureResolutionScale;
        OVRManager.eyeTextureResolutionScale = Mathf.Min(1.2f, currentScale + 0.02f);
        
        // 降低注视点渲染等级
        if (OVRManager.fixedFoveatedRenderingLevel > OVRManager.FixedFoveatedRenderingLevel.Off)
        {
            OVRManager.fixedFoveatedRenderingLevel -= 1;
        }
        #endif
    }
}

Quest手势追踪与交互

// QuestHandTracking.cs - Quest手部追踪和交互
using UnityEngine;

#if QUEST_SDK
using OVRTouchSample;
#endif

public class QuestHandTracking : MonoBehaviour
{
    [Header("手部模型")]
    [SerializeField] private GameObject leftHandModel;
    [SerializeField] private GameObject rightHandModel;
    [SerializeField] private GameObject leftControllerModel;
    [SerializeField] private GameObject rightControllerModel;
    
    [Header("交互设置")]
    [SerializeField] private float pinchThreshold = 0.7f;
    [SerializeField] private LayerMask interactableLayers;
    [SerializeField] private float grabDistance = 0.2f;
    
    // 手势状态
    private bool isLeftHandTracked = false;
    private bool isRightHandTracked = false;
    private bool isLeftPinching = false;
    private bool isRightPinching = false;
    private Transform leftIndexTip;
    private Transform rightIndexTip;
    
    // 抓取的对象
    private InteractableObject currentLeftInteractable;
    private InteractableObject currentRightInteractable;
    
    private void Start()
    {
        InitializeHandTracking();
    }
    
    private void InitializeHandTracking()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        // 初始化手部追踪
        OVRManager.instance.handTrackingEnabled = true;
        
        // 获取手指尖Transform
        if (leftHandModel != null)
        {
            leftIndexTip = FindFingerTip(leftHandModel, "index");
        }
        
        if (rightHandModel != null)
        {
            rightIndexTip = FindFingerTip(rightHandModel, "index");
        }
        
        // 初始隐藏手模型
        SetHandModelsActive(false);
        SetControllerModelsActive(true);
        #endif
    }
    
    private Transform FindFingerTip(GameObject handModel, string fingerName)
    {
        // 通常手指尖会有一个特定命名的骨骼或Transform
        Transform[] allTransforms = handModel.GetComponentsInChildren<Transform>();
        foreach (Transform t in allTransforms)
        {
            if (t.name.ToLower().Contains(fingerName) && t.name.ToLower().Contains("tip"))
            {
                return t;
            }
        }
        return null;
    }
    
    private void Update()
    {
        UpdateHandTracking();
        CheckForInteractions();
    }
    
    private void UpdateHandTracking()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        // 检测手部追踪状态
        isLeftHandTracked = OVRInput.IsControllerConnected(OVRInput.Controller.LHand);
        isRightHandTracked = OVRInput.IsControllerConnected(OVRInput.Controller.RHand);
        
        // 根据手部追踪状态切换手模型和控制器模型
        if (isLeftHandTracked || isRightHandTracked)
        {
            if (leftHandModel != null) leftHandModel.SetActive(isLeftHandTracked);
            if (rightHandModel != null) rightHandModel.SetActive(isRightHandTracked);
            
            if (leftControllerModel != null) leftControllerModel.SetActive(!isLeftHandTracked);
            if (rightControllerModel != null) rightControllerModel.SetActive(!isRightHandTracked);
        }
        
        // 更新手势状态
        if (isLeftHandTracked)
        {
            UpdateHandGestures(OVRHand.Hand.HandLeft, ref isLeftPinching);
        }
        
        if (isRightHandTracked)
        {
            UpdateHandGestures(OVRHand.Hand.HandRight, ref isRightPinching);
        }
        #endif
    }
    
    #if QUEST_SDK
    private void UpdateHandGestures(OVRHand.Hand handType, ref bool isPinching)
    {
        OVRHand hand = handType == OVRHand.Hand.HandLeft ? 
            OVRInput.GetActiveController(OVRInput.Hand.HandLeft).GetComponent<OVRHand>() : 
            OVRInput.GetActiveController(OVRInput.Hand.HandRight).GetComponent<OVRHand>();
            
        if (hand != null)
        {
            // 检测捏合手势
            float pinchStrength = hand.GetFingerPinchStrength(OVRHand.HandFinger.Index);
            
            // 更新捏合状态 (添加滞后效应避免抖动)
            if (pinchStrength >= pinchThreshold && !isPinching)
            {
                isPinching = true;
                OnPinchStart(handType);
            }
            else if (pinchStrength < pinchThreshold * 0.8f && isPinching)
            {
                isPinching = false;
                OnPinchEnd(handType);
            }
        }
    }
    #endif
    
    private void OnPinchStart(OVRHand.Hand handType)
    {
        // 处理开始捏合事件
        if (handType == OVRHand.Hand.HandLeft)
        {
            TryGrabObject(leftIndexTip, ref currentLeftInteractable);
        }
        else
        {
            TryGrabObject(rightIndexTip, ref currentRightInteractable);
        }
    }
    
    private void OnPinchEnd(OVRHand.Hand handType)
    {
        // 处理结束捏合事件
        if (handType == OVRHand.Hand.HandLeft && currentLeftInteractable != null)
        {
            currentLeftInteractable.OnRelease();
            currentLeftInteractable = null;
        }
        else if (handType == OVRHand.Hand.HandRight && currentRightInteractable != null)
        {
            currentRightInteractable.OnRelease();
            currentRightInteractable = null;
        }
    }
    
    private void TryGrabObject(Transform fingerTip, ref InteractableObject currentInteractable)
    {
        if (fingerTip == null) return;
        
        // 射线检测可交互物体
        RaycastHit hit;
        if (Physics.SphereCast(fingerTip.position, 0.02f, fingerTip.forward, out hit, grabDistance, interactableLayers))
        {
            InteractableObject interactable = hit.collider.GetComponent<InteractableObject>();
            if (interactable != null && interactable.CanInteract())
            {
                currentInteractable = interactable;
                currentInteractable.OnGrab(fingerTip);
            }
        }
    }
    
    private void CheckForInteractions()
    {
        // 更新已抓取物体的位置
        if (currentLeftInteractable != null && leftIndexTip != null)
        {
            currentLeftInteractable.UpdatePosition(leftIndexTip);
        }
        
        if (currentRightInteractable != null && rightIndexTip != null)
        {
            currentRightInteractable.UpdatePosition(rightIndexTip);
        }
    }
    
    private void SetHandModelsActive(bool active)
    {
        if (leftHandModel != null) leftHandModel.SetActive(active);
        if (rightHandModel != null) rightHandModel.SetActive(active);
    }
    
    private void SetControllerModelsActive(bool active)
    {
        if (leftControllerModel != null) leftControllerModel.SetActive(active);
        if (rightControllerModel != null) rightControllerModel.SetActive(active);
    }
}

// InteractableObject.cs - 可交互对象基类
public class InteractableObject : MonoBehaviour
{
    [SerializeField] private bool isGrabbable = true;
    [SerializeField] private bool isSnappable = false;
    [SerializeField] private Transform snapPoint;
    
    private bool isGrabbed = false;
    private Transform grabber;
    private Vector3 grabOffset;
    private Rigidbody rb;
    
    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }
    
    public bool CanInteract()
    {
        return isGrabbable && !isGrabbed;
    }
    
    public void OnGrab(Transform newGrabber)
    {
        if (!isGrabbable) return;
        
        isGrabbed = true;
        grabber = newGrabber;
        
        // 计算抓取偏移
        grabOffset = transform.position - grabber.position;
        
        // 如果有刚体,变更物理行为
        if (rb != null)
        {
            rb.isKinematic = true;
        }
        
        // 触发抓取事件
        OnGrabEvent();
    }
    
    public void UpdatePosition(Transform grabber)
    {
        if (!isGrabbed) return;
        
        // 移动物体
        if (isSnappable && snapPoint != null)
        {
            // 使用吸附点
            transform.position = Vector3.Lerp(transform.position, 
                                             grabber.position + grabOffset, 
                                             Time.deltaTime * 15f);
        }
        else
        {
            // 直接跟随手指
            transform.position = grabber.position + grabOffset;
        }
    }
    
    public void OnRelease()
    {
        if (!isGrabbed) return;
        
        isGrabbed = false;
        
        // 如果有刚体,恢复物理
        if (rb != null)
        {
            rb.isKinematic = false;
        }
        
        // 触发释放事件
        OnReleaseEvent();
    }
    
    // 可重写的事件方法
    protected virtual void OnGrabEvent()
    {
        // 子类重写以添加特定行为
    }
    
    protected virtual void OnReleaseEvent()
    {
        // 子类重写以添加特定行为
    }
}

三、Pico平台开发

Pico控制器和手势系统

// PicoControllerManager.cs - Pico控制器和手势管理
using UnityEngine;

#if PICO_SDK
using Unity.XR.PXR;
#endif

public class PicoControllerManager : MonoBehaviour
{
    [Header("控制器设置")]
    [SerializeField] private GameObject leftControllerPrefab;
    [SerializeField] private GameObject rightControllerPrefab;
    
    [Header("手势设置")]
    [SerializeField] private bool enableHandTracking = true;
    [SerializeField] private GameObject leftHandPrefab;
    [SerializeField] private GameObject rightHandPrefab;
    
    // 控制器引用
    private GameObject leftController;
    private GameObject rightController;
    private GameObject leftHand;
    private GameObject rightHand;
    
    // 手势状态
    private bool handTrackingActive = false;
    
    private void Start()
    {
        InitializeControllers();
        
        if (enableHandTracking)
        {
            InitializeHandTracking();
        }
    }
    
    private void InitializeControllers()
    {
        // 实例化控制器模型
        if (leftControllerPrefab != null)
        {
            leftController = Instantiate(leftControllerPrefab);
        }
        
        if (rightControllerPrefab != null)
        {
            rightController = Instantiate(rightControllerPrefab);
        }
        
        #if PICO_SDK && !UNITY_EDITOR
        // 初始化Pico控制器
        PXR_Input.SetControllerVibration(1.0f, 1.0f, 0.1f, PXR_Input.Controller.LeftController);
        PXR_Input.SetControllerVibration(1.0f, 1.0f, 0.1f, PXR_Input.Controller.RightController);
        
        Debug.Log("Pico控制器已初始化");
        #endif
    }
    
    private void InitializeHandTracking()
    {
        #if PICO_SDK && !UNITY_EDITOR
        // 初始化Pico手部追踪
        PXR_HandTracking.EnableHandTracking(true);
        
        // 实例化手部模型
        if (leftHandPrefab != null)
        {
            leftHand = Instantiate(leftHandPrefab);
            leftHand.SetActive(false);
        }
        
        if (rightHandPrefab != null)
        {
            rightHand = Instantiate(rightHandPrefab);
            rightHand.SetActive(false);
        }
        
        Debug.Log("Pico手部追踪已初始化");
        #endif
    }
    
    private void Update()
    {
        UpdateControllerStates();
        
        if (enableHandTracking)
        {
            UpdateHandTracking();
        }
    }
    
    private void UpdateControllerStates()
    {
        #if PICO_SDK && !UNITY_EDITOR
        // 获取控制器状态
        Vector3 leftPos = PXR_Input.GetLocalPosition(PXR_Input.Controller.LeftController);
        Quaternion leftRot = PXR_Input.GetLocalRotation(PXR_Input.Controller.LeftController);
        
        Vector3 rightPos = PXR_Input.GetLocalPosition(PXR_Input.Controller.RightController);
        Quaternion rightRot = PXR_Input.GetLocalRotation(PXR_Input.Controller.RightController);
        
        // 更新控制器位置
        if (leftController != null)
        {
            leftController.transform.localPosition = leftPos;
            leftController.transform.localRotation = leftRot;
        }
        
        if (rightController != null)
        {
            rightController.transform.localPosition = rightPos;
            rightController.transform.localRotation = rightRot;
        }
        
        // 处理按键输入
        if (PXR_Input.GetDown(PXR_Input.ButtonMask.A, PXR_Input.Controller.RightController))
        {
            OnButtonAPressed();
        }
        
        if (PXR_Input.GetDown(PXR_Input.ButtonMask.X, PXR_Input.Controller.LeftController))
        {
            OnButtonXPressed();
        }
        
        // 处理扳机键
        float leftTrigger = PXR_Input.GetTriggerValue(PXR_Input.Controller.LeftController);
        float rightTrigger = PXR_Input.GetTriggerValue(PXR_Input.Controller.RightController);
        
        if (leftTrigger > 0.7f)
        {
            OnLeftTriggerPressed(leftTrigger);
        }
        
        if (rightTrigger > 0.7f)
        {
            OnRightTriggerPressed(rightTrigger);
        }
        #endif
    }
    
    private void UpdateHandTracking()
    {
        #if PICO_SDK && !UNITY_EDITOR
        // 检查手部追踪是否激活
        bool isLeftHandTracked = PXR_HandTracking.GetTrackingState(PXR_HandTracking.HandType.HandLeft);
        bool isRightHandTracked = PXR_HandTracking.GetTrackingState(PXR_HandTracking.HandType.HandRight);
        
        handTrackingActive = isLeftHandTracked || isRightHandTracked;
        
        // 根据手部追踪状态切换可见性
        if (leftController != null) leftController.SetActive(!isLeftHandTracked);
        if (rightController != null) rightController.SetActive(!isRightHandTracked);
        if (leftHand != null) leftHand.SetActive(isLeftHandTracked);
        if (rightHand != null) rightHand.SetActive(isRightHandTracked);
        
        // 更新手部位置
        if (isLeftHandTracked && leftHand != null)
        {
            UpdateHandModel(PXR_HandTracking.HandType.HandLeft, leftHand);
        }
        
        if (isRightHandTracked && rightHand != null)
        {
            UpdateHandModel(PXR_HandTracking.HandType.HandRight, rightHand);
        }
        
        // 检测手势
        if (isLeftHandTracked)
        {
            CheckHandGestures(PXR_HandTracking.HandType.HandLeft);
        }
        
        if (isRightHandTracked)
        {
            CheckHandGestures(PXR_HandTracking.HandType.HandRight);
        }
        #endif
    }
    
    #if PICO_SDK
    private void UpdateHandModel(PXR_HandTracking.HandType handType, GameObject handModel)
    {
        // 更新手部关节位置
        for (PXR_HandTracking.HandJoint joint = PXR_HandTracking.HandJoint.JointWrist;
             joint <= PXR_HandTracking.HandJoint.JointPinkyTip;
             joint++)
        {
            // 获取关节位置和旋转
            Vector3 position;
            Quaternion rotation;
            float radius;
            
            if (PXR_HandTracking.GetJointPose(joint, handType, out position, out rotation, out radius))
            {
                // 查找对应的手部骨骼
                Transform jointTransform = FindJointTransform(handModel, joint);
                if (jointTransform != null)
                {
                    jointTransform.localPosition = position;
                    jointTransform.localRotation = rotation;
                }
            }
        }
    }
    
    private Transform FindJointTransform(GameObject handModel, PXR_HandTracking.HandJoint joint)
    {
        // 这个函数需要根据您的手部模型骨骼结构来实现
        // 下面是一个简单的示例,实际上您需要映射PICO SDK的关节枚举到您模型中的骨骼名称
        string jointName = joint.ToString().Replace("Joint", "");
        
        Transform[] allTransforms = handModel.GetComponentsInChildren<Transform>();
        foreach (Transform t in allTransforms)
        {
            if (t.name.Contains(jointName))
            {
                return t;
            }
        }
        
        return null;
    }
    
    private void CheckHandGestures(PXR_HandTracking.HandType handType)
    {
        // 检测手指弯曲度判断手势
        float thumbBend = PXR_HandTracking.GetJointBendValue(PXR_HandTracking.HandJoint.JointThumbProximal, handType);
        float indexBend = PXR_HandTracking.GetJointBendValue(PXR_HandTracking.HandJoint.JointIndexProximal, handType);
        float middleBend = PXR_HandTracking.GetJointBendValue(PXR_HandTracking.HandJoint.JointMiddleProximal, handType);
        
        // 检测捏合手势 (拇指和食指)
        if (thumbBend > 0.5f && indexBend > 0.7f && middleBend < 0.3f)
        {
            OnPinchGesture(handType);
        }
        
        // 检测握拳手势
        if (thumbBend > 0.7f && indexBend > 0.7f && middleBend > 0.7f)
        {
            OnFistGesture(handType);
        }
    }
    #endif
    
    // 手势事件处理
    private void OnPinchGesture(PXR_HandTracking.HandType handType)
    {
        // 处理捏合手势
        Debug.Log($"检测到捏合手势: {handType}");
    }
    
    private void OnFistGesture(PXR_HandTracking.HandType handType)
    {
        // 处理握拳手势
        Debug.Log($"检测到握拳手势: {handType}");
    }
    
    // 按键事件处理
    private void OnButtonAPressed()
    {
        Debug.Log("按下A键");
    }
    
    private void OnButtonXPressed()
    {
        Debug.Log("按下X键");
    }
    
    private void OnLeftTriggerPressed(float value)
    {
        Debug.Log($"按下左扳机: {value}");
    }
    
    private void OnRightTriggerPressed(float value)
    {
        Debug.Log($"按下右扳机: {value}");
    }
    
    // 控制器震动
    public void TriggerHapticFeedback(bool isLeft, float intensity, float duration)
    {
        #if PICO_SDK && !UNITY_EDITOR
        PXR_Input.Controller controller = isLeft ? 
            PXR_Input.Controller.LeftController : 
            PXR_Input.Controller.RightController;
            
        PXR_Input.SetControllerVibration(intensity, 1.0f, duration, controller);
        #endif
    }
}

Pico特有功能集成

// PicoFeatureManager.cs - Pico特有功能管理
using UnityEngine;

#if PICO_SDK
using Unity.XR.PXR;
#endif

public class PicoFeatureManager : MonoBehaviour
{
    [Header("Pico特性功能")]
    [SerializeField] private bool enableEyeTracking = false;
    [SerializeField] private bool enableFaceTracking = false;
    [SerializeField] private bool enableSpeechRecognition = false;
    
    [Header("眼动追踪")]
    [SerializeField] private LayerMask eyeTrackingLayers;
    [SerializeField] private float maxEyeRayDistance = 10f;
    [SerializeField] private GameObject eyeGazeCursor;
    
    // 眼动追踪状态
    private bool eyeTrackingActive = false;
    private Vector3 eyeGazeDirection;
    private Vector3 eyeGazeOrigin;
    private GameObject lastGazedObject;
    
    private void Start()
    {
        InitializePicoFeatures();
    }
    
    private void InitializePicoFeatures()
    {
        #if PICO_SDK && !UNITY_EDITOR
        try
        {
            // 初始化眼动追踪
            if (enableEyeTracking)
            {
                PXR_EyeTracking.EnableEyeTracking(true);
                eyeTrackingActive = true;
                
                if (eyeGazeCursor != null)
                {
                    eyeGazeCursor.SetActive(true);
                }
                
                Debug.Log("Pico眼动追踪已启用");
            }
            
            // 初始化面部追踪
            if (enableFaceTracking)
            {
                PXR_FaceTracking.EnableFaceTracking(true);
                Debug.Log("Pico面部追踪已启用");
            }
            
            // 初始化语音识别
            if (enableSpeechRecognition)
            {
                // PICO语音识别初始化...
                Debug.Log("Pico语音识别已启用");
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"初始化Pico特性时出错: {e.Message}");
        }
        #endif
    }
    
    private void Update()
    {
        if (enableEyeTracking)
        {
            UpdateEyeTracking();
        }
        
        if (enableFaceTracking)
        {
            UpdateFaceTracking();
        }
    }
    
    private void UpdateEyeTracking()
    {
        #if PICO_SDK && !UNITY_EDITOR
        if (!eyeTrackingActive) return;
        
        // 获取凝视方向和原点
        if (PXR_EyeTracking.GetCombinedEyeGazePoint(out eyeGazeDirection) &&
            PXR_EyeTracking.GetCombinedEyeGazeOrigin(out eyeGazeOrigin))
        {
            // 执行射线检测
            RaycastHit hit;
            if (Physics.Raycast(eyeGazeOrigin, eyeGazeDirection, out hit, maxEyeRayDistance, eyeTrackingLayers))
            {
                // 更新光标位置
                if (eyeGazeCursor != null)
                {
                    eyeGazeCursor.transform.position = hit.point;
                    eyeGazeCursor.transform.forward = -hit.normal;
                }
                
                // 处理凝视事件
                GameObject gazedObject = hit.collider.gameObject;
                if (gazedObject != lastGazedObject)
                {
                    // 退出上一个物体
                    if (lastGazedObject != null)
                    {
                        IGazeInteractable interactable = lastGazedObject.GetComponent<IGazeInteractable>();
                        if (interactable != null)
                        {
                            interactable.OnGazeExit();
                        }
                    }
                    
                    // 进入新物体
                    IGazeInteractable newInteractable = gazedObject.GetComponent<IGazeInteractable>();
                    if (newInteractable != null)
                    {
                        newInteractable.OnGazeEnter();
                    }
                    
                    lastGazedObject = gazedObject;
                }
                
                // 凝视保持事件
                if (lastGazedObject != null)
                {
                    IGazeInteractable interactable = lastGazedObject.GetComponent<IGazeInteractable>();
                    if (interactable != null)
                    {
                        interactable.OnGazeStay();
                    }
                }
            }
            else
            {
                // 无命中,隐藏光标或移动到远处
                if (eyeGazeCursor != null)
                {
                    eyeGazeCursor.transform.position = eyeGazeOrigin + eyeGazeDirection * 5f;
                }
                
                // 如果之前有凝视物体,发送退出事件
                if (lastGazedObject != null)
                {
                    IGazeInteractable interactable = lastGazedObject.GetComponent<IGazeInteractable>();
                    if (interactable != null)
                    {
                        interactable.OnGazeExit();
                    }
                    
                    lastGazedObject = null;
                }
            }
            
            // 检测瞳孔直径变化
            float leftPupilDiameter, rightPupilDiameter;
            if (PXR_EyeTracking.GetPupilDiameter(out leftPupilDiameter, out rightPupilDiameter))
            {
                // 处理瞳孔变化事件,例如亮度自适应
                float avgPupilDiameter = (leftPupilDiameter + rightPupilDiameter) / 2f;
                OnPupilDiameterChanged(avgPupilDiameter);
            }
        }
        #endif
    }
    
    private void UpdateFaceTracking()
    {
        #if PICO_SDK && !UNITY_EDITOR
        // 获取面部追踪数据
        PXR_FaceTrackingData faceData = new PXR_FaceTrackingData();
        
        if (PXR_FaceTracking.GetFaceTrackingData(ref faceData))
        {
            // 处理面部表情数据
            ProcessFacialExpressions(faceData);
        }
        #endif
    }
    
    #if PICO_SDK
    private void ProcessFacialExpressions(PXR_FaceTrackingData faceData)
    {
        // 处理眉毛表情
        float browRaise = faceData.blendShapeWeights[(int)PXR_FaceTrackingBlendShape.BrowLowererL] +
                         faceData.blendShapeWeights[(int)PXR_FaceTrackingBlendShape.BrowLowererR];
                         
        // 处理嘴部表情                
        float jawOpen = faceData.blendShapeWeights[(int)PXR_FaceTrackingBlendShape.JawOpen];
        float smile = faceData.blendShapeWeights[(int)PXR_FaceTrackingBlendShape.MouthSmileL] +
                     faceData.blendShapeWeights[(int)PXR_FaceTrackingBlendShape.MouthSmileR];
        
        // 触发表情事件
        if (jawOpen > 0.7f)
        {
            OnMouthOpen(jawOpen);
        }
        
        if (smile > 0.6f)
        {
            OnSmile(smile);
        }
        
        if (browRaise > 0.7f)
        {
            OnBrowRaise(browRaise);
        }
    }
    #endif
    
    // 眼动追踪事件处理
    private void OnPupilDiameterChanged(float diameter)
    {
        // 处理瞳孔直径变化,例如调整应用亮度
        float normalizedDiameter = Mathf.Clamp01(diameter / 7f); // 假设最大瞳孔直径为7mm
        
        // 调整场景亮度 - 示例代码
        RenderSettings.ambientIntensity = Mathf.Lerp(0.5f, 1.0f, 1f - normalizedDiameter);
    }
    
    // 面部表情事件处理
    private void OnMouthOpen(float strength)
    {
        Debug.Log($"检测到嘴部张开: {strength}");
        // 触发与嘴部张开相关的交互
    }
    
    private void OnSmile(float strength)
    {
        Debug.Log($"检测到微笑: {strength}");
        // 触发与微笑相关的交互
    }
    
    private void OnBrowRaise(float strength)
    {
        Debug.Log($"检测到眉毛抬起: {strength}");
        // 触发与惊讶表情相关的交互
    }
}

// IGazeInteractable.cs - 眼动交互接口
public interface IGazeInteractable
{
    void OnGazeEnter();
    void OnGazeStay();
    void OnGazeExit();
}

// 示例眼动交互物体
public class GazeInteractableObject : MonoBehaviour, IGazeInteractable
{
    [SerializeField] private float gazeActivationTime = 2.0f;
    [SerializeField] private Color normalColor = Color.white;
    [SerializeField] private Color hoverColor = Color.yellow;
    [SerializeField] private Color activatedColor = Color.green;
    
    private Renderer objectRenderer;
    private float gazeTimer = 0f;
    private bool isGazed = false;
    
    private void Awake()
    {
        objectRenderer = GetComponent<Renderer>();
        objectRenderer.material.color = normalColor;
    }
    
    private void Update()
    {
        if (isGazed)
        {
            gazeTimer += Time.deltaTime;
            
            // 更新视觉反馈
            float t = Mathf.Clamp01(gazeTimer / gazeActivationTime);
            objectRenderer.material.color = Color.Lerp(hoverColor, activatedColor, t);
            
            // 激活事件
            if (gazeTimer >= gazeActivationTime)
            {
                OnGazeActivated();
                gazeTimer = 0f;
            }
        }
    }
    
    public void OnGazeEnter()
    {
        isGazed = true;
        gazeTimer = 0f;
        objectRenderer.material.color = hoverColor;
    }
    
    public void OnGazeStay()
    {
        // 持续凝视处理已在Update中
    }
    
    public void OnGazeExit()
    {
        isGazed = false;
        gazeTimer = 0f;
        objectRenderer.material.color = normalColor;
    }
    
    private void OnGazeActivated()
    {
        Debug.Log($"通过凝视激活了物体: {gameObject.name}");
        
        // 执行激活逻辑
        // 例如:播放动画、触发事件等
        transform.localScale *= 1.2f;
        
        // 重置状态
        Invoke("ResetScale", 0.5f);
    }
    
    private void ResetScale()
    {
        transform.localScale /= 1.2f;
    }
}

四、HoloLens平台开发

空间映射与理解

// HoloLensSpatialMapping.cs - HoloLens空间映射管理
using UnityEngine;
using System.Collections.Generic;

#if HOLOLENS_SDK
using UnityEngine.XR.WSA;
using UnityEngine.XR.WSA.Persistence;
#if !UNITY_2020_1_OR_NEWER
using UnityEngine.XR.WSA.Sharing;
#endif
#endif

public class HoloLensSpatialMapping : MonoBehaviour
{
    [Header("空间映射设置")]
    [SerializeField] private bool autoStartMapping = true;
    [SerializeField] private float mappingExtent = 10.0f;
    [SerializeField] private float mappingResolution = 0.05f;
    [SerializeField] private Material spatialMappingMaterial;
    [SerializeField] private bool visualizeSpatialMesh = true;
    
    [Header("空间锚点")]
    [SerializeField] private bool enableSpatialAnchors = true;
    
    // 空间映射组件
    private GameObject spatialMappingObject;
    
    // 空间锚点管理
    private Dictionary<string, GameObject> spatialAnchors = new Dictionary<string, GameObject>();
    
    #if HOLOLENS_SDK
    private SpatialMappingRenderer spatialMappingRenderer;
    private SpatialMappingCollider spatialMappingCollider;
    private WorldAnchorStore worldAnchorStore;
    #endif
    
    private void Start()
    {
        if (autoStartMapping)
        {
            InitializeSpatialMapping();
        }
        
        if (enableSpatialAnchors)
        {
            InitializeSpatialAnchors();
        }
    }
    
    private void InitializeSpatialMapping()
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        Debug.Log("初始化HoloLens空间映射");
        
        // 创建空间映射对象
        spatialMappingObject = new GameObject("SpatialMapping");
        spatialMappingObject.transform.SetParent(transform);
        
        // 添加空间映射组件
        spatialMappingRenderer = spatialMappingObject.AddComponent<SpatialMappingRenderer>();
        spatialMappingCollider = spatialMappingObject.AddComponent<SpatialMappingCollider>();
        
        // 配置空间映射设置
        SpatialMappingManager mappingManager = spatialMappingObject.AddComponent<SpatialMappingManager>();
        mappingManager.surfaceObserver.SetVolumeAsAxisAlignedBox(Vector3.zero, new Vector3(mappingExtent, mappingExtent, mappingExtent));
        mappingManager.surfaceObserver.SetObservedSurfaceNormals(true);
        
        // 设置渲染参数
        spatialMappingRenderer.material = spatialMappingMaterial;
        spatialMappingRenderer.enabled = visualizeSpatialMesh;
        
        // 设置碰撞参数
        spatialMappingCollider.enableCollisions = true;
        spatialMappingCollider.layer = LayerMask.NameToLayer("Spatial");
        spatialMappingCollider.material = new PhysicMaterial()
        {
            bounciness = 0.0f,
            dynamicFriction = 0.5f,
            staticFriction = 0.5f
        };
        
        // 启动映射
        mappingManager.StartObserver();
        
        Debug.Log("HoloLens空间映射已启动");
        #endif
    }
    
    private void InitializeSpatialAnchors()
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        Debug.Log("初始化HoloLens空间锚点");
        
        // 加载世界锚点存储
        WorldAnchorStore.GetAsync(store =>
        {
            worldAnchorStore = store;
            LoadSavedAnchors();
        });
        #endif
    }
    
    #if HOLOLENS_SDK
    private void LoadSavedAnchors()
    {
        if (worldAnchorStore == null) return;
        
        // 获取所有保存的锚点ID
        string[] anchorIds = worldAnchorStore.GetAllIds();
        
        foreach (string anchorId in anchorIds)
        {
            // 创建锚点容器
            GameObject anchorObject = new GameObject($"Anchor_{anchorId}");
            anchorObject.transform.SetParent(transform);
            
            // 加载锚点
            WorldAnchor anchor = worldAnchorStore.Load(anchorId, anchorObject);
            
            if (anchor != null)
            {
                spatialAnchors.Add(anchorId, anchorObject);
                Debug.Log($"加载空间锚点: {anchorId}");
            }
            else
            {
                Debug.LogError($"无法加载锚点: {anchorId}");
                Destroy(anchorObject);
            }
        }
    }
    #endif
    
    // 创建新的空间锚点
    public GameObject CreateSpatialAnchor(Vector3 position, string anchorId = null)
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        if (worldAnchorStore == null)
        {
            Debug.LogError("WorldAnchorStore未初始化");
            return null;
        }
        
        // 生成唯一ID
        if (string.IsNullOrEmpty(anchorId))
        {
            anchorId = System.Guid.NewGuid().ToString();
        }
        
        // 创建锚点物体
        GameObject anchorObject = new GameObject($"Anchor_{anchorId}");
        anchorObject.transform.position = position;
        anchorObject.transform.SetParent(transform);
        
        // 添加世界锚点
        WorldAnchor anchor = anchorObject.AddComponent<WorldAnchor>();
        
        // 保存到存储
        if (worldAnchorStore.Save(anchorId, anchor))
        {
            spatialAnchors.Add(anchorId, anchorObject);
            Debug.Log($"创建并保存锚点: {anchorId}");
            return anchorObject;
        }
        else
        {
            Debug.LogError($"无法保存锚点: {anchorId}");
            Destroy(anchorObject);
            return null;
        }
        #else
        // 编辑器模式下的模拟
        GameObject anchorObject = new GameObject($"EditorAnchor_{anchorId ?? System.Guid.NewGuid().ToString()}");
        anchorObject.transform.position = position;
        anchorObject.transform.SetParent(transform);
        return anchorObject;
        #endif
    }
    
    // 删除空间锚点
    public bool DeleteSpatialAnchor(string anchorId)
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        if (worldAnchorStore == null || !spatialAnchors.ContainsKey(anchorId))
        {
            return false;
        }
        
        // 删除锚点
        GameObject anchorObject = spatialAnchors[anchorId];
        bool result = worldAnchorStore.Delete(anchorId);
        
        if (result)
        {
            spatialAnchors.Remove(anchorId);
            Destroy(anchorObject);
            Debug.Log($"删除锚点: {anchorId}");
        }
        else
        {
            Debug.LogError($"无法删除锚点: {anchorId}");
        }
        
        return result;
        #else
        return true;
        #endif
    }
    
    // 设置空间映射可见性
    public void SetMappingVisibility(bool visible)
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        if (spatialMappingRenderer != null)
        {
            spatialMappingRenderer.enabled = visible;
        }
        #endif
    }
    
    // 设置空间映射物理碰撞
    public void SetMappingCollision(bool enabled)
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        if (spatialMappingCollider != null)
        {
            spatialMappingCollider.enableCollisions = enabled;
        }
        #endif
    }
    
    // 获取空间锚点位置
    public bool TryGetAnchorPosition(string anchorId, out Vector3 position)
    {
        position = Vector3.zero;
        
        if (spatialAnchors.ContainsKey(anchorId))
        {
            position = spatialAnchors[anchorId].transform.position;
            return true;
        }
        
        return false;
    }
    
    // 射线检测空间表面
    public bool RaycastSpatialMesh(Ray ray, out RaycastHit hitInfo, float maxDistance = 10f)
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        return Physics.Raycast(ray, out hitInfo, maxDistance, 
                             1 << LayerMask.NameToLayer("Spatial"));
        #else
        // 编辑器模式下模拟平面
        Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
        float distance;
        
        if (groundPlane.Raycast(ray, out distance) && distance <= maxDistance)
        {
            hitInfo = new RaycastHit
            {
                point = ray.GetPoint(distance),
                normal = Vector3.up,
                distance = distance
            };
            return true;
        }
        else
        {
            hitInfo = new RaycastHit();
            return false;
        }
        #endif
    }
}

HoloLens手势和语音交互

// HoloLensInputManager.cs - HoloLens输入管理
using UnityEngine;
using System.Collections.Generic;

#if HOLOLENS_SDK
using UnityEngine.XR.WSA.Input;
using UnityEngine.Windows.Speech;
#endif

public class HoloLensInputManager : MonoBehaviour
{
    [Header("手势设置")]
    [SerializeField] private bool enableHandGestures = true;
    [SerializeField] private float maxRaycastDistance = 5.0f;
    [SerializeField] private LayerMask interactionLayers;
    
    [Header("语音设置")]
    [SerializeField] private bool enableVoiceCommands = true;
    [SerializeField] private string[] voiceCommands;
    [SerializeField] private float confidenceThreshold = 0.8f;
    
    [Header("注视设置")]
    [SerializeField] private bool enableGazeInteraction = true;
    [SerializeField] private GameObject gazeCursor;
    
    // 凝视状态
    private GameObject gazeTarget;
    private Transform gazeTransform;
    private Vector3 gazeDirection;
    private Vector3 gazeOrigin;
    
    // 手势识别器
    #if HOLOLENS_SDK
    private GestureRecognizer gestureRecognizer;
    private KeywordRecognizer keywordRecognizer;
    #endif
    
    // 委托事件
    public delegate void GazeEventHandler(GameObject target);
    public delegate void TapEventHandler(GameObject target, Vector3 position);
    public delegate void VoiceCommandHandler(string command);
    
    public event GazeEventHandler OnGazeEnter;
    public event GazeEventHandler OnGazeExit;
    public event TapEventHandler OnTap;
    public event VoiceCommandHandler OnVoiceCommand;
    
    private void Awake()
    {
        // 设置凝视来源
        Camera mainCamera = Camera.main;
        if (mainCamera != null)
        {
            gazeTransform = mainCamera.transform;
        }
        else
        {
            Debug.LogError("未找到主摄像机,无法初始化凝视功能");
        }
    }
    
    private void Start()
    {
        if (enableHandGestures)
        {
            InitializeGestureRecognizer();
        }
        
        if (enableVoiceCommands)
        {
            InitializeVoiceRecognizer();
        }
    }
    
    private void InitializeGestureRecognizer()
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        Debug.Log("初始化HoloLens手势识别");
        
        gestureRecognizer = new GestureRecognizer();
        
        // 设置识别的手势类型
        gestureRecognizer.SetRecognizableGestures(
            GestureSettings.Tap |
            GestureSettings.Hold |
            GestureSettings.ManipulationTranslate
        );
        
        // 注册事件处理程序
        gestureRecognizer.TappedEvent += HandleTapped;
        gestureRecognizer.HoldStartedEvent += HandleHoldStarted;
        gestureRecognizer.HoldCompletedEvent += HandleHoldCompleted;
        gestureRecognizer.ManipulationStartedEvent += HandleManipulationStarted;
        gestureRecognizer.ManipulationUpdatedEvent += HandleManipulationUpdated;
        gestureRecognizer.ManipulationCompletedEvent += HandleManipulationCompleted;
        
        // 启动识别器
        gestureRecognizer.StartCapturingGestures();
        #endif
    }
    
    private void InitializeVoiceRecognizer()
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        if (voiceCommands == null || voiceCommands.Length == 0)
        {
            Debug.LogWarning("未指定语音命令");
            return;
        }
        
        Debug.Log("初始化HoloLens语音识别");
        
        // 创建关键词识别器
        keywordRecognizer = new KeywordRecognizer(voiceCommands, (ConfidenceLevel)confidenceThreshold);
        
        // 注册事件
        keywordRecognizer.OnPhraseRecognized += HandlePhraseRecognized;
        
        // 启动识别器
        keywordRecognizer.Start();
        #endif
    }
    
    private void Update()
    {
        if (enableGazeInteraction)
        {
            UpdateGaze();
        }
    }
    
    private void UpdateGaze()
    {
        if (gazeTransform == null) return;
        
        // 更新凝视参数
        gazeOrigin = gazeTransform.position;
        gazeDirection = gazeTransform.forward;
        
        // 使用射线检测
        RaycastHit hitInfo;
        GameObject newGazeTarget = null;
        
        if (Physics.Raycast(gazeOrigin, gazeDirection, out hitInfo, maxRaycastDistance, interactionLayers))
        {
            newGazeTarget = hitInfo.collider.gameObject;
            
            // 更新光标位置
            if (gazeCursor != null)
            {
                gazeCursor.transform.position = hitInfo.point;
                gazeCursor.transform.forward = -hitInfo.normal;
                gazeCursor.SetActive(true);
            }
        }
        else if (gazeCursor != null)
        {
            // 没有命中,将光标放在远处
            gazeCursor.transform.position = gazeOrigin + gazeDirection * 2.0f;
            gazeCursor.transform.forward = gazeDirection;
            gazeCursor.SetActive(true);
        }
        
        // 如果凝视目标改变,触发事件
        if (gazeTarget != newGazeTarget)
        {
            // 退出旧目标
            if (gazeTarget != null)
            {
                OnGazeExit?.Invoke(gazeTarget);
                
                IHoloLensGazeInteractable interactable = gazeTarget.GetComponent<IHoloLensGazeInteractable>();
                if (interactable != null)
                {
                    interactable.OnGazeExit();
                }
            }
            
            // 进入新目标
            if (newGazeTarget != null)
            {
                OnGazeEnter?.Invoke(newGazeTarget);
                
                IHoloLensGazeInteractable interactable = newGazeTarget.GetComponent<IHoloLensGazeInteractable>();
                if (interactable != null)
                {
                    interactable.OnGazeEnter();
                }
            }
            
            gazeTarget = newGazeTarget;
        }
    }
    
    #if HOLOLENS_SDK
    // 手势事件处理
    private void HandleTapped(InteractionSourceKind source, int tapCount, Ray headRay)
    {
        if (gazeTarget != null)
        {
            // 触发点击事件
            OnTap?.Invoke(gazeTarget, gazeOrigin + gazeDirection * 2.0f);
            
            // 检查组件实现
            IHoloLensTapHandler tapHandler = gazeTarget.GetComponent<IHoloLensTapHandler>();
            if (tapHandler != null)
            {
                tapHandler.OnTapped();
            }
        }
    }
    
    private void HandleHoldStarted(InteractionSourceKind source, Ray headRay)
    {
        if (gazeTarget != null)
        {
            IHoloLensHoldHandler holdHandler = gazeTarget.GetComponent<IHoloLensHoldHandler>();
            if (holdHandler != null)
            {
                holdHandler.OnHoldStarted();
            }
        }
    }
    
    private void HandleHoldCompleted(InteractionSourceKind source, Ray headRay)
    {
        if (gazeTarget != null)
        {
            IHoloLensHoldHandler holdHandler = gazeTarget.GetComponent<IHoloLensHoldHandler>();
            if (holdHandler != null)
            {
                holdHandler.OnHoldCompleted();
            }
        }
    }
    
    private void HandleManipulationStarted(InteractionSourceKind source, Vector3 position, Ray headRay)
    {
        if (gazeTarget != null)
        {
            IHoloLensManipulationHandler manipHandler = gazeTarget.GetComponent<IHoloLensManipulationHandler>();
            if (manipHandler != null)
            {
                manipHandler.OnManipulationStarted(position);
            }
        }
    }
    
    private void HandleManipulationUpdated(InteractionSourceKind source, Vector3 position, Ray headRay)
    {
        if (gazeTarget != null)
        {
            IHoloLensManipulationHandler manipHandler = gazeTarget.GetComponent<IHoloLensManipulationHandler>();
            if (manipHandler != null)
            {
                manipHandler.OnManipulationUpdated(position);
            }
        }
    }
    
    private void HandleManipulationCompleted(InteractionSourceKind source, Vector3 position, Ray headRay)
    {
        if (gazeTarget != null)
        {
            IHoloLensManipulationHandler manipHandler = gazeTarget.GetComponent<IHoloLensManipulationHandler>();
            if (manipHandler != null)
            {
                manipHandler.OnManipulationCompleted(position);
            }
        }
    }
    
    // 语音事件处理
    private void HandlePhraseRecognized(PhraseRecognizedEventArgs args)
    {
        string command = args.text;
        float confidence = args.confidence;
        
        Debug.Log($"识别到语音命令: {command} (置信度: {confidence})");
        
        // 触发语音命令事件
        OnVoiceCommand?.Invoke(command);
        
        // 查找并触发命令处理程序
        if (gazeTarget != null)
        {
            IHoloLensVoiceHandler voiceHandler = gazeTarget.GetComponent<IHoloLensVoiceHandler>();
            if (voiceHandler != null)
            {
                voiceHandler.OnVoiceCommand(command);
            }
        }
        
        // 全局命令处理
        ProcessGlobalVoiceCommand(command);
    }
    #endif
    
    // 处理全局语音命令
    private void ProcessGlobalVoiceCommand(string command)
    {
        switch (command.ToLower())
        {
            case "reset":
                // 重置场景
                Debug.Log("重置场景");
                break;
                
            case "menu":
                // 显示菜单
                Debug.Log("显示菜单");
                break;
                
            // 更多全局命令...
        }
    }
    
    private void OnDestroy()
    {
        #if HOLOLENS_SDK
        // 清理手势识别器
        if (gestureRecognizer != null)
        {
            gestureRecognizer.TappedEvent -= HandleTapped;
            gestureRecognizer.HoldStartedEvent -= HandleHoldStarted;
            gestureRecognizer.HoldCompletedEvent -= HandleHoldCompleted;
            gestureRecognizer.ManipulationStartedEvent -= HandleManipulationStarted;
            gestureRecognizer.ManipulationUpdatedEvent -= HandleManipulationUpdated;
            gestureRecognizer.ManipulationCompletedEvent -= HandleManipulationCompleted;
            
            gestureRecognizer.StopCapturingGestures();
            gestureRecognizer.Dispose();
        }
        
        // 清理语音识别器
        if (keywordRecognizer != null)
        {
            keywordRecognizer.OnPhraseRecognized -= HandlePhraseRecognized;
            
            if (keywordRecognizer.IsRunning)
            {
                keywordRecognizer.Stop();
            }
            
            keywordRecognizer.Dispose();
        }
        #endif
    }
}

// HoloLens交互接口
public interface IHoloLensGazeInteractable
{
    void OnGazeEnter();
    void OnGazeExit();
}

public interface IHoloLensTapHandler
{
    void OnTapped();
}

public interface IHoloLensHoldHandler
{
    void OnHoldStarted();
    void OnHoldCompleted();
}

public interface IHoloLensManipulationHandler
{
    void OnManipulationStarted(Vector3 position);
    void OnManipulationUpdated(Vector3 position);
    void OnManipulationCompleted(Vector3 position);
}

public interface IHoloLensVoiceHandler
{
    void OnVoiceCommand(string command);
}

// HoloLens交互物体示例
public class HoloLensInteractableObject : MonoBehaviour, 
    IHoloLensGazeInteractable, 
    IHoloLensTapHandler, 
    IHoloLensManipulationHandler,
    IHoloLensVoiceHandler
{
    [SerializeField] private Color normalColor = Color.white;
    [SerializeField] private Color hoverColor = Color.yellow;
    [SerializeField] private Color selectedColor = Color.green;
    [SerializeField] private bool isManipulatable = true;
    
    private Renderer objRenderer;
    private bool isSelected = false;
    private Vector3 manipulationStartPosition;
    private Vector3 objectStartPosition;
    
    private void Awake()
    {
        objRenderer = GetComponent<Renderer>();
        if (objRenderer != null)
        {
            objRenderer.material.color = normalColor;
        }
    }
    
    public void OnGazeEnter()
    {
        if (objRenderer != null && !isSelected)
        {
            objRenderer.material.color = hoverColor;
        }
    }
    
    public void OnGazeExit()
    {
        if (objRenderer != null && !isSelected)
        {
            objRenderer.material.color = normalColor;
        }
    }
    
    public void OnTapped()
    {
        Debug.Log($"点击了物体: {gameObject.name}");
        
        isSelected = !isSelected;
        
        if (objRenderer != null)
        {
            objRenderer.material.color = isSelected ? selectedColor : normalColor;
        }
        
        // 根据选择状态执行操作
        if (isSelected)
        {
            OnSelected();
        }
        else
        {
            OnDeselected();
        }
    }
    
    public void OnHoldStarted()
    {
        Debug.Log($"开始长按物体: {gameObject.name}");
        // 显示上下文菜单或执行特定操作
    }
    
    public void OnHoldCompleted()
    {
        Debug.Log($"完成长按物体: {gameObject.name}");
        // 隐藏上下文菜单或完成特定操作
    }
    
    public void OnManipulationStarted(Vector3 position)
    {
        if (!isManipulatable) return;
        
        Debug.Log($"开始操作物体: {gameObject.name}");
        
        manipulationStartPosition = position;
        objectStartPosition = transform.position;
    }
    
    public void OnManipulationUpdated(Vector3 position)
    {
        if (!isManipulatable) return;
        
        // 计算移动距离
        Vector3 delta = position - manipulationStartPosition;
        
        // 更新物体位置
        transform.position = objectStartPosition + delta;
    }
    
    public void OnManipulationCompleted(Vector3 position)
    {
        if (!isManipulatable) return;
        
        Debug.Log($"完成操作物体: {gameObject.name}");
        
        // 可以在这里执行其他完成操作,例如吸附到网格等
    }
    
    public void OnVoiceCommand(string command)
    {
        Debug.Log($"物体 {gameObject.name} 接收到语音命令: {command}");
        
        switch (command.ToLower())
        {
            case "rotate":
                // 旋转物体
                transform.Rotate(0, 45, 0);
                break;
                
            case "bigger":
                // 放大物体
                transform.localScale *= 1.2f;
                break;
                
            case "smaller":
                // 缩小物体
                transform.localScale *= 0.8f;
                break;
                
            case "reset":
                // 重置物体
                transform.localScale = Vector3.one;
                transform.rotation = Quaternion.identity;
                break;
        }
    }
    
    private void OnSelected()
    {
        // 特定选择效果
        transform.localScale *= 1.1f;
    }
    
    private void OnDeselected()
    {
        // 恢复正常效果
        transform.localScale /= 1.1f;
    }
}

五、通用XR交互系统

跨平台输入系统

// XRInputSystem.cs - 跨平台XR输入系统
using UnityEngine;
using UnityEngine.XR;

public class XRInputSystem : MonoBehaviour
{
    [Header("平台适配")]
    [SerializeField] private XRPlatformType targetPlatform;
    [SerializeField] private bool autoDetectPlatform = true;
    
    [Header("输入映射")]
    [SerializeField] private Transform leftHandTransform;
    [SerializeField] private Transform rightHandTransform;
    [SerializeField] private Transform headTransform;
    
    [Header("控制器模型")]
    [SerializeField] private GameObject questControllerPrefab;
    [SerializeField] private GameObject picoControllerPrefab;
    [SerializeField] private GameObject hololensHandPrefab;
    
    // 当前激活的控制器模型
    private GameObject leftControllerModel;
    private GameObject rightControllerModel;
    
    // 输入状态
    public Vector2 LeftThumbstick { get; private set; }
    public Vector2 RightThumbstick { get; private set; }
    public float LeftTrigger { get; private set; }
    public float RightTrigger { get; private set; }
    public float LeftGrip { get; private set; }
    public float RightGrip { get; private set; }
    public bool LeftPrimaryButton { get; private set; }
    public bool RightPrimaryButton { get; private set; }
    public bool LeftSecondaryButton { get; private set; }
    public bool RightSecondaryButton { get; private set; }
    
    // 输入事件委托
    public delegate void ButtonEventHandler(string buttonName, bool value);
    public delegate void AxisEventHandler(string axisName, float value);
    public delegate void Vector2EventHandler(string axisName, Vector2 value);
    
    public event ButtonEventHandler OnButtonStateChanged;
    public event AxisEventHandler OnAxisChanged;
    public event Vector2EventHandler OnVector2Changed;
    
    private void Awake()
    {
        if (autoDetectPlatform && XRPlatformManager.Instance != null)
        {
            targetPlatform = XRPlatformManager.Instance.CurrentPlatform;
        }
        
        InitializeInputSystem();
    }
    
    private void InitializeInputSystem()
    {
        Debug.Log($"初始化XR输入系统,平台: {targetPlatform}");
        
        // 确保我们有头部Transform
        if (headTransform == null)
        {
            Camera mainCamera = Camera.main;
            if (mainCamera != null)
            {
                headTransform = mainCamera.transform;
            }
        }
        
        // 根据平台初始化控制器模型
        switch (targetPlatform)
        {
            case XRPlatformType.Quest:
                InitializeQuestControllers();
                break;
                
            case XRPlatformType.Pico:
                InitializePicoControllers();
                break;
                
            case XRPlatformType.HoloLens:
                InitializeHoloLensControllers();
                break;
                
            default:
                Debug.LogWarning("未识别的XR平台,使用默认输入设置");
                break;
        }
    }
    
    private void InitializeQuestControllers()
    {
        #if QUEST_SDK
        // 初始化Quest控制器
        if (questControllerPrefab != null)
        {
            if (leftHandTransform == null)
            {
                GameObject leftHand = new GameObject("LeftHandAnchor");
                leftHandTransform = leftHand.transform;
            }
            
            if (rightHandTransform == null)
            {
                GameObject rightHand = new GameObject("RightHandAnchor");
                rightHandTransform = rightHand.transform;
            }
            
            leftControllerModel = Instantiate(questControllerPrefab, leftHandTransform);
            leftControllerModel.name = "LeftQuestController";
            
            rightControllerModel = Instantiate(questControllerPrefab, rightHandTransform);
            rightControllerModel.name = "RightQuestController";
            
            // 确保右手模型是右手版本
            rightControllerModel.transform.localScale = new Vector3(-1, 1, 1);
        }
        #endif
    }
    
    private void InitializePicoControllers()
    {
        #if PICO_SDK
        // 初始化Pico控制器
        if (picoControllerPrefab != null)
        {
            if (leftHandTransform == null)
            {
                GameObject leftHand = new GameObject("LeftHandAnchor");
                leftHandTransform = leftHand.transform;
            }
            
            if (rightHandTransform == null)
            {
                GameObject rightHand = new GameObject("RightHandAnchor");
                rightHandTransform = rightHand.transform;
            }
            
            leftControllerModel = Instantiate(picoControllerPrefab, leftHandTransform);
            leftControllerModel.name = "LeftPicoController";
            
            rightControllerModel = Instantiate(picoControllerPrefab, rightHandTransform);
            rightControllerModel.name = "RightPicoController";
            
            // 确保右手模型是右手版本
            rightControllerModel.transform.localScale = new Vector3(-1, 1, 1);
        }
        #endif
    }
    
    private void InitializeHoloLensControllers()
    {
        #if HOLOLENS_SDK
        // 初始化HoloLens手势模型
        if (hololensHandPrefab != null)
        {
            if (leftHandTransform == null)
            {
                GameObject leftHand = new GameObject("LeftHandAnchor");
                leftHandTransform = leftHand.transform;
                leftHandTransform.parent = headTransform;
                leftHandTransform.localPosition = new Vector3(-0.2f, -0.2f, 0.5f);
            }
            
            if (rightHandTransform == null)
            {
                GameObject rightHand = new GameObject("RightHandAnchor");
                rightHandTransform = rightHand.transform;
                rightHandTransform.parent = headTransform;
                rightHandTransform.localPosition = new Vector3(0.2f, -0.2f, 0.5f);
            }
            
            leftControllerModel = Instantiate(hololensHandPrefab, leftHandTransform);
            leftControllerModel.name = "LeftHoloLensHand";
            
            rightControllerModel = Instantiate(hololensHandPrefab, rightHandTransform);
            rightControllerModel.name = "RightHoloLensHand";
            
            // 设置为右手
            rightControllerModel.transform.localScale = new Vector3(-1, 1, 1);
        }
        #endif
    }
    
    private void Update()
    {
        UpdateControllerStates();
        UpdateInputStates();
    }
    
    private void UpdateControllerStates()
    {
        switch (targetPlatform)
        {
            case XRPlatformType.Quest:
                UpdateQuestControllers();
                break;
                
            case XRPlatformType.Pico:
                UpdatePicoControllers();
                break;
                
            case XRPlatformType.HoloLens:
                UpdateHoloLensControllers();
                break;
                
            default:
                // 默认行为可以是使用通用XR输入API
                UpdateGenericXRControllers();
                break;
        }
    }
    
    private void UpdateQuestControllers()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        // 更新Quest控制器位置和旋转
        if (leftHandTransform != null)
        {
            leftHandTransform.localPosition = OVRInput.GetLocalControllerPosition(OVRInput.Controller.LTouch);
            leftHandTransform.localRotation = OVRInput.GetLocalControllerRotation(OVRInput.Controller.LTouch);
        }
        
        if (rightHandTransform != null)
        {
            rightHandTransform.localPosition = OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch);
            rightHandTransform.localRotation = OVRInput.GetLocalControllerRotation(OVRInput.Controller.RTouch);
        }
        
        // 更新输入状态
        LeftThumbstick = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.LTouch);
        RightThumbstick = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.RTouch);
        
        LeftTrigger = OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, OVRInput.Controller.LTouch);
        RightTrigger = OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, OVRInput.Controller.RTouch);
        
        LeftGrip = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, OVRInput.Controller.LTouch);
        RightGrip = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, OVRInput.Controller.RTouch);
        
        bool newLeftPrimaryButton = OVRInput.Get(OVRInput.Button.One, OVRInput.Controller.LTouch);
        bool newRightPrimaryButton = OVRInput.Get(OVRInput.Button.One, OVRInput.Controller.RTouch);
        
        bool newLeftSecondaryButton = OVRInput.Get(OVRInput.Button.Two, OVRInput.Controller.LTouch);
        bool newRightSecondaryButton = OVRInput.Get(OVRInput.Button.Two, OVRInput.Controller.RTouch);
        
        // 检测按钮状态变化并触发事件
        CheckButtonStateChanged("LeftPrimary", newLeftPrimaryButton, ref LeftPrimaryButton);
        CheckButtonStateChanged("RightPrimary", newRightPrimaryButton, ref RightPrimaryButton);
        CheckButtonStateChanged("LeftSecondary", newLeftSecondaryButton, ref LeftSecondaryButton);
        CheckButtonStateChanged("RightSecondary", newRightSecondaryButton, ref RightSecondaryButton);
        #endif
    }
    
    private void UpdatePicoControllers()
    {
        #if PICO_SDK && !UNITY_EDITOR
        // 更新Pico控制器位置和旋转
        if (leftHandTransform != null)
        {
            leftHandTransform.localPosition = PXR_Input.GetLocalPosition(PXR_Input.Controller.LeftController);
            leftHandTransform.localRotation = PXR_Input.GetLocalRotation(PXR_Input.Controller.LeftController);
        }
        
        if (rightHandTransform != null)
        {
            rightHandTransform.localPosition = PXR_Input.GetLocalPosition(PXR_Input.Controller.RightController);
            rightHandTransform.localRotation = PXR_Input.GetLocalRotation(PXR_Input.Controller.RightController);
        }
        
        // 更新输入状态
        LeftThumbstick = PXR_Input.GetAxis2D(PXR_Input.Controller.LeftController);
        RightThumbstick = PXR_Input.GetAxis2D(PXR_Input.Controller.RightController);
        
        LeftTrigger = PXR_Input.GetTriggerValue(PXR_Input.Controller.LeftController);
        RightTrigger = PXR_Input.GetTriggerValue(PXR_Input.Controller.RightController);
        
        LeftGrip = PXR_Input.GetGripValue(PXR_Input.Controller.LeftController);
        RightGrip = PXR_Input.GetGripValue(PXR_Input.Controller.RightController);
        
        bool newLeftPrimaryButton = PXR_Input.GetDown(PXR_Input.ButtonMask.X, PXR_Input.Controller.LeftController);
        bool newRightPrimaryButton = PXR_Input.GetDown(PXR_Input.ButtonMask.A, PXR_Input.Controller.RightController);
        
        bool newLeftSecondaryButton = PXR_Input.GetDown(PXR_Input.ButtonMask.Y, PXR_Input.Controller.LeftController);
        bool newRightSecondaryButton = PXR_Input.GetDown(PXR_Input.ButtonMask.B, PXR_Input.Controller.RightController);
        
        // 检测按钮状态变化并触发事件
        CheckButtonStateChanged("LeftPrimary", newLeftPrimaryButton, ref LeftPrimaryButton);
        CheckButtonStateChanged("RightPrimary", newRightPrimaryButton, ref RightPrimaryButton);
        CheckButtonStateChanged("LeftSecondary", newLeftSecondaryButton, ref LeftSecondaryButton);
        CheckButtonStateChanged("RightSecondary", newRightSecondaryButton, ref RightSecondaryButton);
        #endif
    }
    
    private void UpdateHoloLensControllers()
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        // HoloLens使用手势而非真实控制器
        // 这里我们使用非常简化的模拟
        
        // 从手势识别获取手部位置
        // 实际应用中使用HandsManager或GestureRecognizer
        
        // 模拟简单的输入映射,例如点击作为主按钮
        bool newLeftPrimaryButton = false;
        bool newRightPrimaryButton = false;
        
        // 获取手势识别器的数据
        InteractionManager.InteractionSourcePressType pressType;
        if (InteractionManager.TryGetInteractionSourcePressType(out pressType))
        {
            if (pressType == InteractionManager.InteractionSourcePressType.Select)
            {
                InteractionManager.InteractionSourceState sourceState;
                if (InteractionManager.TryGetInteractionSourceState(out sourceState))
                {
                    // 根据手的位置确定左右
                    if (sourceState.sourcePose.position.x < 0)
                    {
                        newLeftPrimaryButton = true;
                    }
                    else
                    {
                        newRightPrimaryButton = true;
                    }
                }
            }
        }
        
        // 检测按钮状态变化并触发事件
        CheckButtonStateChanged("LeftPrimary", newLeftPrimaryButton, ref LeftPrimaryButton);
        CheckButtonStateChanged("RightPrimary", newRightPrimaryButton, ref RightPrimaryButton);
        #endif
    }
    
    private void UpdateGenericXRControllers()
    {
        InputDevice leftHandDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
        InputDevice rightHandDevice = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
        
        // 更新左手控制器
        if (leftHandDevice.isValid && leftHandTransform != null)
        {
            Vector3 position;
            Quaternion rotation;
            
            if (leftHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out position))
            {
                leftHandTransform.localPosition = position;
            }
            
            if (leftHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out rotation))
            {
                leftHandTransform.localRotation = rotation;
            }
            
            // 获取输入
            Vector2 thumbstick;
            if (leftHandDevice.TryGetFeatureValue(CommonUsages.primary2DAxis, out thumbstick))
            {
                LeftThumbstick = thumbstick;
            }
            
            float trigger;
            if (leftHandDevice.TryGetFeatureValue(CommonUsages.trigger, out trigger))
            {
                LeftTrigger = trigger;
            }
            
            float grip;
            if (leftHandDevice.TryGetFeatureValue(CommonUsages.grip, out grip))
            {
                LeftGrip = grip;
            }
            
            bool primaryButton;
            if (leftHandDevice.TryGetFeatureValue(CommonUsages.primaryButton, out primaryButton))
            {
                CheckButtonStateChanged("LeftPrimary", primaryButton, ref LeftPrimaryButton);
            }
            
            bool secondaryButton;
            if (leftHandDevice.TryGetFeatureValue(CommonUsages.secondaryButton, out secondaryButton))
            {
                CheckButtonStateChanged("LeftSecondary", secondaryButton, ref LeftSecondaryButton);
            }
        }
        
        // 更新右手控制器
        if (rightHandDevice.isValid && rightHandTransform != null)
        {
            Vector3 position;
            Quaternion rotation;
            
            if (rightHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out position))
            {
                rightHandTransform.localPosition = position;
            }
            
            if (rightHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out rotation))
            {
                rightHandTransform.localRotation = rotation;
            }
            
            // 获取输入
            Vector2 thumbstick;
            if (rightHandDevice.TryGetFeatureValue(CommonUsages.primary2DAxis, out thumbstick))
            {
                RightThumbstick = thumbstick;
            }
            
            float trigger;
            if (rightHandDevice.TryGetFeatureValue(CommonUsages.trigger, out trigger))
            {
                RightTrigger = trigger;
            }
            
            float grip;
            if (rightHandDevice.TryGetFeatureValue(CommonUsages.grip, out grip))
            {
                RightGrip = grip;
            }
            
            bool primaryButton;
            if (rightHandDevice.TryGetFeatureValue(CommonUsages.primaryButton, out primaryButton))
            {
                CheckButtonStateChanged("RightPrimary", primaryButton, ref RightPrimaryButton);
            }
            
            bool secondaryButton;
            if (rightHandDevice.TryGetFeatureValue(CommonUsages.secondaryButton, out secondaryButton))
            {
                CheckButtonStateChanged("RightSecondary", secondaryButton, ref RightSecondaryButton);
            }
        }
    }
    
    // 检查按钮状态变化
    private void CheckButtonStateChanged(string buttonName, bool newState, ref bool currentState)
    {
        if (newState != currentState)
        {
            currentState = newState;
            OnButtonStateChanged?.Invoke(buttonName, currentState);
        }
    }
    
    // 更新所有输入状态并触发事件
    private void UpdateInputStates()
    {
        // 轴向输入检测
        static void CheckAxis(string name, float value, float previousValue, AxisEventHandler handler, float threshold = 0.01f)
        {
            if (Mathf.Abs(value - previousValue) > threshold)
            {
                handler?.Invoke(name, value);
            }
        }
        
        // 二维轴向输入检测
        static void CheckVector2(string name, Vector2 value, Vector2 previousValue, Vector2EventHandler handler, float threshold = 0.01f)
        {
            if (Vector2.Distance(value, previousValue) > threshold)
            {
                handler?.Invoke(name, value);
            }
        }
        
        // 记录当前值用于比较
        static float prevLeftTrigger = LeftTrigger;
        static float prevRightTrigger = RightTrigger;
        static float prevLeftGrip = LeftGrip;
        static float prevRightGrip = RightGrip;
        static Vector2 prevLeftThumbstick = LeftThumbstick;
        static Vector2 prevRightThumbstick = RightThumbstick;
        
        // 检查轴输入变化
        CheckAxis("LeftTrigger", LeftTrigger, prevLeftTrigger, OnAxisChanged);
        CheckAxis("RightTrigger", RightTrigger, prevRightTrigger, OnAxisChanged);
        CheckAxis("LeftGrip", LeftGrip, prevLeftGrip, OnAxisChanged);
        CheckAxis("RightGrip", RightGrip, prevRightGrip, OnAxisChanged);
        
        // 检查二维轴输入变化
        CheckVector2("LeftThumbstick", LeftThumbstick, prevLeftThumbstick, OnVector2Changed);
        CheckVector2("RightThumbstick", RightThumbstick, prevRightThumbstick, OnVector2Changed);
        
        // 更新之前的值
        prevLeftTrigger = LeftTrigger;
        prevRightTrigger = RightTrigger;
        prevLeftGrip = LeftGrip;
        prevRightGrip = RightGrip;
        prevLeftThumbstick = LeftThumbstick;
        prevRightThumbstick = RightThumbstick;
    }
}

跨平台交互系统

// XRInteractionSystem.cs - 跨平台XR交互系统
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;

public class XRInteractionSystem : MonoBehaviour
{
    [Header("交互设置")]
    [SerializeField] private float interactionDistance = 0.3f;
    [SerializeField] private LayerMask interactableLayers;
    [SerializeField] private Transform leftInteractor;
    [SerializeField] private Transform rightInteractor;
    [SerializeField] private bool useRayInteraction = true;
    [SerializeField] private float rayLength = 5.0f;
    [SerializeField] private GameObject leftRayVisual;
    [SerializeField] private GameObject rightRayVisual;
    
    [Header("视觉反馈")]
    [SerializeField] private GameObject highlightPrefab;
    [SerializeField] private GameObject selectPrefab;
    
    // 交互状态
    private XRInteractable leftHoverTarget;
    private XRInteractable rightHoverTarget;
    private XRInteractable leftSelectedTarget;
    private XRInteractable rightSelectedTarget;
    
    // 高亮对象
    private GameObject leftHighlight;
    private GameObject rightHighlight;
    private GameObject leftSelectIndicator;
    private GameObject rightSelectIndicator;
    
    // 交互历史队列(用于过滤抖动)
    private Queue<XRInteractable> leftHoverHistory = new Queue<XRInteractable>();
    private Queue<XRInteractable> rightHoverHistory = new Queue<XRInteractable>();
    private const int hoverHistoryLength = 3;
    
    // XR输入系统引用
    private XRInputSystem xrInput;
    
    private void Awake()
    {
        xrInput = FindObjectOfType<XRInputSystem>();
        
        if (xrInput == null)
        {
            Debug.LogWarning("未找到XRInputSystem,交互系统可能无法正常工作");
        }
        
        // 初始化视觉反馈
        if (highlightPrefab != null)
        {
            leftHighlight = Instantiate(highlightPrefab);
            rightHighlight = Instantiate(highlightPrefab);
            
            leftHighlight.SetActive(false);
            rightHighlight.SetActive(false);
        }
        
        if (selectPrefab != null)
        {
            leftSelectIndicator = Instantiate(selectPrefab);
            rightSelectIndicator = Instantiate(selectPrefab);
            
            leftSelectIndicator.SetActive(false);
            rightSelectIndicator.SetActive(false);
        }
        
        // 初始化射线可视化
        if (leftRayVisual != null) leftRayVisual.SetActive(false);
        if (rightRayVisual != null) rightRayVisual.SetActive(false);
    }
    
    private void Start()
    {
        // 订阅输入事件
        if (xrInput != null)
        {
            xrInput.OnButtonStateChanged += HandleButtonStateChanged;
            xrInput.OnAxisChanged += HandleAxisChanged;
        }
    }
    
    private void Update()
    {
        // 更新悬停逻辑
        if (leftInteractor != null) UpdateInteractorHover(leftInteractor, true);
        if (rightInteractor != null) UpdateInteractorHover(rightInteractor, false);
        
        // 更新射线可视化
        UpdateRayVisuals();
        
        // 更新选中物体位置
        UpdateSelectedObjects();
    }
    
    private void UpdateInteractorHover(Transform interactor, bool isLeft)
    {
        XRInteractable nearestInteractable = null;
        
        if (useRayInteraction)
        {
            // 射线交互
            nearestInteractable = GetRayInteractable(interactor, isLeft);
        }
        else
        {
            // 近距离交互
            nearestInteractable = GetNearestInteractable(interactor, isLeft);
        }
        
        // 使用历史队列过滤抖动
        Queue<XRInteractable> history = isLeft ? leftHoverHistory : rightHoverHistory;
        
        if (nearestInteractable != null)
        {
            history.Enqueue(nearestInteractable);
            
            while (history.Count > hoverHistoryLength)
            {
                history.Dequeue();
            }
            
            // 检查历史记录中是否有相同的对象
            bool isConsistent = true;
            XRInteractable firstItem = null;
            
            foreach (XRInteractable item in history)
            {
                if (firstItem == null)
                {
                    firstItem = item;
                }
                else if (firstItem != item)
                {
                    isConsistent = false;
                    break;
                }
            }
            
            // 如果历史记录中的对象一致,更新悬停目标
            if (isConsistent && history.Count == hoverHistoryLength)
            {
                UpdateHoverTarget(firstItem, isLeft);
            }
        }
        else
        {
            // 清空历史
            history.Clear();
            
            // 更新悬停目标为null
            UpdateHoverTarget(null, isLeft);
        }
    }
    
    private XRInteractable GetNearestInteractable(Transform interactor, bool isLeft)
    {
        // 查找附近的可交互对象
        Collider[] colliders = Physics.OverlapSphere(interactor.position, interactionDistance, interactableLayers);
        
        XRInteractable nearestInteractable = null;
        float minDistance = float.MaxValue;
        
        foreach (Collider collider in colliders)
        {
            XRInteractable interactable = collider.GetComponent<XRInteractable>();
            
            if (interactable != null && interactable.IsInteractable)
            {
                float distance = Vector3.Distance(interactor.position, interactable.transform.position);
                
                if (distance < minDistance)
                {
                    minDistance = distance;
                    nearestInteractable = interactable;
                }
            }
        }
        
        return nearestInteractable;
    }
    
    private XRInteractable GetRayInteractable(Transform interactor, bool isLeft)
    {
        RaycastHit hit;
        
        if (Physics.Raycast(interactor.position, interactor.forward, out hit, rayLength, interactableLayers))
        {
            XRInteractable interactable = hit.collider.GetComponent<XRInteractable>();
            
            if (interactable != null && interactable.IsInteractable)
            {
                // 更新射线可视化对象
                GameObject rayVisual = isLeft ? leftRayVisual : rightRayVisual;
                
                if (rayVisual != null)
                {
                    rayVisual.SetActive(true);
                    rayVisual.transform.position = interactor.position;
                    rayVisual.transform.rotation = Quaternion.LookRotation(hit.point - interactor.position);
                    rayVisual.transform.localScale = new Vector3(0.01f, 0.01f, hit.distance);
                }
                
                return interactable;
            }
        }
        
        return null;
    }
    
    private void UpdateRayVisuals()
    {
        if (!useRayInteraction)
        {
            // 禁用射线可视化
            if (leftRayVisual != null) leftRayVisual.SetActive(false);
            if (rightRayVisual != null) rightRayVisual.SetActive(false);
            return;
        }
        
        // 根据是否有目标更新射线
        if (leftHoverTarget == null && leftRayVisual != null)
        {
            leftRayVisual.SetActive(false);
        }
        
        if (rightHoverTarget == null && rightRayVisual != null)
        {
            rightRayVisual.SetActive(false);
        }
    }
    
    private void UpdateHoverTarget(XRInteractable newTarget, bool isLeft)
    {
        XRInteractable currentTarget = isLeft ? leftHoverTarget : rightHoverTarget;
        GameObject highlight = isLeft ? leftHighlight : rightHighlight;
        
        // 如果目标没有变化,不需要任何操作
        if (newTarget == currentTarget) return;
        
        // 退出旧目标
        if (currentTarget != null)
        {
            currentTarget.OnHoverExit(isLeft);
            
            // 隐藏高亮
            if (highlight != null)
            {
                highlight.SetActive(false);
            }
        }
        
        // 进入新目标
        if (newTarget != null)
        {
            newTarget.OnHoverEnter(isLeft);
            
            // 显示高亮
            if (highlight != null)
            {
                highlight.SetActive(true);
                highlight.transform.position = newTarget.transform.position;
                highlight.transform.rotation = newTarget.transform.rotation;
                
                // 尝试适配高亮到目标大小
                Renderer targetRenderer = newTarget.GetComponent<Renderer>();
                if (targetRenderer != null)
                {
                    Bounds bounds = targetRenderer.bounds;
                    highlight.transform.localScale = bounds.size * 1.05f;
                }
                else
                {
                    highlight.transform.localScale = newTarget.transform.localScale * 1.05f;
                }
            }
        }
        
        // 更新当前目标
        if (isLeft)
        {
            leftHoverTarget = newTarget;
        }
        else
        {
            rightHoverTarget = newTarget;
        }
    }
    
    private void HandleButtonStateChanged(string buttonName, bool value)
    {
        // 根据按钮触发不同的交互
        switch (buttonName)
        {
            case "LeftPrimary":
                if (value) SelectObject(true); else ReleaseObject(true);
                break;
                
            case "RightPrimary":
                if (value) SelectObject(false); else ReleaseObject(false);
                break;
                
            case "LeftSecondary":
                if (value && leftSelectedTarget != null)
                {
                    leftSelectedTarget.OnSecondaryAction(true);
                }
                break;
                
            case "RightSecondary":
                if (value && rightSelectedTarget != null)
                {
                    rightSelectedTarget.OnSecondaryAction(false);
                }
                break;
        }
    }
    
    private void HandleAxisChanged(string axisName, float value)
    {
        // 将轴输入传递给选中对象
        switch (axisName)
        {
            case "LeftTrigger":
                if (leftSelectedTarget != null)
                {
                    leftSelectedTarget.OnTriggerChanged(value, true);
                }
                break;
                
            case "RightTrigger":
                if (rightSelectedTarget != null)
                {
                    rightSelectedTarget.OnTriggerChanged(value, false);
                }
                break;
                
            case "LeftGrip":
                if (leftSelectedTarget != null)
                {
                    leftSelectedTarget.OnGripChanged(value, true);
                }
                break;
                
            case "RightGrip":
                if (rightSelectedTarget != null)
                {
                    rightSelectedTarget.OnGripChanged(value, false);
                }
                break;
        }
    }
    
    private void SelectObject(bool isLeft)
    {
        XRInteractable target = isLeft ? leftHoverTarget : rightHoverTarget;
        Transform interactor = isLeft ? leftInteractor : rightInteractor;
        GameObject selectVisual = isLeft ? leftSelectIndicator : rightSelectIndicator;
        
        if (target != null && target.IsSelectable && interactor != null)
        {
            if (isLeft)
            {
                leftSelectedTarget = target;
            }
            else
            {
                rightSelectedTarget = target;
            }
            
            // 通知开始选中
            target.OnSelectEnter(isLeft, interactor);
            
            // 显示选中指示器
            if (selectVisual != null)
            {
                selectVisual.SetActive(true);
                selectVisual.transform.position = target.transform.position;
                selectVisual.transform.rotation = target.transform.rotation;
                selectVisual.transform.localScale = target.transform.localScale * 1.1f;
            }
        }
    }
    
    private void ReleaseObject(bool isLeft)
    {
        XRInteractable target = isLeft ? leftSelectedTarget : rightSelectedTarget;
        GameObject selectVisual = isLeft ? leftSelectIndicator : rightSelectIndicator;
        
        if (target != null)
        {
            // 通知结束选中
            target.OnSelectExit(isLeft);
            
            // 隐藏选中指示器
            if (selectVisual != null)
            {
                selectVisual.SetActive(false);
            }
            
            // 清空选中目标
            if (isLeft)
            {
                leftSelectedTarget = null;
            }
            else
            {
                rightSelectedTarget = null;
            }
        }
    }
    
    private void UpdateSelectedObjects()
    {
        // 更新左手选中物体
        if (leftSelectedTarget != null && leftInteractor != null && leftSelectedTarget.IsAttachable)
        {
            leftSelectedTarget.OnPositionUpdate(leftInteractor, true);
        }
        
        // 更新右手选中物体
        if (rightSelectedTarget != null && rightInteractor != null && rightSelectedTarget.IsAttachable)
        {
            rightSelectedTarget.OnPositionUpdate(rightInteractor, false);
        }
    }
    
    private void OnDestroy()
    {
        // 取消订阅事件
        if (xrInput != null)
        {
            xrInput.OnButtonStateChanged -= HandleButtonStateChanged;
            xrInput.OnAxisChanged -= HandleAxisChanged;
        }
    }
}

// XRInteractable.cs - 可交互对象基类
public class XRInteractable : MonoBehaviour
{
    [Header("交互属性")]
    [SerializeField] private bool isInteractable = true;
    [SerializeField] private bool isSelectable = true;
    [SerializeField] private bool isAttachable = true;
    [SerializeField] private bool useLocalSpace = false;
    [SerializeField] private Vector3 attachOffset = Vector3.zero;
    [SerializeField] private Vector3 attachRotationOffset = Vector3.zero;
    
    [Header("交互事件")]
    [SerializeField] private UnityEvent onHoverEnterEvent;
    [SerializeField] private UnityEvent onHoverExitEvent;
    [SerializeField] private UnityEvent onSelectEnterEvent;
    [SerializeField] private UnityEvent onSelectExitEvent;
    
    // 物理属性引用
    private Rigidbody objectRigidbody;
    private bool wasKinematic = false;
    
    // 交互状态
    private bool isHovered = false;
    private bool isSelected = false;
    private Transform attachTransform;
    
    // 初始状态备份
    private Vector3 initialPosition;
    private Quaternion initialRotation;
    private Vector3 initialScale;
    
    // 属性访问器
    public bool IsInteractable => isInteractable;
    public bool IsSelectable => isSelectable;
    public bool IsAttachable => isAttachable;
    
    private void Awake()
    {
        objectRigidbody = GetComponent<Rigidbody>();
        
        // 保存初始状态
        initialPosition = transform.position;
        initialRotation = transform.rotation;
        initialScale = transform.localScale;
    }
    
    // 悬停开始
    public virtual void OnHoverEnter(bool isLeft)
    {
        isHovered = true;
        
        // 触发Unity事件
        onHoverEnterEvent?.Invoke();
        
        // 可以在子类中重写以添加自定义行为
    }
    
    // 悬停结束
    public virtual void OnHoverExit(bool isLeft)
    {
        isHovered = false;
        
        // 触发Unity事件
        onHoverExitEvent?.Invoke();
        
        // 可以在子类中重写以添加自定义行为
    }
    
    // 选中开始
    public virtual void OnSelectEnter(bool isLeft, Transform interactor)
    {
        isSelected = true;
        attachTransform = interactor;
        
        // 如果有刚体,改变物理属性
        if (objectRigidbody != null && isAttachable)
        {
            wasKinematic = objectRigidbody.isKinematic;
            objectRigidbody.isKinematic = true;
        }
        
        // 触发Unity事件
        onSelectEnterEvent?.Invoke();
        
        // 可以在子类中重写以添加自定义行为
    }
    
    // 选中结束
    public virtual void OnSelectExit(bool isLeft)
    {
        isSelected = false;
        
        // 如果有刚体,恢复物理属性
        if (objectRigidbody != null && isAttachable)
        {
            objectRigidbody.isKinematic = wasKinematic;
            
            // 可以添加一些释放速度
            if (!wasKinematic)
            {
                // 获取控制器速度进行投掷
                // 此处需要补充具体实现
            }
        }
        
        // 触发Unity事件
        onSelectExitEvent?.Invoke();
        
        // 可以在子类中重写以添加自定义行为
    }
    
    // 位置更新
    public virtual void OnPositionUpdate(Transform interactor, bool isLeft)
    {
        if (!isSelected || !isAttachable || interactor == null) return;
        
        if (useLocalSpace)
        {
            // 使用局部坐标
            transform.position = interactor.TransformPoint(attachOffset);
            transform.rotation = interactor.rotation * Quaternion.Euler(attachRotationOffset);
        }
        else
        {
            // 使用世界坐标
            transform.position = interactor.position + interactor.TransformDirection(attachOffset);
            transform.rotation = interactor.rotation * Quaternion.Euler(attachRotationOffset);
        }
    }
    
    // 扳机输入
    public virtual void OnTriggerChanged(float value, bool isLeft)
    {
        // 默认实现为空,子类可以重写
    }
    
    // 握把输入
    public virtual void OnGripChanged(float value, bool isLeft)
    {
        // 默认实现为空,子类可以重写
    }
    
    // 辅助按钮操作
    public virtual void OnSecondaryAction(bool isLeft)
    {
        // 默认实现为空,子类可以重写
    }
    
    // 重置物体
    public virtual void ResetToInitialState()
    {
        transform.position = initialPosition;
        transform.rotation = initialRotation;
        transform.localScale = initialScale;
        
        // 重置物理状态
        if (objectRigidbody != null)
        {
            objectRigidbody.velocity = Vector3.zero;
            objectRigidbody.angularVelocity = Vector3.zero;
        }
    }
    
    // 设置交互状态
    public void SetInteractable(bool interactable)
    {
        isInteractable = interactable;
    }
    
    // 设置选中状态
    public void SetSelectable(bool selectable)
    {
        isSelectable = selectable;
    }
    
    // 设置附加状态
    public void SetAttachable(bool attachable)
    {
        isAttachable = attachable;
    }
}

六、多平台渲染与优化策略

跨平台渲染管理器

// XRRenderingManager.cs - 跨平台渲染管理器
using UnityEngine;
using UnityEngine.Rendering;

[DefaultExecutionOrder(-100)] // 确保在其他脚本前执行
public class XRRenderingManager : MonoBehaviour
{
    [Header("渲染设置")]
    [SerializeField] private bool autoDetectPlatform = true;
    [SerializeField] private XRPlatformType targetPlatform;
    
    [Header("Quest设置")]
    [SerializeField] private bool enableFFR = true;
    [Range(1, 4)]
    [SerializeField] private int questQualityLevel = 3;
    [Range(0.7f, 1.4f)]
    [SerializeField] private float questResolutionScale = 1.0f;
    
    [Header("Pico设置")]
    [SerializeField] private bool enableFoveatedRendering = true;
    [Range(1, 4)]
    [SerializeField] private int picoQualityLevel = 3;
    [Range(0.7f, 1.4f)]
    [SerializeField] private float picoResolutionScale = 1.0f;
    
    [Header("HoloLens设置")]
    [SerializeField] private bool enableHolographicReprojection = true;
    [Range(1, 3)]
    [SerializeField] private int hololensQualityLevel = 2;
    [Range(0.7f, 1.4f)]
    [SerializeField] private float hololensResolutionScale = 1.0f;
    
    [Header("动态优化")]
    [SerializeField] private bool enableDynamicResolution = true;
    [SerializeField] private bool enableAdaptiveQuality = true;
    [Range(0.5f, 1.0f)]
    [SerializeField] private float minResolutionScale = 0.7f;
    [Range(60, 90)]
    [SerializeField] private float targetFrameRate = 72f;
    
    // 性能监控
    private float[] frameTimes = new float[60];
    private int frameIndex = 0;
    private float lastResolutionAdjustTime = 0f;
    private float resolutionAdjustInterval = 2.0f;
    
    // 当前状态
    private float currentResolutionScale;
    private int currentQualityLevel;
    
    private void Awake()
    {
        if (autoDetectPlatform && XRPlatformManager.Instance != null)
        {
            targetPlatform = XRPlatformManager.Instance.CurrentPlatform;
        }
        
        ApplyRenderingSettings();
    }
    
    private void ApplyRenderingSettings()
    {
        Debug.Log($"应用XR渲染设置,平台: {targetPlatform}");
        
        switch (targetPlatform)
        {
            case XRPlatformType.Quest:
                ApplyQuestSettings();
                break;
                
            case XRPlatformType.Pico:
                ApplyPicoSettings();
                break;
                
            case XRPlatformType.HoloLens:
                ApplyHololensSettings();
                break;
                
            default:
                ApplyDefaultSettings();
                break;
        }
        
        // 设置应用目标帧率
        Application.targetFrameRate = (int)targetFrameRate;
    }
    
    private void ApplyQuestSettings()
    {
        #if QUEST_SDK && !UNITY_EDITOR
        try
        {
            Debug.Log("应用Quest渲染设置");
            
            // 设置固定注视点渲染
            if (enableFFR)
            {
                OVRManager.fixedFoveatedRenderingLevel = OVRManager.FixedFoveatedRenderingLevel.High;
                OVRManager.useDynamicFixedFoveatedRendering = true;
            }
            else
            {
                OVRManager.fixedFoveatedRenderingLevel = OVRManager.FixedFoveatedRenderingLevel.Off;
            }
            
            // 设置渲染分辨率
            OVRManager.eyeTextureResolutionScale = questResolutionScale;
            currentResolutionScale = questResolutionScale;
            
            // 设置性能等级
            int cpuLevel = Mathf.Clamp(questQualityLevel - 1, 0, 3);
            int gpuLevel = Mathf.Clamp(questQualityLevel - 1, 0, 3);
            
            OVRManager.cpuLevel = cpuLevel;
            OVRManager.gpuLevel = gpuLevel;
            currentQualityLevel = questQualityLevel;
        }
        catch (System.Exception e)
        {
            Debug.LogError($"设置Quest渲染参数时出错: {e.Message}");
        }
        #endif
    }
    
    private void ApplyPicoSettings()
    {
        #if PICO_SDK && !UNITY_EDITOR
        try
        {
            Debug.Log("应用Pico渲染设置");
            
            // 设置注视点渲染
            if (enableFoveatedRendering)
            {
                Unity.XR.PXR.PXR_Manager.Instance.foveationLevel = Unity.XR.PXR.FoveationLevel.High;
            }
            else
            {
                Unity.XR.PXR.PXR_Manager.Instance.foveationLevel = Unity.XR.PXR.FoveationLevel.None;
            }
            
            // 设置渲染分辨率
            Unity.XR.PXR.PXR_Manager.Instance.eyeTexureResolutionScale = picoResolutionScale;
            currentResolutionScale = picoResolutionScale;
            
            // 设置质量等级
            QualitySettings.SetQualityLevel(Mathf.Clamp(picoQualityLevel - 1, 0, QualitySettings.names.Length - 1));
            currentQualityLevel = picoQualityLevel;
        }
        catch (System.Exception e)
        {
            Debug.LogError($"设置Pico渲染参数时出错: {e.Message}");
        }
        #endif
    }
    
    private void ApplyHololensSettings()
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        try
        {
            Debug.Log("应用HoloLens渲染设置");
            
            // 设置全息重投影
            if (enableHolographicReprojection)
            {
                UnityEngine.XR.WSA.HolographicSettings.EnableReprojection(
                    UnityEngine.XR.WSA.HolographicSettings.ReprojectionMode.PositionAndOrientation);
            }
            else
            {
                UnityEngine.XR.WSA.HolographicSettings.EnableReprojection(
                    UnityEngine.XR.WSA.HolographicSettings.ReprojectionMode.Disabled);
            }
            
            // 设置渲染分辨率
            UnityEngine.XR.XRSettings.eyeTextureResolutionScale = hololensResolutionScale;
            currentResolutionScale = hololensResolutionScale;
            
            // 设置质量等级
            QualitySettings.SetQualityLevel(Mathf.Clamp(hololensQualityLevel - 1, 0, QualitySettings.names.Length - 1));
            currentQualityLevel = hololensQualityLevel;
        }
        catch (System.Exception e)
        {
            Debug.LogError($"设


            Debug.LogError($"设置HoloLens渲染参数时出错: {e.Message}");
        }
        #endif
    }
    
    private void ApplyDefaultSettings()
    {
        Debug.Log("应用通用XR渲染设置");
        
        // 设置渲染分辨率
        UnityEngine.XR.XRSettings.eyeTextureResolutionScale = 1.0f;
        currentResolutionScale = 1.0f;
        
        // 设置适当的质量等级
        int qualityLevel = Mathf.Clamp(2, 0, QualitySettings.names.Length - 1); // 中等质量
        QualitySettings.SetQualityLevel(qualityLevel);
        currentQualityLevel = qualityLevel + 1;
    }
    
    private void Update()
    {
        if (enableDynamicResolution || enableAdaptiveQuality)
        {
            MonitorPerformance();
        }
    }
    
    private void MonitorPerformance()
    {
        // 记录帧时间
        frameTimes[frameIndex] = Time.deltaTime;
        frameIndex = (frameIndex + 1) % frameTimes.Length;
        
        // 每隔一段时间调整分辨率
        if (Time.time - lastResolutionAdjustTime > resolutionAdjustInterval)
        {
            lastResolutionAdjustTime = Time.time;
            
            // 计算平均帧时间
            float avgFrameTime = 0;
            for (int i = 0; i < frameTimes.Length; i++)
            {
                avgFrameTime += frameTimes[i];
            }
            avgFrameTime /= frameTimes.Length;
            
            // 计算当前帧率
            float currentFPS = 1.0f / avgFrameTime;
            
            // 调整渲染设置
            if (enableDynamicResolution)
            {
                AdjustResolution(currentFPS);
            }
            
            if (enableAdaptiveQuality && currentFPS < targetFrameRate * 0.8f)
            {
                AdjustQualityLevel(currentFPS);
            }
        }
    }
    
    private void AdjustResolution(float currentFPS)
    {
        // 帧率低于目标的85%时降低分辨率
        if (currentFPS < targetFrameRate * 0.85f && currentResolutionScale > minResolutionScale)
        {
            float newScale = Mathf.Max(currentResolutionScale - 0.05f, minResolutionScale);
            SetResolutionScale(newScale);
            Debug.Log($"降低渲染分辨率至 {newScale:F2} (当前帧率: {currentFPS:F1})");
        }
        // 帧率高于目标的95%时尝试提高分辨率
        else if (currentFPS > targetFrameRate * 0.95f && currentResolutionScale < 1.2f)
        {
            // 更缓慢地增加分辨率
            float newScale = Mathf.Min(currentResolutionScale + 0.02f, 1.2f);
            SetResolutionScale(newScale);
            Debug.Log($"提高渲染分辨率至 {newScale:F2} (当前帧率: {currentFPS:F1})");
        }
    }
    
    private void AdjustQualityLevel(float currentFPS)
    {
        // 帧率低于目标的70%时降低质量等级
        if (currentFPS < targetFrameRate * 0.7f && currentQualityLevel > 1)
        {
            int newLevel = currentQualityLevel - 1;
            SetQualityLevel(newLevel);
            Debug.Log($"降低质量等级至 {newLevel} (当前帧率: {currentFPS:F1})");
        }
    }
    
    private void SetResolutionScale(float scale)
    {
        currentResolutionScale = scale;
        
        switch (targetPlatform)
        {
            case XRPlatformType.Quest:
                #if QUEST_SDK && !UNITY_EDITOR
                OVRManager.eyeTextureResolutionScale = scale;
                #endif
                break;
                
            case XRPlatformType.Pico:
                #if PICO_SDK && !UNITY_EDITOR
                Unity.XR.PXR.PXR_Manager.Instance.eyeTexureResolutionScale = scale;
                #endif
                break;
                
            case XRPlatformType.HoloLens:
                #if HOLOLENS_SDK && !UNITY_EDITOR
                UnityEngine.XR.XRSettings.eyeTextureResolutionScale = scale;
                #endif
                break;
                
            default:
                UnityEngine.XR.XRSettings.eyeTextureResolutionScale = scale;
                break;
        }
    }
    
    private void SetQualityLevel(int level)
    {
        currentQualityLevel = level;
        
        switch (targetPlatform)
        {
            case XRPlatformType.Quest:
                #if QUEST_SDK && !UNITY_EDITOR
                int cpuLevel = Mathf.Clamp(level - 1, 0, 3);
                int gpuLevel = Mathf.Clamp(level - 1, 0, 3);
                
                OVRManager.cpuLevel = cpuLevel;
                OVRManager.gpuLevel = gpuLevel;
                #endif
                break;
                
            default:
                // 适当调整Unity质量等级
                QualitySettings.SetQualityLevel(Mathf.Clamp(level - 1, 0, QualitySettings.names.Length - 1));
                break;
        }
        
        // 调用自定义质量调整
        OnQualityLevelChanged(level);
    }
    
    // 可以在子类中重写以添加额外的质量调整
    protected virtual void OnQualityLevelChanged(int level)
    {
        // 常见的质量参数调整
        switch (level)
        {
            case 1: // 低质量
                RenderSettings.shadowDistance = 5f;
                QualitySettings.shadowResolution = ShadowResolution.Low;
                QualitySettings.shadowCascades = 1;
                QualitySettings.antiAliasing = 0;
                QualitySettings.realtimeReflectionProbes = false;
                break;
                
            case 2: // 中等质量
                RenderSettings.shadowDistance = 20f;
                QualitySettings.shadowResolution = ShadowResolution.Medium;
                QualitySettings.shadowCascades = 2;
                QualitySettings.antiAliasing = 2;
                QualitySettings.realtimeReflectionProbes = false;
                break;
                
            case 3: // 高质量
                RenderSettings.shadowDistance = 40f;
                QualitySettings.shadowResolution = ShadowResolution.High;
                QualitySettings.shadowCascades = 2;
                QualitySettings.antiAliasing = 4;
                QualitySettings.realtimeReflectionProbes = true;
                break;
                
            case 4: // 超高质量
                RenderSettings.shadowDistance = 60f;
                QualitySettings.shadowResolution = ShadowResolution.VeryHigh;
                QualitySettings.shadowCascades = 4;
                QualitySettings.antiAliasing = 8;
                QualitySettings.realtimeReflectionProbes = true;
                break;
        }
    }
    
    // 为其他脚本提供公共方法
    public void BoostPerformanceForScenario()
    {
        // 临时提高性能的方法,例如在复杂场景转换时调用
        float previousScale = currentResolutionScale;
        SetResolutionScale(minResolutionScale);
        
        // 在短时间后恢复
        StartCoroutine(RestoreResolutionAfterDelay(previousScale, 2.0f));
    }
    
    private System.Collections.IEnumerator RestoreResolutionAfterDelay(float targetScale, float delay)
    {
        yield return new WaitForSeconds(delay);
        SetResolutionScale(targetScale);
    }
}

高效渲染批次合并与优化工具

// XRBatchingOptimizer.cs - XR批处理优化器
using UnityEngine;
using System.Collections.Generic;

#if UNITY_EDITOR
using UnityEditor;
#endif

public class XRBatchingOptimizer : MonoBehaviour
{
    [Header("批处理设置")]
    [SerializeField] private bool enableStaticBatching = true;
    [SerializeField] private bool enableDynamicBatching = true;
    [SerializeField] private bool enableGPUInstancing = true;
    
    [Header("优化设置")]
    [SerializeField] private bool combineStaticMeshes = true;
    [SerializeField] private bool generateLightmapUVs = true;
    [SerializeField] private bool organizeObjectsByMaterial = true;
    
    [Header("运行时监控")]
    [SerializeField] private bool monitorBatchCount = true;
    [SerializeField] private bool logWarnings = true;
    [SerializeField] private int warningBatchThreshold = 100;
    
    // 批次统计
    private int drawCallCount = 0;
    private int batchCount = 0;
    private int savedBatches = 0;
    
    // 缓存用于查找相同材质的对象
    private Dictionary<Material, List<Renderer>> materialGroups = new Dictionary<Material, List<Renderer>>();
    
    private void Awake()
    {
        // 设置批处理参数
        if (Application.isPlaying)
        {
            ApplyBatchingSettings();
            
            if (combineStaticMeshes)
            {
                CombineStaticMeshesAtRuntime();
            }
        }
    }
    
    private void ApplyBatchingSettings()
    {
        // 尽管这些是项目设置,但在运行时仍可以通过反射修改某些参数
        // 这部分代码实际上不会修改GraphicsSettings中的值,只是展示可能的方法
        Debug.Log($"应用XR批处理设置 - 静态批处理: {enableStaticBatching}, 动态批处理: {enableDynamicBatching}, GPU实例化: {enableGPUInstancing}");
        
        // 集中启用GPU实例化
        if (enableGPUInstancing)
        {
            EnableGPUInstancingOnMaterials();
        }
    }
    
    private void EnableGPUInstancingOnMaterials()
    {
        Renderer[] renderers = FindObjectsOfType<Renderer>();
        int enabledCount = 0;
        
        foreach (Renderer renderer in renderers)
        {
            // 只处理非粒子系统渲染器
            if (!(renderer is ParticleSystemRenderer))
            {
                foreach (Material material in renderer.sharedMaterials)
                {
                    if (material != null && !material.enableInstancing && material.shader.supportsInstancing)
                    {
                        material.enableInstancing = true;
                        enabledCount++;
                    }
                }
            }
        }
        
        Debug.Log($"为 {enabledCount} 个材质启用了GPU实例化");
    }
    
    private void Start()
    {
        if (monitorBatchCount)
        {
            // 注册到相机预渲染事件以收集批处理统计信息
            Camera.onPreRender += OnCameraPreRender;
            InvokeRepeating("LogBatchStatistics", 5f, 10f);
        }
        
        if (organizeObjectsByMaterial)
        {
            OrganizeObjectsByMaterial();
        }
    }
    
    private void OnCameraPreRender(Camera cam)
    {
        // 只监视主摄像机
        if (cam.CompareTag("MainCamera"))
        {
            drawCallCount = UnityStats.drawCalls;
            batchCount = UnityStats.batches;
            savedBatches = UnityStats.renderInstancedBatches;
            
            if (logWarnings && batchCount > warningBatchThreshold)
            {
                Debug.LogWarning($"批次数量 ({batchCount}) 超过警告阈值 ({warningBatchThreshold})!");
            }
        }
    }
    
    private void LogBatchStatistics()
    {
        Debug.Log($"渲染统计 - 绘制调用: {drawCallCount}, 批次: {batchCount}, 实例化批次: {savedBatches}");
    }
    
    private void CombineStaticMeshesAtRuntime()
    {
        // 在运行时合并静态网格以减少绘制调用
        // 注意:这可能会增加内存使用量和加载时间
        
        // 按材质分组
        Dictionary<Material, List<MeshFilter>> meshGroups = new Dictionary<Material, List<MeshFilter>>();
        MeshFilter[] meshFilters = FindObjectsOfType<MeshFilter>();
        
        foreach (MeshFilter meshFilter in meshFilters)
        {
            // 检查是否是静态对象且有对应的渲染器
            if (meshFilter.gameObject.isStatic && meshFilter.sharedMesh != null)
            {
                MeshRenderer meshRenderer = meshFilter.GetComponent<MeshRenderer>();
                if (meshRenderer != null && meshRenderer.enabled && meshRenderer.sharedMaterial != null)
                {
                    Material material = meshRenderer.sharedMaterial;
                    
                    if (!meshGroups.ContainsKey(material))
                    {
                        meshGroups[material] = new List<MeshFilter>();
                    }
                    
                    meshGroups[material].Add(meshFilter);
                }
            }
        }
        
        // 为每个材质组合并网格
        int combinedGroupCount = 0;
        foreach (var group in meshGroups)
        {
            if (group.Value.Count > 1) // 只有当有多个网格时才需要合并
            {
                CombineMeshGroup(group.Key, group.Value);
                combinedGroupCount++;
            }
        }
        
        Debug.Log($"运行时合并了 {combinedGroupCount} 组网格");
    }
    
    private void CombineMeshGroup(Material material, List<MeshFilter> meshFilters)
    {
        // 每组最多合并1023个网格(Unity的限制)
        const int MAX_MESHES_PER_GROUP = 1023;
        
        for (int groupIndex = 0; groupIndex < meshFilters.Count; groupIndex += MAX_MESHES_PER_GROUP)
        {
            int meshesInThisGroup = Mathf.Min(MAX_MESHES_PER_GROUP, meshFilters.Count - groupIndex);
            
            // 准备组合数据
            CombineInstance[] combineInstances = new CombineInstance[meshesInThisGroup];
            
            for (int i = 0; i < meshesInThisGroup; i++)
            {
                MeshFilter meshFilter = meshFilters[groupIndex + i];
                
                combineInstances[i] = new CombineInstance();
                combineInstances[i].mesh = meshFilter.sharedMesh;
                combineInstances[i].transform = meshFilter.transform.localToWorldMatrix;
            }
            
            // 创建新的组合网格
            GameObject combinedObject = new GameObject($"Combined_Mesh_{material.name}_{groupIndex/MAX_MESHES_PER_GROUP}");
            combinedObject.transform.SetParent(transform);
            combinedObject.transform.localPosition = Vector3.zero;
            combinedObject.transform.localRotation = Quaternion.identity;
            combinedObject.transform.localScale = Vector3.one;
            
            MeshFilter newMeshFilter = combinedObject.AddComponent<MeshFilter>();
            MeshRenderer newRenderer = combinedObject.AddComponent<MeshRenderer>();
            
            // 创建组合网格
            Mesh combinedMesh = new Mesh();
            combinedMesh.CombineMeshes(combineInstances, true);
            newMeshFilter.sharedMesh = combinedMesh;
            newRenderer.sharedMaterial = material;
            
            // 禁用或销毁原始对象
            for (int i = 0; i < meshesInThisGroup; i++)
            {
                meshFilters[groupIndex + i].gameObject.SetActive(false);
            }
        }
    }
    
    private void OrganizeObjectsByMaterial()
    {
        Renderer[] renderers = FindObjectsOfType<Renderer>();
        materialGroups.Clear();
        
        // 分组收集共享相同材质的渲染器
        foreach (Renderer renderer in renderers)
        {
            if (renderer.sharedMaterial != null)
            {
                Material material = renderer.sharedMaterial;
                
                if (!materialGroups.ContainsKey(material))
                {
                    materialGroups[material] = new List<Renderer>();
                }
                
                materialGroups[material].Add(renderer);
            }
        }
        
        // 输出分析结果
        foreach (var group in materialGroups)
        {
            if (group.Value.Count > 10)
            {
                Debug.Log($"发现大量共享材质: {group.Key.name} - 使用对象数: {group.Value.Count}");
            }
        }
        
        // 寻找材质不必要的重复使用
        FindDuplicateMaterialUsage();
    }
    
    private void FindDuplicateMaterialUsage()
    {
        Dictionary<string, List<Material>> materialsByName = new Dictionary<string, List<Material>>();
        
        // 按名称分组材质
        foreach (var material in materialGroups.Keys)
        {
            string name = material.name;
            
            // 删除材质名称中的实例标记
            name = name.Replace("(Instance)", "").Trim();
            
            if (!materialsByName.ContainsKey(name))
            {
                materialsByName[name] = new List<Material>();
            }
            
            materialsByName[name].Add(material);
        }
        
        // 查找同名但不同实例的材质
        foreach (var nameGroup in materialsByName)
        {
            if (nameGroup.Value.Count > 1)
            {
                Debug.LogWarning($"发现可能的重复材质: {nameGroup.Key} - {nameGroup.Value.Count}个不同实例");
                
                // 输出使用这些材质的对象
                foreach (Material material in nameGroup.Value)
                {
                    int objectCount = materialGroups[material].Count;
                    Debug.Log($"  - 材质实例 {material.GetInstanceID()} 被 {objectCount} 个对象使用");
                }
            }
        }
    }
    
    private void OnDestroy()
    {
        // 取消注册事件
        if (monitorBatchCount)
        {
            Camera.onPreRender -= OnCameraPreRender;
            CancelInvoke("LogBatchStatistics");
        }
    }
    
    #if UNITY_EDITOR
    // 编辑器工具方法
    [MenuItem("XR工具/优化/自动设置静态批处理标记")]
    private static void SetStaticBatchingFlags()
    {
        Renderer[] renderers = FindObjectsOfType<Renderer>();
        int count = 0;
        
        foreach (Renderer renderer in renderers)
        {
            // 如果对象有MeshFilter且没有移动脚本
            MeshFilter meshFilter = renderer.GetComponent<MeshFilter>();
            Rigidbody rb = renderer.GetComponent<Rigidbody>();
            
            if (meshFilter != null && rb == null && 
                !renderer.GetComponent<Animation>() && 
                !renderer.GetComponent<MonoBehaviour>())
            {
                // 设置为静态批处理
                StaticEditorFlags flags = GameObjectUtility.GetStaticEditorFlags(renderer.gameObject);
                flags |= StaticEditorFlags.BatchingStatic;
                GameObjectUtility.SetStaticEditorFlags(renderer.gameObject, flags);
                count++;
            }
        }
        
        Debug.Log($"自动为 {count} 个对象设置了静态批处理标记");
    }
    
    [MenuItem("XR工具/优化/合并相同材质")]
    private static void MergeSameMaterial()
    {
        // 查找所有使用相同名称材质的渲染器
        Dictionary<string, List<Renderer>> renderersByMaterialName = new Dictionary<string, List<Renderer>>();
        Dictionary<string, Material> firstMaterialByName = new Dictionary<string, Material>();
        Renderer[] renderers = FindObjectsOfType<Renderer>();
        
        foreach (Renderer renderer in renderers)
        {
            foreach (Material material in renderer.sharedMaterials)
            {
                if (material != null)
                {
                    string name = material.name.Replace("(Instance)", "").Trim();
                    
                    if (!renderersByMaterialName.ContainsKey(name))
                    {
                        renderersByMaterialName[name] = new List<Renderer>();
                        firstMaterialByName[name] = material;
                    }
                    
                    renderersByMaterialName[name].Add(renderer);
                }
            }
        }
        
        // 统一每个组的材质
        int replacedCount = 0;
        foreach (var group in renderersByMaterialName)
        {
            if (group.Value.Count > 1)
            {
                Material firstMaterial = firstMaterialByName[group.Key];
                
                foreach (Renderer renderer in group.Value)
                {
                    Material[] materials = renderer.sharedMaterials;
                    bool materialReplaced = false;
                    
                    for (int i = 0; i < materials.Length; i++)
                    {
                        if (materials[i] != null)
                        {
                            string name = materials[i].name.Replace("(Instance)", "").Trim();
                            
                            if (name == group.Key && materials[i] != firstMaterial)
                            {
                                materials[i] = firstMaterial;
                                materialReplaced = true;
                                replacedCount++;
                            }
                        }
                    }
                    
                    if (materialReplaced)
                    {
                        renderer.sharedMaterials = materials;
                    }
                }
            }
        }
        
        Debug.Log($"合并了 {replacedCount} 个材质引用");
    }
    
    [MenuItem("XR工具/优化/启用所有材质GPU实例化")]
    private static void EnableAllMaterialsGPUInstancing()
    {
        Material[] materials = Resources.FindObjectsOfTypeAll<Material>();
        int count = 0;
        
        foreach (Material material in materials)
        {
            if (!material.enableInstancing && material.shader.supportsInstancing)
            {
                material.enableInstancing = true;
                count++;
            }
        }
        
        Debug.Log($"为 {count} 个材质启用了GPU实例化");
    }
    #endif
}

多平台共享着色器模板

// XRPlatformShader.shader - 多平台优化着色器
Shader "XR/PlatformOptimizedShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        [Toggle(ENABLE_NORMAL_MAP)] _EnableNormalMap ("Enable Normal Map", Float) = 0
        [NoScaleOffset] _BumpMap ("Normal Map", 2D) = "bump" {}
        [Toggle(ENABLE_EMISSION)] _EnableEmission ("Enable Emission", Float) = 0
        [HDR] _EmissionColor ("Emission Color", Color) = (0,0,0,1)
        [NoScaleOffset] _EmissionMap ("Emission Map", 2D) = "white" {}
    }
    
    // 多个变体以支持不同平台和优化级别
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 300
        
        // ======== 高品质渲染路径(Quest 2/Pico 4高级) ========
        CGPROGRAM
        #pragma target 3.5
        #pragma surface surf Standard fullforwardshadows
        #pragma shader_feature_local ENABLE_NORMAL_MAP
        #pragma shader_feature_local ENABLE_EMISSION
        #pragma multi_compile_instancing
        
        sampler2D _MainTex;
        sampler2D _BumpMap;
        sampler2D _EmissionMap;
        
        struct Input
        {
            float2 uv_MainTex;
            UNITY_VERTEX_INPUT_INSTANCE_ID
        };
        
        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        half4 _EmissionColor;
        
        UNITY_INSTANCING_BUFFER_START(Props)
            // 如果需要实例化的属性,放在这里
        UNITY_INSTANCING_BUFFER_END(Props)
        
        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            UNITY_SETUP_INSTANCE_ID(IN);
            
            // 基础材质属性
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
            
            // 法线贴图
            #ifdef ENABLE_NORMAL_MAP
                o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
            #endif
            
            // 自发光
            #ifdef ENABLE_EMISSION
                o.Emission = tex2D(_EmissionMap, IN.uv_MainTex).rgb * _EmissionColor.rgb;
            #endif
        }
        ENDCG
    }
    
    // 中级品质变体
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 200
        
        CGPROGRAM
        #pragma target 3.0
        #pragma surface surf StandardSpecular noshadow
        #pragma shader_feature_local ENABLE_EMISSION
        #pragma multi_compile_instancing
        
        sampler2D _MainTex;
        sampler2D _EmissionMap;
        
        struct Input
        {
            float2 uv_MainTex;
            UNITY_VERTEX_INPUT_INSTANCE_ID
        };
        
        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        half4 _EmissionColor;
        
        void surf (Input IN, inout SurfaceOutputStandardSpecular o)
        {
            UNITY_SETUP_INSTANCE_ID(IN);
            
            // 简化的材质属性
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Smoothness = _Glossiness;
            o.Specular = _Metallic * 0.5; // 简化金属度计算
            o.Alpha = c.a;
            
            // 自发光
            #ifdef ENABLE_EMISSION
                o.Emission = tex2D(_EmissionMap, IN.uv_MainTex).rgb * _EmissionColor.rgb;
            #endif
        }
        ENDCG
    }
    
    // 低级品质变体
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 100
        
        CGPROGRAM
        #pragma target 2.5
        #pragma surface surf Lambert noambient noshadow
        #pragma multi_compile_instancing
        
        sampler2D _MainTex;
        
        struct Input
        {
            float2 uv_MainTex;
            UNITY_VERTEX_INPUT_INSTANCE_ID
        };
        
        fixed4 _Color;
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            UNITY_SETUP_INSTANCE_ID(IN);
            
            // 极简材质属性
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    
    Fallback "Diffuse"
    CustomEditor "XRShaderGUI"
}

自定义XR着色器GUI编辑器

#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;

// XRShaderGUI.cs - 自定义XR着色器GUI
public class XRShaderGUI : ShaderGUI
{
    // 用于检测平台的属性
    private MaterialProperty emissionEnabledProp = null;
    private MaterialProperty normalMapEnabledProp = null;
    private MaterialProperty mainTexProp = null;
    private MaterialProperty colorProp = null;
    private MaterialProperty glossinessProp = null;
    private MaterialProperty metallicProp = null;
    private MaterialProperty bumpMapProp = null;
    private MaterialProperty emissionColorProp = null;
    private MaterialProperty emissionMapProp = null;
    
    // UI状态
    private bool showAdvancedOptions = false;
    private bool showPlatformSettings = false;
    private bool showPerformanceInfo = false;
    
    // 当前平台推荐设置
    private XRPlatformType currentPlatform = XRPlatformType.Quest;
    
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        // 获取目标材质
        Material material = materialEditor.target as Material;
        
        // 获取属性
        FindProperties(properties);
        
        // 绘制标题
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("XR平台优化着色器", EditorStyles.boldLabel);
        DrawDivider();
        
        // 绘制平台优化区域
        DrawPlatformSettingsSection(material);
        
        // 绘制基础属性
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("基础属性", EditorStyles.boldLabel);
        materialEditor.TexturePropertySingleLine(new GUIContent("主纹理"), mainTexProp, colorProp);
        materialEditor.TextureScaleOffsetProperty(mainTexProp);
        
        // PBR属性
        materialEditor.RangeProperty(glossinessProp, "平滑度");
        materialEditor.RangeProperty(metallicProp, "金属度");
        
        // 法线贴图
        bool useNormalMap = normalMapEnabledProp.floatValue > 0.5f;
        useNormalMap = EditorGUILayout.Toggle(new GUIContent("使用法线贴图"), useNormalMap);
        normalMapEnabledProp.floatValue = useNormalMap ? 1.0f : 0.0f;
        
        if (useNormalMap)
        {
            EditorGUI.indentLevel++;
            materialEditor.TexturePropertySingleLine(new GUIContent("法线贴图"), bumpMapProp);
            EditorGUI.indentLevel--;
        }
        
        // 自发光
        bool useEmission = emissionEnabledProp.floatValue > 0.5f;
        useEmission = EditorGUILayout.Toggle(new GUIContent("使用自发光"), useEmission);
        emissionEnabledProp.floatValue = useEmission ? 1.0f : 0.0f;
        
        if (useEmission)
        {
            EditorGUI.indentLevel++;
            materialEditor.TexturePropertyWithHDRColor(
                new GUIContent("自发光贴图"), emissionMapProp, emissionColorProp, false);
            EditorGUI.indentLevel--;
        }
        
        // 高级属性
        EditorGUILayout.Space();
        showAdvancedOptions = EditorGUILayout.Foldout(showAdvancedOptions, "高级设置", true);
        if (showAdvancedOptions)
        {
            EditorGUI.indentLevel++;
            
            // 绘制高级材质设置
            DrawAdvancedSettings(materialEditor, material);
            
            EditorGUI.indentLevel--;
        }
        
        // 性能信息
        DrawPerformanceInfo(material);
        
        // 应用更改
        materialEditor.RenderQueueField();
        materialEditor.EnableInstancingField();
        materialEditor.DoubleSidedGIField();
    }
    
    private void FindProperties(MaterialProperty[] properties)
    {
        mainTexProp = FindProperty("_MainTex", properties);
        colorProp = FindProperty("_Color", properties);
        glossinessProp = FindProperty("_Glossiness", properties);
        metallicProp = FindProperty("_Metallic", properties);
        normalMapEnabledProp = FindProperty("_EnableNormalMap", properties);
        bumpMapProp = FindProperty("_BumpMap", properties);
        emissionEnabledProp = FindProperty("_EnableEmission", properties);
        emissionColorProp = FindProperty("_EmissionColor", properties);
        emissionMapProp = FindProperty("_EmissionMap", properties);
    }
    
    private void DrawDivider()
    {
        EditorGUILayout.Space();
        Rect rect = EditorGUILayout.GetControlRect(false, 1);
        rect.height = 1;
        EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f));
        EditorGUILayout.Space();
    }
    
    private void DrawPlatformSettingsSection(Material material)
    {
        showPlatformSettings = EditorGUILayout.Foldout(showPlatformSettings, "平台优化设置", true);
        if (showPlatformSettings)
        {
            EditorGUI.indentLevel++;
            
            // 平台选择
            currentPlatform = (XRPlatformType)EditorGUILayout.EnumPopup("目标XR平台", currentPlatform);
            
            // 应用推荐设置按钮
            EditorGUILayout.Space();
            if (GUILayout.Button("应用平台推荐设置"))
            {
                ApplyPlatformRecommendedSettings(material);
            }
            
            EditorGUI.indentLevel--;
        }
    }
    
    private void ApplyPlatformRecommendedSettings(Material material)
    {
        // 根据不同平台应用优化设置
        switch (currentPlatform)
        {
            case XRPlatformType.Quest:
                // Quest优化设置
                material.SetFloat("_EnableNormalMap", IsHighEndQuest() ? 1.0f : 0.0f);
                material.SetFloat("_EnableEmission", IsHighEndQuest() ? 1.0f : 0.0f);
                material.SetFloat("_Glossiness", IsHighEndQuest() ? 0.5f : 0.3f);
                
                // 选择适当的LOD级别子着色器
                material.shader.maximumLOD = IsHighEndQuest() ? 300 : 200;
                
                EditorUtility.DisplayDialog("Quest优化设置", 
                    "已应用Quest推荐设置。" + (IsHighEndQuest() ? 
                    "已检测为Quest 2/Pro,启用了完整特性。" : 
                    "已应用性能优化设置。"), "确定");
                break;
                
            case XRPlatformType.Pico:
                // Pico优化设置
                material.SetFloat("_EnableNormalMap", 1.0f);
                material.SetFloat("_EnableEmission", 1.0f);
                material.SetFloat("_Glossiness", 0.5f);
                
                // 选择适当的LOD级别子着色器
                material.shader.maximumLOD = 300;
                
                EditorUtility.DisplayDialog("Pico优化设置", 
                    "已应用Pico推荐设置。已启用法线贴图和自发光功能。", "确定");
                break;
                
            case XRPlatformType.HoloLens:
                // HoloLens优化设置
                material.SetFloat("_EnableNormalMap", 0.0f);
                material.SetFloat("_EnableEmission", 1.0f);
                material.SetFloat("_Glossiness", 0.3f);
                
                // 选择适当的LOD级别子着色器
                material.shader.maximumLOD = 200;
                
                EditorUtility.DisplayDialog("HoloLens优化设置", 
                    "已应用HoloLens推荐设置。禁用法线贴图以提高性能,保留自发光以增强AR可见性。", "确定");
                break;
        }
        
        // 确保GPU实例化已启用
        material.enableInstancing = true;
    }
    
    private bool IsHighEndQuest()
    {
        // 在实际产品中,这可以通过查询编辑器设置或项目配置来确定
        // 这里简单返回true,假设是Quest 2或更高版本
        return true;
    }
    
    private void DrawAdvancedSettings(MaterialEditor materialEditor, Material material)
    {
        // 渲染模式
        EditorGUILayout.LabelField("渲染模式", EditorStyles.boldLabel);
        
        // 创建一个选项列表
        string[] blendModes = { "不透明", "透明", "镂空", "添加" };
        int currentBlendMode = material.GetInt("_Mode");
        currentBlendMode = EditorGUILayout.Popup("混合模式", currentBlendMode, blendModes);
        
        // 如果模式发生变化,更新材质
        if (material.GetInt("_Mode") != currentBlendMode)
        {
            material.SetInt("_Mode", currentBlendMode);
            UpdateBlendMode(material, currentBlendMode);
        }
        
        // 其他高级设置...
        bool receiveShadows = material.GetInt("_ReceiveShadows") > 0;
        receiveShadows = EditorGUILayout.Toggle("接收阴影", receiveShadows);
        material.SetInt("_ReceiveShadows", receiveShadows ? 1 : 0);
    }
    
    private void UpdateBlendMode(Material material, int blendMode)
    {
        switch (blendMode)
        {
            case 0: // 不透明
                material.SetOverrideTag("RenderType", "Opaque");
                material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                material.SetInt("_ZWrite", 1);
                material.DisableKeyword("_ALPHATEST_ON");
                material.DisableKeyword("_ALPHABLEND_ON");
                material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry;
                break;
                
            case 1: // 透明
                material.SetOverrideTag("RenderType", "Transparent");
                material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                material.SetInt("_ZWrite", 0);
                material.DisableKeyword("_ALPHATEST_ON");
                material.EnableKeyword("_ALPHABLEND_ON");
                material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
                break;
                
            case 2: // 镂空
                material.SetOverrideTag("RenderType", "TransparentCutout");
                material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                material.SetInt("_ZWrite", 1);
                material.EnableKeyword("_ALPHATEST_ON");
                material.DisableKeyword("_ALPHABLEND_ON");
                material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
                break;
                
            case 3: // 添加
                material.SetOverrideTag("RenderType", "Transparent");
                material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One);
                material.SetInt("_ZWrite", 0);
                material.DisableKeyword("_ALPHATEST_ON");
                material.DisableKeyword("_ALPHABLEND_ON");
                material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                material.EnableKeyword("_ADDITIVE_ON");
                material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
                break;
        }
    }
    
    private void DrawPerformanceInfo(Material material)
    {
        EditorGUILayout.Space();
        showPerformanceInfo = EditorGUILayout.Foldout(showPerformanceInfo, "性能分析", true);
        if (showPerformanceInfo)
        {
            EditorGUI.indentLevel++;
            
            GUIStyle warningStyle = new GUIStyle(EditorStyles.label);
            GUIStyle goodStyle = new GUIStyle(EditorStyles.label);
            warningStyle.normal.textColor = Color.yellow;
            goodStyle.normal.textColor = Color.green;
            
            // 计算材质复杂度(简化版)
            int complexity = 0;
            
            // 检查法线贴图
            if (material.GetFloat("_EnableNormalMap") > 0.5f)
            {
                complexity += 2;
                EditorGUILayout.LabelField("法线贴图", "增加2复杂度", warningStyle);
            }
            else
            {
                EditorGUILayout.LabelField("法线贴图", "已禁用", goodStyle);
            }
            
            // 检查自发光
            if (material.GetFloat("_EnableEmission") > 0.5f)
            {
                complexity += 1;
                EditorGUILayout.LabelField("自发光", "增加1复杂度", warningStyle);
            }
            else
            {
                EditorGUILayout.LabelField("自发光", "已禁用", goodStyle);
            }
            
            // 检查混合模式
            int blendMode = material.GetInt("_Mode");
            if (blendMode > 0)
            {
                complexity += 2;
                EditorGUILayout.LabelField("透明/混合", "增加2复杂度", warningStyle);
            }
            else
            {
                EditorGUILayout.LabelField("混合模式", "不透明 (高效)", goodStyle);
            }
            
            // 检查GPU实例化
            if (material.enableInstancing)
            {
                EditorGUILayout.LabelField("GPU实例化", "已启用", goodStyle);
            }
            else
            {
                EditorGUILayout.LabelField("GPU实例化", "未启用", warningStyle);
            }
            
            // 总结
            EditorGUILayout.Space();
            GUIStyle complexityStyle = new GUIStyle(EditorStyles.boldLabel);
            
            if (complexity <= 2)
            {
                complexityStyle.normal.textColor = Color.green;
                EditorGUILayout.LabelField($"总体复杂度: {complexity} (高效)", complexityStyle);
            }
            else if (complexity <= 4)
            {
                complexityStyle.normal.textColor = Color.yellow;
                EditorGUILayout.LabelField($"总体复杂度: {complexity} (中等)", complexityStyle);
            }
            else
            {
                complexityStyle.normal.textColor = Color.red;
                EditorGUILayout.LabelField($"总体复杂度: {complexity} (较高)", complexityStyle);
            }
            
            // 平台特定提示
            EditorGUILayout.Space();
            EditorGUILayout.LabelField($"目标平台: {currentPlatform}", EditorStyles.boldLabel);
            
            switch (currentPlatform)
            {
                case XRPlatformType.Quest:
                    if (complexity > 3)
                    {
                        EditorGUILayout.HelpBox("当前材质复杂度对Quest来说较高。考虑禁用一些特性以提高性能。", MessageType.Warning);
                    }
                    break;
                    
                case XRPlatformType.HoloLens:
                    if (complexity > 2)
                    {
                        EditorGUILayout.HelpBox("HoloLens对材质复杂度非常敏感。建议尽可能简化。", MessageType.Warning);
                    }
                    break;
            }
            
            EditorGUI.indentLevel--;
        }
    }
}
#endif

七、XR平台常见问题解决方案

控制器抖动修复工具

// ControllerStabilizer.cs - 控制器防抖动系统
using UnityEngine;
using System.Collections.Generic;

public class ControllerStabilizer : MonoBehaviour
{
    [Header("平滑设置")]
    [Range(0, 0.5f)]
    [SerializeField] private float positionSmoothing = 0.15f;
    [Range(0, 0.5f)]
    [SerializeField] private float rotationSmoothing = 0.12f;
    [SerializeField] private bool useAdaptiveSmoothing = true;
    [SerializeField] private float velocityThreshold = 0.5f;
    [SerializeField] private float angularVelocityThreshold = 30f;
    
    [Header("抖动检测")]
    [SerializeField] private float jitterThreshold = 0.01f;
    [SerializeField] private float jitterTimeThreshold = 0.05f;
    [SerializeField] private int stabilizerUpdateRate = 90;
    
    // 内部状态变量
    private Vector3 targetPosition;
    private Quaternion targetRotation;
    private Vector3 smoothedPosition;
    private Quaternion smoothedRotation;
    
    // 速度计算
    private Vector3 lastPosition;
    private Quaternion lastRotation;
    private Vector3 velocity;
    private Vector3 angularVelocity;
    
    // 抖动检测
    private Queue<Vector3> positionSamples = new Queue<Vector3>();
    private Queue<Quaternion> rotationSamples = new Queue<Quaternion>();
    private float timeSinceLastJitter = 0f;
    private bool jitterDetected = false;
    
    // 原始转换引用
    private Transform originalTransform;
    
    private void Awake()
    {
        // 创建一个子对象用于平滑移动
        GameObject smoothedObject = new GameObject(gameObject.name + "_Smoothed");
        smoothedObject.transform.SetParent(transform.parent);
        smoothedObject.transform.localPosition = transform.localPosition;
        smoothedObject.transform.localRotation = transform.localRotation;
        
        // 将所有子对象移动到平滑对象下
        for (int i = transform.childCount - 1; i >= 0; i--)
        {
            Transform child = transform.GetChild(i);
            child.SetParent(smoothedObject.transform, true);
        }
        
        // 保存原始Transform引用
        originalTransform = transform;
        
        // 初始化位置和旋转
        targetPosition = originalTransform.position;
        targetRotation = originalTransform.rotation;
        smoothedPosition = targetPosition;
        smoothedRotation = targetRotation;
        
        lastPosition = targetPosition;
        lastRotation = targetRotation;
        
        // 修改当前游戏对象以添加一个空占位符
        transform.SetParent(smoothedObject.transform);
    }
    
    private void Start()
    {
        // 修改更新频率
        if (stabilizerUpdateRate > 0)
        {
            Application.targetFrameRate = stabilizerUpdateRate;
        }
    }
    
    private void Update()
    {
        // 获取当前位置和旋转(来自控制器/跟踪源)
        targetPosition = originalTransform.position;
        targetRotation = originalTransform.rotation;
        
        // 计算速度
        velocity = (targetPosition - lastPosition) / Time.deltaTime;
        
        // 计算角速度(简化)
        Quaternion deltaRotation = targetRotation * Quaternion.Inverse(lastRotation);
        float angle;
        Vector3 axis;
        deltaRotation.ToAngleAxis(out angle, out axis);
        if (angle > 180f) angle -= 360f;
        angularVelocity = axis * (angle * Mathf.Deg2Rad / Time.deltaTime);
        
        // 更新位置采样队列
        positionSamples.Enqueue(targetPosition);
        if (positionSamples.Count > 5) // 保持5个样本
            positionSamples.Dequeue();
            
        rotationSamples.Enqueue(targetRotation);
        if (rotationSamples.Count > 5)
            rotationSamples.Dequeue();
            
        // 检测抖动
        DetectJitter();
        
        // 应用平滑
        ApplySmoothing();
        
        // 更新历史状态
        lastPosition = targetPosition;
        lastRotation = targetRotation;
    }
    
    private void DetectJitter()
    {
        if (positionSamples.Count < 3)
            return;
            
        Vector3[] positions = positionSamples.ToArray();
        
        // 检查连续3个点的方向变化
        for (int i = 2; i < positions.Length; i++)
        {
            Vector3 dir1 = (positions[i-1] - positions[i-2]).normalized;
            Vector3 dir2 = (positions[i] - positions[i-1]).normalized;
            
            // 如果方向突然反转,可能是抖动
            if (Vector3.Dot(dir1, dir2) < -0.7f && 
                Vector3.Distance(positions[i], positions[i-1]) < jitterThreshold)
            {
                jitterDetected = true;
                timeSinceLastJitter = 0f;
                return;
            }
        }
        
        // 更新抖动计时器
        timeSinceLastJitter += Time.deltaTime;
        if (timeSinceLastJitter > jitterTimeThreshold)
        {
            jitterDetected = false;
        }
    }
    
    private void ApplySmoothing()
    {
        float posSmooth = positionSmoothing;
        float rotSmooth = rotationSmoothing;
        
        // 根据速度自适应调整平滑度
        if (useAdaptiveSmoothing)
        {
            float velocityFactor = Mathf.Clamp01(velocity.magnitude / velocityThreshold);
            float angularFactor = Mathf.Clamp01(angularVelocity.magnitude / angularVelocityThreshold);
            
            // 运动越快,平滑度越低
            posSmooth = Mathf.Lerp(positionSmoothing, 0.01f, velocityFactor);
            rotSmooth = Mathf.Lerp(rotationSmoothing, 0.01f, angularFactor);
        }
        
        // 如果检测到抖动,增加平滑度
        if (jitterDetected)
        {
            posSmooth = Mathf.Max(posSmooth, 0.3f);
            rotSmooth = Mathf.Max(rotSmooth, 0.3f);
        }
        
        // 平滑位置
        smoothedPosition = Vector3.Lerp(smoothedPosition, targetPosition, 1 - posSmooth);
        
        // 平滑旋转
        smoothedRotation = Quaternion.Slerp(smoothedRotation, targetRotation, 1 - rotSmooth);
        
        // 应用平滑的位置和旋转
        transform.parent.position = smoothedPosition;
        transform.parent.rotation = smoothedRotation;
    }
}

空间映射修复与增强

// SpatialMappingEnhancer.cs - 空间映射修复与增强
using UnityEngine;
using System.Collections.Generic;

public class SpatialMappingEnhancer : MonoBehaviour
{
    [Header("平台设置")]
    [SerializeField] private XRPlatformType targetPlatform;
    
    [Header("网格优化设置")]
    [SerializeField] private bool optimizeMeshes = true;
    [SerializeField] private bool simplifyMeshes = true;
    [Range(0.01f, 0.5f)]
    [SerializeField] private float simplificationDistance = 0.05f;
    [SerializeField] private bool fillHoles = true;
    [Range(0.05f, 0.5f)]
    [SerializeField] private float holeDetectionSize = 0.2f;
    
    [Header("平面检测设置")]
    [SerializeField] private bool detectPlanes = true;
    [Range(0.5f, 10f)]
    [SerializeField] private float minPlaneSize = 0.5f;
    [SerializeField] private LayerMask spatialMappingLayer;
    [SerializeField] private GameObject planeMarkerPrefab;
    
    [Header("可视化")]
    [SerializeField] private bool visualizeOptimizedMeshes = true;
    [SerializeField] private Material optimizedMeshMaterial;
    [SerializeField] private bool visualizePlanes = true;
    
    // 内部状态
    private List<MeshFilter> originalMeshFilters = new List<MeshFilter>();
    private List<MeshFilter> optimizedMeshFilters = new List<MeshFilter>();
    private List<GameObject> detectedPlanes = new List<GameObject>();
    private bool hasInitialized = false;
    
    private void Start()
    {
        // 延迟初始化,等待空间映射完成
        Invoke("Initialize", 2.0f);
    }
    
    private void Initialize()
    {
        Debug.Log("初始化空间映射增强器");
        
        // 收集所有空间映射网格
        CollectSpatialMeshes();
        
        if (optimizeMeshes)
        {
            OptimizeSpatialMeshes();
        }
        
        if (detectPlanes)
        {
            DetectPlanesInEnvironment();
        }
        
        hasInitialized = true;
    }
    
    private void CollectSpatialMeshes()
    {
        // 查找具有空间映射层的网格
        MeshFilter[] meshFilters = FindObjectsOfType<MeshFilter>();
        
        foreach (MeshFilter filter in meshFilters)
        {
            if (((1 << filter.gameObject.layer) & spatialMappingLayer) != 0)
            {
                originalMeshFilters.Add(filter);
                Debug.Log($"找到空间映射网格: {filter.gameObject.name}");
            }
        }
        
        Debug.Log($"总共找到 {originalMeshFilters.Count} 个空间映射网格");
    }
    
    private void OptimizeSpatialMeshes()
    {
        foreach (MeshFilter originalFilter in originalMeshFilters)
        {
            // 创建优化的网格对象
            GameObject optimizedObj = new GameObject(originalFilter.gameObject.name + "_Optimized");
            optimizedObj.transform.SetParent(transform);
            optimizedObj.transform.position = originalFilter.transform.position;
            optimizedObj.transform.rotation = originalFilter.transform.rotation;
            optimizedObj.transform.localScale = originalFilter.transform.localScale;
            
            // 添加组件
            MeshFilter optimizedFilter = optimizedObj.AddComponent<MeshFilter>();
            MeshRenderer renderer = optimizedObj.AddComponent<MeshRenderer>();
            MeshCollider collider = optimizedObj.AddComponent<MeshCollider>();
            
            // 复制和优化网格
            Mesh optimizedMesh = OptimizeMesh(originalFilter.sharedMesh);
            
            // 应用优化的网格
            optimizedFilter.sharedMesh = optimizedMesh;
            collider.sharedMesh = optimizedMesh;
            
            // 设置材质
            if (visualizeOptimizedMeshes && optimizedMeshMaterial != null)
            {
                renderer.sharedMaterial = optimizedMeshMaterial;
            }
            else
            {
                // 尝试从原始对象复制材质
                MeshRenderer originalRenderer = originalFilter.GetComponent<MeshRenderer>();
                if (originalRenderer != null)
                {
                    renderer.sharedMaterial = originalRenderer.sharedMaterial;
                }
            }
            
            // 将原始网格禁用
            originalFilter.gameObject.SetActive(false);
            
            // 添加到优化列表
            optimizedMeshFilters.Add(optimizedFilter);
        }
        
        Debug.Log($"已优化 {optimizedMeshFilters.Count} 个空间映射网格");
    }
    
    private Mesh OptimizeMesh(Mesh originalMesh)
    {
        if (originalMesh == null)
            return null;
            
        Mesh optimizedMesh = new Mesh();
        
        // 复制原始网格数据
        Vector3[] vertices = originalMesh.vertices;
        int[] triangles = originalMesh.triangles;
        Vector3[] normals = originalMesh.normals;
        Vector2[] uvs = originalMesh.uv;
        
        if (simplifyMeshes)
        {
            // 简化网格
            // 注意:实际实现会使用更复杂的算法
            Vector3[] simplifiedVertices;
            int[] simplifiedTriangles;
            Vector3[] simplifiedNormals;
            
            SimplifyMesh(vertices, triangles, normals, 
                        out simplifiedVertices, out simplifiedTriangles, out simplifiedNormals);
                        
            vertices = simplifiedVertices;
            triangles = simplifiedTriangles;
            normals = simplifiedNormals;
            
            // 重新生成UVs
            uvs = GenerateSimpleUVs(vertices);
        }
        
        if (fillHoles)
        {
            // 检测和填充孔洞
            FillMeshHoles(ref vertices, ref triangles, ref normals, ref uvs);
        }
        
        // 分配处理后的数据
        optimizedMesh.vertices = vertices;
        optimizedMesh.triangles = triangles;
        optimizedMesh.normals = normals;
        optimizedMesh.uv = uvs;
        
        // 重新计算边界
        optimizedMesh.RecalculateBounds();
        
        return optimizedMesh;
    }
    
    private void SimplifyMesh(Vector3[] vertices, int[] triangles, Vector3[] normals,
                             out Vector3[] simplifiedVertices, out int[] simplifiedTriangles, out Vector3[] simplifiedNormals)
    {
        // 这只是一个简化的演示版本
        // 实际的网格简化算法会更加复杂
        
        // 为了演示,将保留边缘顶点,合并距离较近的顶点
        Dictionary<Vector3, int> uniqueVertices = new Dictionary<Vector3, int>();
        List<Vector3> newVertices = new List<Vector3>();
        List<int> newTriangles = new List<int>();
        List<Vector3> newNormals = new List<Vector3>();
        
        // 映射表存储旧顶点索引到新顶点索引的映射
        int[] vertexMapping = new int[vertices.Length];
        
        // 处理每个顶点
        for (int i = 0; i < vertices.Length; i++)
        {
            Vector3 roundedPos = RoundVector(vertices[i], simplificationDistance);
            
            if (uniqueVertices.ContainsKey(roundedPos))
            {
                // 如果已存在相似位置的顶点,使用该顶点
                vertexMapping[i] = uniqueVertices[roundedPos];
            }
            else
            {
                // 否则添加新顶点
                int newIndex = newVertices.Count;
                uniqueVertices[roundedPos] = newIndex;
                vertexMapping[i] = newIndex;
                
                newVertices.Add(vertices[i]);
                newNormals.Add(normals[i]);
            }
        }
        
        // 重建三角形
        for (int i = 0; i < triangles.Length; i += 3)
        {
            int a = vertexMapping[triangles[i]];
            int b = vertexMapping[triangles[i + 1]];
            int c = vertexMapping[triangles[i + 2]];
            
            // 跳过退化三角形
            if (a != b && b != c && a != c)
            {
                newTriangles.Add(a);
                newTriangles.Add(b);
                newTriangles.Add(c);
            }
        }
        
        simplifiedVertices = newVertices.ToArray();
        simplifiedTriangles = newTriangles.ToArray();
        simplifiedNormals = newNormals.ToArray();
        
        Debug.Log($"简化网格: 从 {vertices.Length} 顶点减少到 {simplifiedVertices.Length} 顶点");
    }
    
    private Vector3 RoundVector(Vector3 vec, float precision)
    {
        // 将向量舍入到指定精度
        return new Vector3(
            Mathf.Round(vec.x / precision) * precision,
            Mathf.Round(vec.y / precision) * precision,
            Mathf.Round(vec.z / precision) * precision
        );
    }
    
    private Vector2[] GenerateSimpleUVs(Vector3[] vertices)
    {
        // 简单的UV生成
        Vector2[] uvs = new Vector2[vertices.Length];
        for (int i = 0; i < vertices.Length; i++)
        {
            // 使用XZ平面投影
            uvs[i] = new Vector2(vertices[i].x, vertices[i].z);
        }
        return uvs;
    }
    
    private void FillMeshHoles(ref Vector3[] vertices, ref int[] triangles, ref Vector3[] normals, ref Vector2[] uvs)
    {
        // 这是一个复杂的过程,这里只提供一个简化版本
        // 实际实现需要边界检测算法
        
        // 检测边界边
        Dictionary<Vector2Int, int> edgeCount = new Dictionary<Vector2Int, int>();
        
        for (int i = 0; i < triangles.Length; i += 3)
        {
            int a = triangles[i];
            int b = triangles[i + 1];
            int c = triangles[i + 2];
            
            CountEdge(edgeCount, a, b);
            CountEdge(edgeCount, b, c);
            CountEdge(edgeCount, c, a);
        }
        
        // 收集只出现一次的边(边界边)
        List<Vector2Int> boundaryEdges = new List<Vector2Int>();
        foreach (var edge in edgeCount)
        {
            if (edge.Value == 1)
            {
                boundaryEdges.Add(edge.Key);
            }
        }
        
        // 如果边界边数量过多,跳过填充
        if (boundaryEdges.Count > 100)
        {
            Debug.Log("边界太复杂,跳过孔洞填充");
            return;
        }
        
        // 寻找连续的边界回路
        List<List<int>> boundaryLoops = FindBoundaryLoops(boundaryEdges);
        
        // 填充每个孔洞
        List<Vector3> newVertices = new List<Vector3>(vertices);
        List<int> newTriangles = new List<int>(triangles);
        List<Vector3> newNormals = new List<Vector3>(normals);
        List<Vector2> newUVs = new List<Vector2>(uvs);
        
        foreach (var loop in boundaryLoops)
        {
            if (loop.Count >= 3 && IsHoleSizeValid(vertices, loop))
            {
                FillHole(loop, newVertices, newTriangles, newNormals, newUVs);
            }
        }
        
        vertices = newVertices.ToArray();
        triangles = newTriangles.ToArray();
        normals = newNormals.ToArray();
        uvs = newUVs.ToArray();
    }
    
    private void CountEdge(Dictionary<Vector2Int, int> edgeCount, int v1, int v2)
    {
        // 确保边的顶点顺序一致
        Vector2Int edge = v1 < v2 ? new Vector2Int(v1, v2) : new Vector2Int(v2, v1);
        
        if (edgeCount.ContainsKey(edge))
        {
            edgeCount[edge]++;
        }
        else
        {
            edgeCount[edge] = 1;
        }
    }
    
    private List<List<int>> FindBoundaryLoops(List<Vector2Int> boundaryEdges)
    {
        // 寻找连续的边界回路
        List<List<int>> loops = new List<List<int>>();
        
        // 建立顶点连接关系
        Dictionary<int, List<int>> vertexConnections = new Dictionary<int, List<int>>();
        
        foreach (var edge in boundaryEdges)
        {
            if (!vertexConnections.ContainsKey(edge.x))
                vertexConnections[edge.x] = new List<int>();
                
            if (!vertexConnections.ContainsKey(edge.y))
                vertexConnections[edge.y] = new List<int>();
                
            vertexConnections[edge.x].Add(edge.y);
            vertexConnections[edge.y].Add(edge.x);
        }
        
        // 寻找回路
        HashSet<int> processedVertices = new HashSet<int>();
        
        foreach (var vertex in vertexConnections.Keys)
        {
            if (!processedVertices.Contains(vertex))
            {
                List<int> loop = new List<int>();
                int current = vertex;
                
                while (true)
                {
                    loop.Add(current);
                    processedVertices.Add(current);
                    
                    bool foundNext = false;
                    foreach (int next in vertexConnections[current])
                    {
                        if (!processedVertices.Contains(next))
                        {
                            current = next;
                            foundNext = true;
                            break;
                        }
                    }
                    
                    if (!foundNext || loop.Count > 1000) // 安全检查
                        break;
                }
                
                if (loop.Count >= 3)
                {
                    loops.Add(loop);
                }
            }
        }
        
        return loops;
    }
    
    private bool IsHoleSizeValid(Vector3[] vertices, List<int> loop)
    {
        // 检查孔洞大小是否在合理范围内
        if (loop.Count < 3 || loop.Count > 50) // 过大或过小的孔洞跳过
            return false;
            
        // 计算孔洞周长
        float perimeter = 0;
        for (int i = 0; i < loop.Count; i++)
        {
            int j = (i + 1) % loop.Count;
            perimeter += Vector3.Distance(vertices[loop[i]], vertices[loop[j]]);
        }
        
        // 如果周长太小,可能是小裂缝而非需要填充的孔洞
        if (perimeter < holeDetectionSize)
            return false;
            
        return true;
    }
    
    private void FillHole(List<int> boundaryLoop, List<Vector3> vertices, List<int> triangles, 
                         List<Vector3> normals, List<Vector2> uvs)
    {
        // 这里使用简单的三角形扇形填充
        // 实际应用可能需要更复杂的算法,如耳切算法
        
        // 计算孔洞中心点
        Vector3 center = Vector3.zero;
        Vector3 avgNormal = Vector3.zero;
        Vector2 avgUV = Vector2.zero;
        
        foreach (int idx in boundaryLoop)
        {
            center += vertices[idx];
            avgNormal += normals[idx];
            avgUV += uvs[idx];
        }
        
        center /= boundaryLoop.Count;
        avgNormal = (avgNormal / boundaryLoop.Count).normalized;
        avgUV /= boundaryLoop.Count;
        
        // 添加中心点
        int centerIndex = vertices.Count;
        vertices.Add(center);
        normals.Add(avgNormal);
        uvs.Add(avgUV);
        
        // 创建三角形
        for (int i = 0; i < boundaryLoop.Count; i++)
        {
            int next = (i + 1) % boundaryLoop.Count;
            
            triangles.Add(centerIndex);
            triangles.Add(boundaryLoop[i]);
            triangles.Add(boundaryLoop[next]);
        }
    }
    
    private void DetectPlanesInEnvironment()
    {
        // 检测环境中的平面
        foreach (MeshFilter filter in originalMeshFilters)
        {
            Mesh mesh = filter.sharedMesh;
            if (mesh == null) continue;
            
            Vector3[] vertices = mesh.vertices;
            int[] triangles = mesh.triangles;
            
            if (vertices.Length == 0 || triangles.Length == 0)
                continue;
                
            // 平面检测逻辑
            List<Vector3[]> potentialPlanes = FindPotentialPlanes(vertices, triangles, filter.transform);
            
            // 创建平面标记
            if (planeMarkerPrefab != null)
            {
                CreatePlaneMarkers(potentialPlanes);
            }
        }
    }
    
    private List<Vector3[]> FindPotentialPlanes(Vector3[] localVertices, int[] triangles, Transform meshTransform)
    {
        List<Vector3[]> potentialPlanes = new List<Vector3[]>();
        HashSet<int> processedTriangles = new HashSet<int>();
        
        // 将顶点从局部坐标转换为世界坐标
        Vector3[] worldVertices = new Vector3[localVertices.Length];
        for (int i = 0; i < localVertices.Length; i++)
        {
            worldVertices[i] = meshTransform.TransformPoint(localVertices[i]);
        }
        
        // 处理三角形
        for (int i = 0; i < triangles.Length; i += 3)
        {
            if (processedTriangles.Contains(i))
                continue;
                
            // 获取三角形
            int a = triangles[i];
            int b = triangles[i + 1];
            int c = triangles[i + 2];
            
            // 计算三角形法线
            Vector3 normal = Vector3.Cross(
                worldVertices[b] - worldVertices[a],
                worldVertices[c] - worldVertices[a]
            ).normalized;
            
            // 检查法线是否指向上方
            if (Mathf.Abs(Vector3.Dot(normal, Vector3.up)) > 0.95f)
            {
                // 寻找相连的相似朝向三角形
                List<Vector3> planeVertices = new List<Vector3>();
                HashSet<int> planeTriangles = new HashSet<int>();
                
                // 将初始三角形加入平面
                planeVertices.Add(worldVertices[a]);
                planeVertices.Add(worldVertices[b]);
                planeVertices.Add(worldVertices[c]);
                planeTriangles.Add(i);
                processedTriangles.Add(i);
                
                // 扩展平面
                GrowPlane(triangles, worldVertices, normal, ref planeVertices, ref planeTriangles, ref processedTriangles);
                
                // 检查平面大小
                if (IsLargeEnoughPlane(planeVertices))
                {
                    potentialPlanes.Add(planeVertices.ToArray());
                }
            }
        }
        
        return potentialPlanes;
    }
    
    private void GrowPlane(int[] triangles, Vector3[] worldVertices, Vector3 normal, 
                         ref List<Vector3> planeVertices, ref HashSet<int> planeTriangles, 
                         ref HashSet<int> processedTriangles)
    {
        // 这是一个简化的区域增长算法
        // 实际实现会更复杂并考虑边界连接性
        
        bool foundNewTriangles = true;
        
        while (foundNewTriangles)
        {
            foundNewTriangles = false;
            
            for (int i = 0; i < triangles.Length; i += 3)
            {
                if (processedTriangles.Contains(i))
                    continue;
                    
                int a = triangles[i];
                int b = triangles[i + 1];
                int c = triangles[i + 2];
                
                // 检查此三角形是否与平面相连
                bool connected = false;
                
                foreach (int idx in new int[] { a, b, c })
                {
                    if (planeVertices.Contains(worldVertices[idx]))
                    {
                        connected = true;
                        break;
                    }
                }
                
                if (connected)
                {
                    // 计算三角形法线
                    Vector3 triNormal = Vector3.Cross(
                        worldVertices[b] - worldVertices[a],
                        worldVertices[c] - worldVertices[a]
                    ).normalized;
                    
                    // 检查法线是否与平面法线相似
                    if (Vector3.Dot(normal, triNormal) > 0.95f)
                    {
                        // 添加到平面
                        if (!planeVertices.Contains(worldVertices[a]))
                            planeVertices.Add(worldVertices[a]);
                            
                        if (!planeVertices.Contains(worldVertices[b]))
                            planeVertices.Add(worldVertices[b]);
                            
                        if (!planeVertices.Contains(worldVertices[c]))
                            planeVertices.Add(worldVertices[c]);
                            
                        planeTriangles.Add(i);
                        processedTriangles.Add(i);
                        foundNewTriangles = true;
                    }
                }
            }
        }
    }
    
    private bool IsLargeEnoughPlane(List<Vector3> planeVertices)
    {
        // 找出平面的尺寸
        Vector3 min = planeVertices[0];
        Vector3 max = planeVertices[0];
        
        foreach (Vector3 v in planeVertices)
        {
            min = Vector3.Min(min, v);
            max = Vector3.Max(max, v);
        }
        
        // 计算边界框尺寸
        Vector3 size = max - min;
        
        // 检查平面尺寸是否足够大
        return size.x > minPlaneSize && size.z > minPlaneSize;
    }
    
    private void CreatePlaneMarkers(List<Vector3[]> planes)
    {
        foreach (Vector3[] planeVertices in planes)
        {
            // 计算平面中心
            Vector3 center = Vector3.zero;
            foreach (Vector3 v in planeVertices)
            {
                center += v;
            }
            center /= planeVertices.Length;
            
            // 实例化标记物
            GameObject marker = Instantiate(planeMarkerPrefab, center, Quaternion.identity);
            marker.transform.SetParent(transform);
            
            // 添加到列表
            detectedPlanes.Add(marker);
        }
        
        Debug.Log($"检测到 {detectedPlanes.Count} 个平面");
    }
    
    public void UpdateMappingAsync()
    {
        // 当空间映射更新时调用此方法
        if (!hasInitialized)
        {
            Initialize();
        }
        else
        {
            // 清理旧数据
            CleanupOldData();
            
            // 重新收集和处理
            CollectSpatialMeshes();
            
            if (optimizeMeshes)
            {
                OptimizeSpatialMeshes();
            }
            
            if (detectPlanes)
            {
                DetectPlanesInEnvironment();
            }
        }
    }
    
    private void CleanupOldData()
    {
        // 清理优化的网格
        foreach (MeshFilter filter in optimizedMeshFilters)
        {
            if (filter != null && filter.gameObject != null)
            {
                Destroy(filter.gameObject);
            }
        }
        optimizedMeshFilters.Clear();
        
        // 清理平面标记
        foreach (GameObject plane in detectedPlanes)
        {
            if (plane != null)
            {
                Destroy(plane);
            }
        }
        detectedPlanes.Clear();
        
        // 清理原始网格引用
        originalMeshFilters.Clear();
    }
    
    private void OnDestroy()
    {
        CleanupOldData();
    }
}

XR手势识别系统

// XRGestureRecognizer.cs - XR手势识别系统
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Events;

public enum GestureType
{
    Pinch,
    Grab,
    Point,
    ThumbsUp,
    Victory,
    Wave,
    Swipe,
    Custom
}

[System.Serializable]
public class GestureDetectedEvent : UnityEvent<GestureType, Vector3> { }

public class XRGestureRecognizer : MonoBehaviour
{
    [Header("平台设置")]
    [SerializeField] private XRPlatformType targetPlatform;
    
    [Header("手势设置")]
    [SerializeField] private bool trackLeftHand = true;
    [SerializeField] private bool trackRightHand = true;
    [SerializeField] private bool continuousDetection = true;
    [SerializeField] private float gestureTimeout = 0.5f;
    [SerializeField] private float confidenceThreshold = 0.8f;
    
    [Header("检测灵敏度")]
    [Range(0.5f, 1.0f)]
    [SerializeField] private float pinchSensitivity = 0.8f;
    [Range(0.5f, 1.0f)]
    [SerializeField] private float grabSensitivity = 0.7f;
    [Range(0.1f, 0.3f)]
    [SerializeField] private float swipeDistanceThreshold = 0.2f;
    [Range(0.2f, 1.0f)]
    [SerializeField] private float swipeTimeThreshold = 0.5f;
    
    [Header("调试")]
    [SerializeField] private bool showDebugInfo = false;
    [SerializeField] private bool visualizeHandTracking = false;
    [SerializeField] private GameObject handVisualizerPrefab;
    
    [Header("事件")]
    public GestureDetectedEvent onGestureDetected;
    
    // 手势数据
    private struct FingerData
    {
        public Vector3 position;
        public Vector3 direction;
        public float flexion; // 0=直, 1=完全弯曲
        
        public FingerData(Vector3 pos, Vector3 dir, float flex)
        {
            position = pos;
            direction = dir;
            flexion = flex;
        }
    }
    
    private struct HandData
    {
        public Vector3 palmPosition;
        public Vector3 palmNormal;
        public FingerData thumb;
        public FingerData index;
        public FingerData middle;
        public FingerData ring;
        public FingerData pinky;
        public Vector3 wristPosition;
        
        // 轨迹跟踪
        public List<Vector3> recentPositions;
        public List<float> recentTimestamps;
    }
    
    // 状态数据
    private HandData leftHandData;
    private HandData rightHandData;
    private Dictionary<GestureType, float> gestureConfidence = new Dictionary<GestureType, float>();
    private Dictionary<GestureType, float> gestureLastDetectedTime = new Dictionary<GestureType, float>();
    
    // 可视化对象
    private GameObject leftHandVisualizer;
    private GameObject rightHandVisualizer;
    
    // 自定义手势
    private class CustomGesture
    {
        public string name;
        public List<Vector3> trajectory;
        public float similarity;
    }
    
    private List<CustomGesture> customGestures = new List<CustomGesture>();
    
    private void Awake()
    {
        // 初始化手势数据结构
        leftHandData.recentPositions = new List<Vector3>();
        leftHandData.recentTimestamps = new List<float>();
        rightHandData.recentPositions = new List<Vector3>();
        rightHandData.recentTimestamps = new List<float>();
        
        // 初始化手势信息
        foreach (GestureType gesture in System.Enum.GetValues(typeof(GestureType)))
        {
            gestureConfidence[gesture] = 0f;
            gestureLastDetectedTime[gesture] = -gestureTimeout;
        }
        
        // 初始化手势可视化
        if (visualizeHandTracking && handVisualizerPrefab != null)
        {
            if (trackLeftHand)
            {
                leftHandVisualizer = Instantiate(handVisualizerPrefab);
                leftHandVisualizer.name = "LeftHandVisualizer";
                leftHandVisualizer.SetActive(false);
            }
            
            if (trackRightHand)
            {
                rightHandVisualizer = Instantiate(handVisualizerPrefab);
                rightHandVisualizer.name = "RightHandVisualizer";
                rightHandVisualizer.SetActive(false);
            }
        }
    }
    
    private void Update()
    {
        // 获取手部数据
        bool leftHandValid = trackLeftHand && UpdateHandData(true, ref leftHandData);
        bool rightHandValid = trackRightHand && UpdateHandData(false, ref rightHandData);
        
        // 更新手势可视化
        if (visualizeHandTracking)
        {
            UpdateHandVisualization(leftHandValid, leftHandData, leftHandVisualizer);
            UpdateHandVisualization(rightHandValid, rightHandData, rightHandVisualizer);
        }
        
        // 检测手势
        if (leftHandValid)
        {
            DetectGestures(leftHandData, true);
        }
        
        if (rightHandValid)
        {
            DetectGestures(rightHandData, false);
        }
    }
    
    private bool UpdateHandData(bool isLeft, ref HandData handData)
    {
        switch (targetPlatform)
        {
            case XRPlatformType.Quest:
                return UpdateQuestHandData(isLeft, ref handData);
                
            case XRPlatformType.Pico:
                return UpdatePicoHandData(isLeft, ref handData);
                
            case XRPlatformType.HoloLens:
                return UpdateHoloLensHandData(isLeft, ref handData);
                
            default:
                return UpdateGenericXRHandData(isLeft, ref handData);
        }
    }
    
    private bool UpdateQuestHandData(bool isLeft, ref HandData handData)
    {
        #if QUEST_SDK && !UNITY_EDITOR
        // 获取OVR手部数据
        OVRHand hand = isLeft ? 
            OVRInput.GetActiveController(OVRInput.Hand.HandLeft).GetComponent<OVRHand>() : 
            OVRInput.GetActiveController(OVRInput.Hand.HandRight).GetComponent<OVRHand>();
            
        if (hand == null || !hand.IsTracked)
            return false;
            
        // 更新手掌数据
        handData.palmPosition = hand.GetRootPose().position;
        handData.palmNormal = hand.GetRootPose().rotation * Vector3.down;
        handData.wristPosition = hand.GetWristPose().position;
        
        // 更新拇指数据
        OVRHand.HandFinger thumbFinger = OVRHand.HandFinger.Thumb;
        OVRSkeleton.BoneId thumbTip = OVRSkeleton.BoneId.Hand_ThumbTip;
        OVRSkeleton.BoneId thumbBase = OVRSkeleton.BoneId.Hand_Thumb0;
        
        handData.thumb = GetFingerData(hand, thumbFinger, thumbTip, thumbBase);
        
        // 更新食指数据
        OVRHand.HandFinger indexFinger = OVRHand.HandFinger.Index;
        OVRSkeleton.BoneId indexTip = OVRSkeleton.BoneId.Hand_IndexTip;
        OVRSkeleton.BoneId indexBase = OVRSkeleton.BoneId.Hand_Index1;
        
        handData.index = GetFingerData(hand, indexFinger, indexTip, indexBase);
        
        // 更新中指数据
        OVRHand.HandFinger middleFinger = OVRHand.HandFinger.Middle;
        OVRSkeleton.BoneId middleTip = OVRSkeleton.BoneId.Hand_MiddleTip;
        OVRSkeleton.BoneId middleBase = OVRSkeleton.BoneId.Hand_Middle1;
        
        handData.middle = GetFingerData(hand, middleFinger, middleTip, middleBase);
        
        // 更新无名指数据
        OVRHand.HandFinger ringFinger = OVRHand.HandFinger.Ring;
        OVRSkeleton.BoneId ringTip = OVRSkeleton.BoneId.Hand_RingTip;
        OVRSkeleton.BoneId ringBase = OVRSkeleton.BoneId.Hand_Ring1;
        
        handData.ring = GetFingerData(hand, ringFinger, ringTip, ringBase);
        
        // 更新小指数据
        OVRHand.HandFinger pinkyFinger = OVRHand.HandFinger.Pinky;
        OVRSkeleton.BoneId pinkyTip = OVRSkeleton.BoneId.Hand_PinkyTip;
        OVRSkeleton.BoneId pinkyBase = OVRSkeleton.BoneId.Hand_Pinky1;
        
        handData.pinky = GetFingerData(hand, pinkyFinger, pinkyTip, pinkyBase);
        
        // 更新手部轨迹
        UpdateHandTrajectory(ref handData);
        
        return true;
        #else
        return false;
        #endif
    }
    
    private bool UpdatePicoHandData(bool isLeft, ref HandData handData)
    {
        #if PICO_SDK && !UNITY_EDITOR
        // 获取Pico手部数据
        Unity.XR.PXR.HandType hand = isLeft ? 
            Unity.XR.PXR.HandType.HandLeft : 
            Unity.XR.PXR.HandType.HandRight;
            
        if (!Unity.XR.PXR.PXR_HandTracking.GetTrackingState(hand))
            return false;
            
        // 更新手掌数据
        handData.palmPosition = Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointPalm, hand);
        
        Vector3 palmDir = Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointMiddleProximal, hand) - 
            Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointWrist, hand);
        handData.palmNormal = Vector3.Cross(
            palmDir, 
            Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
                Unity.XR.PXR.HandJoint.JointIndexProximal, hand) - 
            Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
                Unity.XR.PXR.HandJoint.JointPinkyProximal, hand)
        ).normalized;
        
        handData.wristPosition = Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointWrist, hand);
            
        // 更新拇指数据
        Vector3 thumbTipPos = Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointThumbTip, hand);
        Vector3 thumbBasePos = Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointThumbProximal, hand);
        float thumbFlex = Unity.XR.PXR.PXR_HandTracking.GetJointBendValue(
            Unity.XR.PXR.HandJoint.JointThumbProximal, hand);
        
        handData.thumb = new FingerData(
            thumbTipPos,
            (thumbTipPos - thumbBasePos).normalized,
            thumbFlex
        );
        
        // 更新食指数据
        Vector3 indexTipPos = Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointIndexTip, hand);
        Vector3 indexBasePos = Unity.XR.PXR.PXR_HandTracking.GetJointPositionFromWrist(
            Unity.XR.PXR.HandJoint.JointIndexProximal, hand);
        float indexFlex = Unity.XR.PXR.PXR_HandTracking.GetJointBendValue(
            Unity.XR.PXR.HandJoint.JointIndexProximal, hand);
        
        handData.index = new FingerData(
            indexTipPos,
            (indexTipPos - indexBasePos).normalized,
            indexFlex
        );
        
        // 中指、无名指和小指数据同样更新...
        
        // 更新手部轨迹
        UpdateHandTrajectory(ref handData);
        
        return true;
        #else
        return false;
        #endif
    }
    
    private bool UpdateHoloLensHandData(bool isLeft, ref HandData handData)
    {
        #if HOLOLENS_SDK && !UNITY_EDITOR
        // HoloLens手部数据更新
        // 这里提供简化实现,实际应用需使用MRTK API
        
        // 对于HoloLens,获取关节数据会更复杂
        // 这是一个简化示例
        var handSource = Microsoft.MixedReality.Toolkit.InputSystem.InputRecognizer.HandSource;
        if (handSource == null)
            return false;
            
        var hand = isLeft ? 
            handSource.GetHand(Microsoft.MixedReality.Toolkit.Utilities.Handedness.Left) : 
            handSource.GetHand(Microsoft.MixedReality.Toolkit.Utilities.Handedness.Right);
            
        if (hand == null || !hand.IsTracked)
            return false;
            
        // 获取关节数据
        handData.palmPosition = hand.GetJointPose(
            Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint.Palm).Position;
        handData.wristPosition = hand.GetJointPose(
            Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint.Wrist).Position;
            
        // 计算手掌法线
        var palmPose = hand.GetJointPose(
            Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint.Palm);
        handData.palmNormal = palmPose.Rotation * Vector3.up;
        
        // 获取手指数据
        handData.thumb = GetFingerDataFromMRTK(hand, 
            Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint.ThumbTip,
            Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint.ThumbProximal);
            
        handData.index = GetFingerDataFromMRTK(hand, 
            Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint.IndexTip,
            Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint.IndexProximal);
            
        // 更新手部轨迹
        UpdateHandTrajectory(ref handData);
        
        return true;
        #else
        return false;
        #endif
    }
    
    private bool UpdateGenericXRHandData(bool isLeft, ref HandData handData)
    {
        // 通用XR手部数据更新
        // 实际应用中,这里可以使用Unity XR Interaction Toolkit
        // 或其他通用API获取手部数据
        
        // 这是模拟数据,用于编辑器测试
        #if UNITY_EDITOR
        // 使用输入模拟生成手部数据
        if (isLeft)
        {
            if (Input.GetKey(KeyCode.LeftShift))
            {
                Vector3 handPos = Camera.main.transform.position + 
                                 Camera.main.transform.forward * 0.3f +
                                 Camera.main.transform.right * -0.2f;
                
                // 模拟左手数据
                handData.palmPosition = handPos;
                handData.palmNormal = Camera.main.transform.right;
                handData.wristPosition = handPos - Camera.main.transform.up * 0.05f;
                
                // 模拟手指弯曲度
                bool pinch = Input.GetKey(KeyCode.Z);
                bool grab = Input.GetKey(KeyCode.X);
                bool point = Input.GetKey(KeyCode.C);
                
                // 模拟拇指
                handData.thumb = new FingerData(
                    handPos + Camera.main.transform.right * 0.03f,
                    Camera.main.transform.right,
                    pinch ? 0.8f : 0.2f
                );
                
                // 模拟食指
                handData.index = new FingerData(
                    handPos + Camera.main.transform.forward * 0.05f,
                    Camera.main.transform.forward,
                    pinch || grab ? 0.9f : (point ? 0.1f : 0.7f)
                );
                
                // 模拟中指
                handData.middle = new FingerData(
                    handPos + Camera.main.transform.forward * 0.04f,
                    Camera.main.transform.forward,
                    grab || point ? 0.9f : 0.5f
                );
                
                // 更新手部轨迹
                UpdateHandTrajectory(ref handData);
                
                return true;
            }
        }
        else // 右手
        {
            if (Input.GetKey(KeyCode.RightShift))
            {
                Vector3 handPos = Camera.main.transform.position + 
                                 Camera.main.transform.forward * 0.3f +
                                 Camera.main.transform.right * 0.2f;
                
                // 模拟右手数据
                handData.palmPosition = handPos;
                handData.palmNormal = -Camera.main.transform.right;
                handData.wristPosition = handPos - Camera.main.transform.up * 0.05f;
                
                // 模拟手指弯曲度
                bool pinch = Input.GetKey(KeyCode.M);
                bool grab = Input.GetKey(KeyCode.N);
                bool point = Input.GetKey(KeyCode.B);
                
                // 模拟手指数据...
                // 与左手类似
                
                // 更新手部轨迹
                UpdateHandTrajectory(ref handData);
                
                return true;
            }
        }
        #endif
        
        return false;
    }
    
    #if QUEST_SDK
    private FingerData GetFingerData(OVRHand hand, OVRHand.HandFinger finger, 
                                  OVRSkeleton.BoneId tipBone, OVRSkeleton.BoneId baseBone)
    {
        // 获取手指位置和方向
        Vector3 tipPosition = hand.GetSkeleton().Bones[(int)tipBone].Transform.position;
        Vector3 basePosition = hand.GetSkeleton().Bones[(int)baseBone].Transform.position;
        Vector3 direction = (tipPosition - basePosition).normalized;
        
        // 获取弯曲度
        float flexion = hand.GetFingerPinchStrength(finger);
        
        return new FingerData(tipPosition, direction, flexion);
    }
    #endif
    
    #if HOLOLENS_SDK
    private FingerData GetFingerDataFromMRTK(Microsoft.MixedReality.Toolkit.Input.IMixedRealityHand hand, 
                                           Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint tipJoint,
                                           Microsoft.MixedReality.Toolkit.Utilities.TrackedHandJoint baseJoint)
    {
        // 获取手指位置和方向
        Vector3 tipPosition = hand.GetJointPose(tipJoint).Position;
        Vector3 basePosition = hand.GetJointPose(baseJoint).Position;
        Vector3 direction = (tipPosition - basePosition).normalized;
        
        // 估算弯曲度
        // MRTK中可以使用手部关节之间的角度估算
        float flexion = Vector3.Dot(direction, hand.GetJointPose(baseJoint).Rotation * Vector3.forward);
        flexion = Mathf.Clamp01(1.0f - flexion);
        
        return new FingerData(tipPosition, direction, flexion);
    }
    #endif
    
    private void UpdateHandTrajectory(ref HandData handData)
    {
        // 添加当前位置和时间戳
        handData.recentPositions.Add(handData.palmPosition);
        handData.recentTimestamps.Add(Time.time);
        
        // 保持队列大小
        const int maxTrajectoryLength = 20;
        if (handData.recentPositions.Count > maxTrajectoryLength)
        {
            handData.recentPositions.RemoveAt(0);
            handData.recentTimestamps.RemoveAt(0);
        }
    }
    
    private void UpdateHandVisualization(bool handValid, HandData handData, GameObject visualizer)
    {
        if (visualizer == null)
            return;
            
        visualizer.SetActive(handValid);
        
        if (handValid)
        {
            // 更新手部可视化物体的位置和旋转
            visualizer.transform.position = handData.palmPosition;
            
            // 根据手掌法线和手指方向计算旋转
            Vector3 forward = handData.index.direction;
            Vector3 up = handData.palmNormal;
            Vector3 right = Vector3.Cross(up, forward).normalized;
            forward = Vector3.Cross(right, up).normalized;
            
            visualizer.transform.rotation = Quaternion.LookRotation(forward, up);
            
            // 更新手指可视化
            // 这部分需要基于具体可视化模型的结构实现
            // 通常需要获取各个手指的骨骼Transform并设置其旋转
        }
    }
    
    private void DetectGestures(HandData handData, bool isLeft)
    {
        // 对各种手势进行检测
        float pinchConfidence = DetectPinchGesture(handData);
        float grabConfidence = DetectGrabGesture(handData);
        float pointConfidence = DetectPointGesture(handData);
        float thumbsUpConfidence = DetectThumbsUpGesture(handData);
        float victoryConfidence = DetectVictoryGesture(handData);
        
        // 检测动态手势
        float waveConfidence = DetectWaveGesture(handData);
        float swipeConfidence = DetectSwipeGesture(handData);
        
        // 自定义手势匹配
        float customConfidence = DetectCustomGestures(handData);
        
        // 更新手势置信度
        UpdateGestureConfidence(GestureType.Pinch, pinchConfidence, handData.palmPosition, isLeft);
        UpdateGestureConfidence(GestureType.Grab, grabConfidence, handData.palmPosition, isLeft);
        UpdateGestureConfidence(GestureType.Point, pointConfidence, handData.palmPosition, isLeft);
        UpdateGestureConfidence(GestureType.ThumbsUp, thumbsUpConfidence, handData.palmPosition, isLeft);
        UpdateGestureConfidence(GestureType.Victory, victoryConfidence, handData.palmPosition, isLeft);
        UpdateGestureConfidence(GestureType.Wave, waveConfidence, handData.palmPosition, isLeft);
        UpdateGestureConfidence(GestureType.Swipe, swipeConfidence, handData.palmPosition, isLeft);
        UpdateGestureConfidence(GestureType.Custom, customConfidence, handData.palmPosition, isLeft);
        
        // 调试信息
        if (showDebugInfo)
        {
            string handName = isLeft ? "左手" : "右手";
            Debug.Log($"{handName} 手势置信度: 捏合={pinchConfidence:F2}, 抓取={grabConfidence:F2}, " +
                    $"指向={pointConfidence:F2}, 点赞={thumbsUpConfidence:F2}, 胜利={victoryConfidence:F2}, " +
                    $"挥手={waveConfidence:F2}, 滑动={swipeConfidence:F2}");
        }
    }
    
    private float DetectPinchGesture(HandData handData)
    {
        // 检测拇指和食指捏合
        float thumbIndexDistance = Vector3.Distance(handData.thumb.position, handData.index.position);
        
        // 判断拇指和食指是否足够接近
        float maxPinchDistance = 0.03f; // 3厘米
        float distanceRatio = Mathf.Clamp01(1.0f - (thumbIndexDistance / maxPinchDistance));
        
        // 结合弯曲度判断
        float pinchConfidence = distanceRatio * 
                              Mathf.Clamp01(handData.thumb.flexion) * 
                              Mathf.Clamp01(handData.index.flexion);
        
        // 其他手指应该是正常弯曲的
        float otherFingersFactor = (handData.middle.flexion + handData.ring.flexion + handData.pinky.flexion) / 3.0f;
        
        return pinchConfidence * pinchSensitivity * otherFingersFactor;
    }
    
    private float DetectGrabGesture(HandData handData)
    {
        // 检测握拳/抓取动作
        // 所有手指都应该弯曲
        float allFingersFactor = (handData.thumb.flexion + 
                                handData.index.flexion + 
                                handData.middle.flexion + 
                                handData.ring.flexion + 
                                handData.pinky.flexion) / 5.0f;
        
        // 判断手指是否都朝向手掌
        float fingerDirectionFactor = 0;
        fingerDirectionFactor += Vector3.Dot(handData.index.direction, handData.palmNormal);
        fingerDirectionFactor += Vector3.Dot(handData.middle.direction, handData.palmNormal);
        fingerDirectionFactor += Vector3.Dot(handData.ring.direction, handData.palmNormal);
        fingerDirectionFactor += Vector3.Dot(handData.pinky.direction, handData.palmNormal);
        fingerDirectionFactor = Mathf.Clamp01(fingerDirectionFactor / 4.0f);
        
        return allFingersFactor * fingerDirectionFactor * grabSensitivity;
    }
    
    private float DetectPointGesture(HandData handData)
    {
        // 检测指向手势
        // 食指应伸直,其他手指应弯曲
        float indexStraightness = 1.0f - handData.index.flexion;
        float otherFingersBent = (handData.middle.flexion + 
                                 handData.ring.flexion + 
                                 handData.pinky.flexion) / 3.0f;
        
        // 拇指可能伸直或弯曲,因此不纳入考虑
        
        return indexStraightness * otherFingersBent;
    }
    
    private float DetectThumbsUpGesture(HandData handData)
    {
        // 检测大拇指朝上手势
        // 拇指应伸直并指向上方,其他手指应弯曲
        float thumbStraightness = 1.0f - handData.thumb.flexion;
        float thumbUpFactor = Vector3.Dot(handData.thumb.direction, Vector3.up);
        thumbUpFactor = Mathf.Clamp01(thumbUpFactor);
        
        float otherFingersBent = (handData.index.flexion + 
                                 handData.middle.flexion + 
                                 handData.ring.flexion + 
                                 handData.pinky.flexion) / 4.0f;
        
        return thumbStraightness * thumbUpFactor * otherFingersBent;
    }
    
    private float DetectVictoryGesture(HandData handData)
    {
        // 检测胜利/V字手势
        // 食指和中指应伸直,其他手指应弯曲
        float indexStraightness = 1.0f - handData.index.flexion;
        float middleStraightness = 1.0f - handData.middle.flexion;
        float otherFingersBent = (handData.thumb.flexion + 
                                 handData.ring.flexion + 
                                 handData.pinky.flexion) / 3.0f;
        
        // 检查食指和中指是否分开
        float separation = Vector3.Distance(handData.index.position, handData.middle.position);
        float expectedSeparation = 0.03f; // 期望的分离距离
        float separationFactor = Mathf.Clamp01(separation / expectedSeparation);
        
        return indexStraightness * middleStraightness * otherFingersBent * separationFactor;
    }
    
    private float DetectWaveGesture(HandData handData)
    {
        // 检测挥手手势
        if (handData.recentPositions.Count < 5)
            return 0;
            
        // 分析手部轨迹的上下运动
        float verticalMotion = 0;
        Vector3 prevPos = handData.recentPositions[0];
        
        for (int i = 1; i < handData.recentPositions.Count; i++)
        {
            Vector3 currentPos = handData.recentPositions[i];
            float verticalDelta = Mathf.Abs(currentPos.y - prevPos.y);
            verticalMotion += verticalDelta;
            prevPos = currentPos;
        }
        
        // 检查是否有足够的垂直运动
        float threshold = 0.1f; // 10厘米的累积垂直运动
        float waveConfidence = Mathf.Clamp01(verticalMotion / threshold);
        
        // 判断手指是否伸直
        float fingersExtended = (1.0f - handData.index.flexion + 
                               1.0f - handData.middle.flexion + 
                               1.0f - handData.ring.flexion + 
                               1.0f - handData.pinky.flexion) / 4.0f;
        
        return waveConfidence * fingersExtended;
    }
    
    private float DetectSwipeGesture(HandData handData)
    {
        // 检测滑动手势
        if (handData.recentPositions.Count < 3 || handData.recentTimestamps.Count < 3)
            return 0;
            
        // 计算最近的位移
        Vector3 startPos = handData.recentPositions[0];
        Vector3 endPos = handData.recentPositions[handData.recentPositions.Count - 1];
        float displacement = Vector3.Distance(startPos, endPos);
        
        // 计算时间跨度
        float startTime = handData.recentTimestamps[0];
        float endTime = handData.recentTimestamps[handData.recentTimestamps.Count - 1];
        float duration = endTime - startTime;
        
        // 检查是否有足够的位移和足够短的时间
        if (displacement > swipeDistanceThreshold && duration < swipeTimeThreshold)
        {
            // 计算滑动方向的一致性
            Vector3 overallDirection = (endPos - startPos).normalized;
            float directionConsistency = 0;
            
            for (int i = 1; i < handData.recentPositions.Count; i++)
            {
                Vector3 segmentDirection = (handData.recentPositions[i] - handData.recentPositions[i-1]).normalized;
                directionConsistency += Vector3.Dot(segmentDirection, overallDirection);
            }
            
            directionConsistency /= (handData.recentPositions.Count - 1);
            
            // 计算最终置信度
            float swipeConfidence = Mathf.Clamp01(displacement / swipeDistanceThreshold) * 
                                  Mathf.Clamp01(directionConsistency) * 
                                  Mathf.Clamp01(1.0f - (duration / swipeTimeThreshold));
            
            return swipeConfidence;
        }
        
        return 0;
    }
    
    private float DetectCustomGestures(HandData handData)
    {
        if (customGestures.Count == 0)
            return 0;
            
        // 提取当前手势轨迹
        if (handData.recentPositions.Count < 5)
            return 0;
            
        // 对轨迹进行归一化
        List<Vector3> normalizedTrajectory = NormalizeTrajectory(handData.recentPositions);
        
        // 与所有自定义手势进行比较
        float bestMatchScore = 0;
        
        foreach (CustomGesture gesture in customGestures)
        {
            float similarity = CalculateTrajectorySimularity(normalizedTrajectory, gesture.trajectory);
            gesture.similarity = similarity;
            
            if (similarity > bestMatchScore)
            {
                bestMatchScore = similarity;
            }
        }
        
        return bestMatchScore;
    }
    
    private List<Vector3> NormalizeTrajectory(List<Vector3> trajectory)
    {
        // 归一化轨迹,使其与参考坐标系无关
        if (trajectory.Count < 2)
            return trajectory;
            
        // 找出轨迹的中心
        Vector3 center = Vector3.zero;
        foreach (Vector3 point in trajectory)
        {
            center += point;
        }
        center /= trajectory.Count;
        
        // 找出轨迹的最大范围
        float maxDistance = 0;
        foreach (Vector3 point in trajectory)
        {
            float distance = Vector3.Distance(point, center);
            if (distance > maxDistance)
            {
                maxDistance = distance;
            }
        }
        
        if (maxDistance < 0.001f)
            return trajectory;
            
        // 归一化后的轨迹
        List<Vector3> normalized = new List<Vector3>();
        foreach (Vector3 point in trajectory)
        {
            normalized.Add((point - center) / maxDistance);
        }
        
        return normalized;
    }
    
    private float CalculateTrajectorySimularity(List<Vector3> trajectory1, List<Vector3> trajectory2)
    {
        // 这里使用动态时间规整(DTW)算法比较两个轨迹的相似度
        // 简化版本,实际实现应更复杂
        
        // 重新采样轨迹到相同长度
        int sampleCount = 10;
        List<Vector3> sampledTraj1 = ResampleTrajectory(trajectory1, sampleCount);
        List<Vector3> sampledTraj2 = ResampleTrajectory(trajectory2, sampleCount);
        
        // 计算各点之间的距离
        float totalDistance = 0;
        for (int i = 0; i < sampleCount; i++)
        {
            totalDistance += Vector3.Distance(sampledTraj1[i], sampledTraj2[i]);
        }
        
        // 计算平均距离并转换为相似度
        float avgDistance = totalDistance / sampleCount;
        float similarity = Mathf.Clamp01(1.0f - (avgDistance / 2.0f));
        
        return similarity;
    }
    
    private List<Vector3> ResampleTrajectory(List<Vector3> trajectory, int sampleCount)
    {
        // 将轨迹重新采样到指定数量的点
        List<Vector3> sampled = new List<Vector3>();
        
        if (trajectory.Count <= 1 || sampleCount <= 1)
        {
            // 处理边界情况
            sampled.Add(trajectory[0]);
            return sampled;
        }
        
        // 计算轨迹总长度
        float totalLength = 0;
        for (int i = 1; i < trajectory.Count; i++)
        {
            totalLength += Vector3.Distance(trajectory[i], trajectory[i-1]);
        }
        
        // 理想的采样间隔
        float idealSegmentLength = totalLength / (sampleCount - 1);
        
        // 第一个采样点始终是轨迹的开始
        sampled.Add(trajectory[0]);
        
        float distanceSinceLastSample = 0;
        for (int i = 1; i < trajectory.Count; i++)
        {
            float segmentLength = Vector3.Distance(trajectory[i], trajectory[i-1]);
            
            // 如果当前累积距离加上当前段长度超过了理想间隔
            if (distanceSinceLastSample + segmentLength >= idealSegmentLength)
            {
                // 计算插值点
                float remainingDistance = idealSegmentLength - distanceSinceLastSample;
                float t = remainingDistance / segmentLength;
                
                Vector3 interpolatedPoint = Vector3.Lerp(trajectory[i-1], trajectory[i], t);
                sampled.Add(interpolatedPoint);
                
                // 继续在同一段内寻找更多采样点
                distanceSinceLastSample = 0;
                i--; // 保持在同一段
                
                // 如果我们已经有了足够的样本点
                if (sampled.Count >= sampleCount)
                    break;
            }
            else
            {
                // 累积距离
                distanceSinceLastSample += segmentLength;
            }
        }
        
        // 确保最后一点被采样
        if (sampled.Count < sampleCount)
        {
            sampled.Add(trajectory[trajectory.Count - 1]);
        }
        
        return sampled;
    }
    
    private void UpdateGestureConfidence(GestureType gesture, float confidence, Vector3 position, bool isLeft)
    {
        // 更新手势置信度并触发事件
        if (confidence >= confidenceThreshold)
        {
            float lastTime = gestureLastDetectedTime[gesture];
            
            // 如果是连续检测或者经过了足够的时间
            if (continuousDetection || Time.time - lastTime >= gestureTimeout)
            {
                gestureLastDetectedTime[gesture] = Time.time;
                gestureConfidence[gesture] = confidence;
                
                // 触发手势事件
                string handPrefix = isLeft ? "左手" : "右手";
                Debug.Log($"检测到{handPrefix} {gesture} 手势 (置信度: {confidence:F2})");
                
                onGestureDetected?.Invoke(gesture, position);
            }
        }
        else
        {
            // 渐变降低置信度
            gestureConfidence[gesture] = Mathf.Lerp(gestureConfidence[gesture], 0, Time.deltaTime * 5f);
        }
    }
    
    // 添加自定义手势
    public void AddCustomGesture(string name, List<Vector3> trajectory)
    {
        if (trajectory.Count < 5)
        {
            Debug.LogWarning("轨迹点太少,无法添加自定义手势");
            return;
        }
        
        CustomGesture gesture = new CustomGesture()
        {
            name = name,
            trajectory = NormalizeTrajectory(trajectory),
            similarity = 0
        };
        
        customGestures.Add(gesture);
        Debug.Log($"添加了自定义手势: {name}");
    }
    
    // 清除录制的轨迹
    public void ClearTrajectory(bool isLeft)
    {
        if (isLeft)
        {
            leftHandData.recentPositions.Clear();
            leftHandData.recentTimestamps.Clear();
        }
        else
        {
            rightHandData.recentPositions.Clear();
            rightHandData.recentTimestamps.Clear();
        }
    }
    
    private void OnDestroy()
    {
        // 清理可视化对象
        if (leftHandVisualizer != null)
            Destroy(leftHandVisualizer);
            
        if (rightHandVisualizer != null)
            Destroy(rightHandVisualizer);
    }
}

八、企业级XR应用开发经验

多平台许可和硬件识别系统

// XRLicenseManager.cs - XR许可证管理器
using UnityEngine;
using System;
using System.Collections;
using System.Text;
using System.Security.Cryptography;
using UnityEngine.Networking;

public class XRLicenseManager : MonoBehaviour
{
    [Header("许可配置")]
    [SerializeField] private string licenseKey;
    [SerializeField] private bool validateOnStart = true;
    [SerializeField] private bool enforceHardwareValidation = false;
    [SerializeField] private string licenseServerURL = "https://yourserver.com/validate";
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小宝哥Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值