XR-RokidAR-UXR3.0-InteractableUnityEventWrapper 脚本解析

cs 复制代码
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Events;

namespace Rokid.UXR.Interaction
{
    /// <summary>
    /// This component makes it possible to connect Interactables in the
    /// inspector to Unity Events that are broadcast on state changes.
    /// </summary>
    public class InteractableUnityEventWrapper : MonoBehaviour
    {
        [SerializeField, Interface(typeof(IInteractableView))]
        private MonoBehaviour _interactableView;
        private IInteractableView InteractableView;

        [SerializeField]
        private UnityEvent _whenHover;
        [SerializeField]
        private UnityEvent _whenUnhover;
        [SerializeField]
        private UnityEvent _whenSelect;
        [SerializeField]
        private UnityEvent _whenUnselect;
        [SerializeField]
        private UnityEvent _whenInteractorViewAdded;
        [SerializeField]
        private UnityEvent _whenInteractorViewRemoved;
        [SerializeField]
        private UnityEvent _whenSelectingInteractorViewAdded;
        [SerializeField]
        private UnityEvent _whenSelectingInteractorViewRemoved;

        #region Properties

        public UnityEvent WhenHover => _whenHover;
        public UnityEvent WhenUnhover => _whenUnhover;
        public UnityEvent WhenSelect => _whenSelect;
        public UnityEvent WhenUnselect => _whenUnselect;
        public UnityEvent WhenInteractorViewAdded => _whenInteractorViewAdded;
        public UnityEvent WhenInteractorViewRemoved => _whenInteractorViewRemoved;
        public UnityEvent WhenSelectingInteractorViewAdded => _whenSelectingInteractorViewAdded;
        public UnityEvent WhenSelectingInteractorViewRemoved => _whenSelectingInteractorViewRemoved;

        #endregion

        protected bool _started = false;

        protected virtual void Awake()
        {
            InteractableView = _interactableView as IInteractableView;
        }

        protected virtual void Start()
        {
            this.BeginStart(ref _started);
            Assert.IsNotNull(InteractableView);
            this.EndStart(ref _started);
        }

        protected virtual void OnEnable()
        {
            if (_started)
            {
                InteractableView.WhenStateChanged += HandleStateChanged;
                InteractableView.WhenInteractorViewAdded += HandleInteractorViewAdded;
                InteractableView.WhenInteractorViewRemoved += HandleInteractorViewRemoved;
                InteractableView.WhenSelectingInteractorViewAdded += HandleSelectingInteractorViewAdded;
                InteractableView.WhenSelectingInteractorViewRemoved += HandleSelectingInteractorViewRemoved;
            }
        }

        protected virtual void OnDisable()
        {
            if (_started)
            {
                InteractableView.WhenStateChanged -= HandleStateChanged;
                InteractableView.WhenStateChanged -= HandleStateChanged;
                InteractableView.WhenInteractorViewAdded -= HandleInteractorViewAdded;
                InteractableView.WhenInteractorViewRemoved -= HandleInteractorViewRemoved;
                InteractableView.WhenSelectingInteractorViewAdded -= HandleSelectingInteractorViewAdded;
                InteractableView.WhenSelectingInteractorViewRemoved -= HandleSelectingInteractorViewRemoved;
            }
        }

        private void HandleStateChanged(InteractableStateChangeArgs args)
        {
            switch (args.NewState)
            {
                case InteractableState.Normal:
                    if (args.PreviousState == InteractableState.Hover)
                    {
                        _whenUnhover.Invoke();
                    }

                    break;
                case InteractableState.Hover:
                    if (args.PreviousState == InteractableState.Normal)
                    {
                        _whenHover.Invoke();
                    }
                    else if (args.PreviousState == InteractableState.Select)
                    {
                        _whenUnselect.Invoke();
                    }
                    break;
                case InteractableState.Select:
                    if (args.PreviousState == InteractableState.Hover)
                    {
                        if (_whenSelect != null)
                            _whenSelect.Invoke();
                    }
                    break;
            }
        }

        private void HandleInteractorViewAdded(IInteractorView interactorView)
        {
            WhenInteractorViewAdded.Invoke();
        }

        private void HandleInteractorViewRemoved(IInteractorView interactorView)
        {
            WhenInteractorViewRemoved.Invoke();
        }

        private void HandleSelectingInteractorViewAdded(IInteractorView interactorView)
        {
            WhenSelectingInteractorViewAdded.Invoke();
        }

        private void HandleSelectingInteractorViewRemoved(IInteractorView interactorView)
        {
            WhenSelectingInteractorViewRemoved.Invoke();
        }
    }
}

InteractableUnityEventWrapper 脚本解析

这个脚本是一个交互事件桥接器,用于将 Rokid AR 交互系统中的事件转换为 Unity 事件系统,方便在编辑器中可视化配置交互反馈。下面我将详细解释这个脚本的功能和实现:

核心功能

  1. 事件桥接

    • 将底层交互系统的事件转换为 UnityEvent

    • 支持在 Unity 编辑器中可视化配置事件响应

  2. 状态转换处理

    • 处理交互状态变化(Normal/Hover/Select)

    • 正确触发状态转换事件

  3. 交互器追踪

    • 追踪与对象交互的交互器

    • 提供交互器加入/离开的事件通知

关键组件解析

1. 核心依赖

cs 复制代码
[SerializeField, Interface(typeof(IInteractableView))]
private MonoBehaviour _interactableView;
private IInteractableView InteractableView;
  • 功能:需要绑定的交互对象视图组件

  • 要求

    • 必须实现 IInteractableView 接口

    • 通常是 Interactable 或其子类组件

2. Unity 事件配置

cs 复制代码
[SerializeField]
private UnityEvent _whenHover;         // 悬停开始事件
[SerializeField]
private UnityEvent _whenUnhover;       // 悬停结束事件
[SerializeField]
private UnityEvent _whenSelect;        // 选择开始事件
[SerializeField]
private UnityEvent _whenUnselect;      // 选择结束事件
[SerializeField]
private UnityEvent _whenInteractorViewAdded;        // 交互器加入事件
[SerializeField]
private UnityEvent _whenInteractorViewRemoved;      // 交互器离开事件
[SerializeField]
private UnityEvent _whenSelectingInteractorViewAdded;   // 选择交互器加入事件
[SerializeField]
private UnityEvent _whenSelectingInteractorViewRemoved; // 选择交互器离开事件

事件处理逻辑

1. 状态变化处理

cs 复制代码
private void HandleStateChanged(InteractableStateChangeArgs args)
{
    switch (args.NewState)
    {
        case InteractableState.Normal:
            if (args.PreviousState == InteractableState.Hover)
            {
                _whenUnhover.Invoke(); // 悬停结束
            }
            break;
            
        case InteractableState.Hover:
            if (args.PreviousState == InteractableState.Normal)
            {
                _whenHover.Invoke(); // 悬停开始
            }
            else if (args.PreviousState == InteractableState.Select)
            {
                _whenUnselect.Invoke(); // 选择结束
            }
            break;
            
        case InteractableState.Select:
            if (args.PreviousState == InteractableState.Hover)
            {
                _whenSelect.Invoke(); // 选择开始
            }
            break;
    }
}

2. 交互器事件处理

cs 复制代码
// 任何交互器加入
private void HandleInteractorViewAdded(IInteractorView interactorView)
{
    WhenInteractorViewAdded.Invoke();
}

// 任何交互器离开
private void HandleInteractorViewRemoved(IInteractorView interactorView)
{
    WhenInteractorViewRemoved.Invoke();
}

// 选择交互器加入
private void HandleSelectingInteractorViewAdded(IInteractorView interactorView)
{
    WhenSelectingInteractorViewAdded.Invoke();
}

// 选择交互器离开
private void HandleSelectingInteractorViewRemoved(IInteractorView interactorView)
{
    WhenSelectingInteractorViewRemoved.Invoke();
}

使用场景示例

1. 简单悬停反馈

cs 复制代码
// 悬停时改变颜色
public class HoverColorChange : MonoBehaviour
{
    public Renderer targetRenderer;
    public Color hoverColor = Color.yellow;
    private Color originalColor;
    
    void Start()
    {
        originalColor = targetRenderer.material.color;
        
        var wrapper = gameObject.AddComponent<InteractableUnityEventWrapper>();
        wrapper.WhenHover.AddListener(() => targetRenderer.material.color = hoverColor);
        wrapper.WhenUnhover.AddListener(() => targetRenderer.material.color = originalColor);
    }
}

2. 交互声音反馈

cs 复制代码
// 添加交互音效
public class InteractionSFX : MonoBehaviour
{
    public AudioClip hoverSound;
    public AudioClip selectSound;
    public AudioClip releaseSound;
    
    void Start()
    {
        var wrapper = gameObject.AddComponent<InteractableUnityEventWrapper>();
        var audioSource = gameObject.AddComponent<AudioSource>();
        
        wrapper.WhenHover.AddListener(() => audioSource.PlayOneShot(hoverSound));
        wrapper.WhenSelect.AddListener(() => audioSource.PlayOneShot(selectSound));
        wrapper.WhenUnselect.AddListener(() => audioSource.PlayOneShot(releaseSound));
    }
}

3. 多交互器处理

cs 复制代码
// 显示当前交互器数量
public class InteractorCounter : MonoBehaviour
{
    public Text displayText;
    private int interactorCount = 0;
    
    void Start()
    {
        var wrapper = gameObject.AddComponent<InteractableUnityEventWrapper>();
        
        wrapper.WhenInteractorViewAdded.AddListener(() => {
            interactorCount++;
            UpdateDisplay();
        });
        
        wrapper.WhenInteractorViewRemoved.AddListener(() => {
            interactorCount--;
            UpdateDisplay();
        });
    }
    
    void UpdateDisplay()
    {
        displayText.text = $"交互器数量: {interactorCount}";
    }
}

完整使用流程

  1. 准备交互对象

    cs 复制代码
    // 创建可交互物体
    var interactableObject = new GameObject("Interactive Object");
    var interactable = interactableObject.AddComponent<MyInteractable>();
  2. 添加事件包装器

    cs 复制代码
    var eventWrapper = interactableObject.AddComponent<InteractableUnityEventWrapper>();
    eventWrapper._interactableView = interactable;
  3. 在编辑器中配置事件

    • 在 Inspector 中展开事件列表

    • 添加需要响应的事件方法

  4. 添加事件响应组件

    cs 复制代码
    // 添加悬停旋转效果
    interactableObject.AddComponent<HoverRotation>();

最佳实践建议

  1. 事件复用

    cs 复制代码
    // 创建共享事件响应器
    public class SharedFeedback : MonoBehaviour
    {
        public void PlayHoverEffect() { /* ... */ }
        public void PlaySelectEffect() { /* ... */ }
    }
    
    // 在多个对象上使用
    var feedback = FindObjectOfType<SharedFeedback>();
    wrapper1.WhenHover.AddListener(feedback.PlayHoverEffect);
    wrapper2.WhenHover.AddListener(feedback.PlayHoverEffect);
  2. 性能优化

    cs 复制代码
    // 避免在频繁事件中执行昂贵操作
    wrapper.WhenSelect.AddListener(() => {
        // 使用协程处理复杂效果
        StartCoroutine(ComplexEffectRoutine());
    });
  3. 事件清理

    cs 复制代码
    void OnDestroy()
    {
        // 移除所有监听器防止内存泄漏
        wrapper.WhenHover.RemoveAllListeners();
        wrapper.WhenSelect.RemoveAllListeners();
        // ...
    }
  4. 调试技巧

    cs 复制代码
    // 添加调试日志
    wrapper.WhenHover.AddListener(() => Debug.Log($"{name} 开始悬停"));
    wrapper.WhenSelect.AddListener(() => Debug.Log($"{name} 被选择"));

交互状态机图示

这个 InteractableUnityEventWrapper 组件是连接 Rokid AR 交互系统和 Unity 事件系统的关键桥梁,通过它可以轻松实现:

  • 视觉反馈(颜色变化、动画)

  • 声音反馈(音效播放)

  • 数据追踪(交互计数)

  • 系统联动(触发其他游戏逻辑)

相关推荐
Lauren_Lu1 天前
如何在 ArcGIS 中使用 Microsoft Excel 文件_20250614
microsoft·arcgis·excel
Elastic 中国社区官方博客1 天前
使用 Azure LLM Functions 与 Elasticsearch 构建更智能的查询体验
大数据·人工智能·elasticsearch·microsoft·搜索引擎·全文检索·azure
小刘同学要加油呀1 天前
SpringAI 函数工具调用
microsoft
wei_shuo1 天前
Azure 机器学习初学者指南
microsoft·机器学习·azure
weixin_531638941 天前
Rokid AR交互开发工具对比
unity·游戏引擎·xr
Clownseven2 天前
对象存储数据一致性:S3 vs Azure Blob vs GCS对比解析 (2025)
microsoft·flask·azure
国际云,接待2 天前
微软云注册被阻止怎么解决?
服务器·网络·microsoft·云原生·微软·云计算
咖啡の猫2 天前
JavaScript基础-DOM事件流
开发语言·javascript·microsoft
明月看潮生2 天前
青少年编程与数学 01-011 系统软件简介 13 Microsoft SQL Server数据库
数据库·microsoft·青少年编程·系统软件