VR开发实战HTC Vive项目之僵尸大战(语音唤醒与手势识别)

一、框架视图

二、主要代码

BaseControllerManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRTK;

public abstract class BaseControllerManager : MonoBehaviour
{
    private VRTK_ControllerEvents controllerEvents;

    public abstract void GripReleased();
    public abstract void GripPressed();
    public abstract void TouchpadReleased();
    public abstract void TouchpadPressed();
    public abstract void TriggerReleased();
    public abstract void TriggerPressed();

    private void Awake()
    {
        controllerEvents = GetComponent<VRTK_ControllerEvents>();
        controllerEvents.GripPressed += ControllerEvents_GripPressed;
        controllerEvents.GripReleased += ControllerEvents_GripReleased;

        controllerEvents.TriggerPressed += ControllerEvents_TriggerPressed;
        controllerEvents.TriggerReleased += ControllerEvents_TriggerReleased;

        controllerEvents.TouchpadPressed += ControllerEvents_TouchpadPressed;
        controllerEvents.TouchpadReleased += ControllerEvents_TouchpadReleased;
    }

    private void ControllerEvents_TouchpadReleased(object sender, ControllerInteractionEventArgs e)
    {
        TouchpadReleased();
    }

    private void ControllerEvents_TouchpadPressed(object sender, ControllerInteractionEventArgs e)
    {
        TouchpadPressed();
    }

    private void ControllerEvents_TriggerReleased(object sender, ControllerInteractionEventArgs e)
    {
        TriggerReleased();
    }

    private void ControllerEvents_TriggerPressed(object sender, ControllerInteractionEventArgs e)
    {
        TriggerPressed();
    }

    private void ControllerEvents_GripReleased(object sender, ControllerInteractionEventArgs e)
    {
        GripReleased();
    }

    private void ControllerEvents_GripPressed(object sender, ControllerInteractionEventArgs e)
    {
        GripPressed();
    }
}

Game:

BodyPartDrop

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 受伤后掉落
/// </summary>
public class BodyPartDrop : MonoBehaviour
{
    public GameObject go_Firend;

    public void SetFriend(GameObject go)
    {
        go_Firend = go;
    }
    /// <summary>
    /// 受伤后掉落的处理
    /// </summary>
    public void Hit()
    {
        BodyPartDrop[] arr = transform.parent.GetComponentsInChildren<BodyPartDrop>();

        foreach (var item in arr)  //中间掉落  下面也要跟着掉落
        {
            item.go_Firend.SetActive(false);
            item.transform.parent = null;
            item.transform.GetChild(0).gameObject.SetActive(true);
            item.gameObject.AddComponent<Rigidbody>();
            Destroy(item);
        }
    }
}

Bomb

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 爆炸
/// </summary>
public class Bomb : MonoBehaviour
{
    public bool IsThrow = false; //是都可以投掷
    public float BrustTime = 5f; //爆炸等待时间
    public GameObject effect_Brust; //爆炸特效

    private float m_Timer = 0.0f; //计时器

    private void FixedUpdate()
    {
        if (IsThrow) //判断是否可以爆炸  在手管理类的时候投掷炸弹设置为true  HandManger170 
        {
            m_Timer += Time.deltaTime;
            if (m_Timer >= BrustTime)
            {
                Instantiate(effect_Brust, transform.position, transform.rotation); //实例化特效
                Destroy(gameObject);
                EventCenter.Broadcast(EventDefine.BombBrust, transform.position);//广播发生爆炸特效的事件 当前爆炸位置的信息
            }
        }
    }
}

Book

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum BookType
{
    StartBook,
    AboutBook,
    GestureBook
}


/// <summary>
/// 书籍管理类
/// </summary>
public class Book : MonoBehaviour
{
    public BookType m_BookType;
    public Vector3 m_StratPos;
    public Quaternion m_StartRot;
    /// <summary>
    /// 判断书本是否触发到书台
    /// </summary>
    public bool m_IsTrigger = false;
    /// <summary>
    /// 触发的书台物体
    /// </summary>
    private GameObject go_StandBook;

    private void Awake()
    {
        m_StratPos = transform.position;
        m_StartRot = transform.rotation;
    }
    private void Update()
    {
        if (transform.parent != null && go_StandBook != null)
        {
            if (transform.parent != go_StandBook.transform)
            {
                IsActiveUI(false);
            }
        }
    }
    /// <summary>
    /// 放置书本
    /// </summary>
    public void Put()
    {
        if (go_StandBook.GetComponentInChildren<Book>() != null)
        {
            go_StandBook.GetComponentInChildren<Book>().Release();
        }
        transform.parent = go_StandBook.transform;
        transform.position = go_StandBook.transform.GetChild(0).position;
        transform.rotation = go_StandBook.transform.GetChild(0).rotation;
        IsActiveUI(true);
    }
    /// <summary>
    /// 书本归为
    /// </summary>
    public void Release()
    {
        transform.parent = null;
        transform.position = m_StratPos;
        transform.rotation = m_StartRot;
        IsActiveUI(false);
    }
    /// <summary>
    /// 是否激活当前书本对应的UI界面
    /// </summary>
    /// <param name="value"></param>
    private void IsActiveUI(bool value)
    {
        switch (m_BookType)
        {
            case BookType.StartBook:
                EventCenter.Broadcast(EventDefine.IsShowStartPanel, value);
                break;
            case BookType.AboutBook:
                EventCenter.Broadcast(EventDefine.IsShowAboutPanel, value);
                break;
            case BookType.GestureBook:
                EventCenter.Broadcast(EventDefine.IsShowGesturePanel, value);
                break;
        }
    }
    private void OnTriggerStay(Collider other)
    {
        if (other.tag == "BookStand")
        {
            m_IsTrigger = true;
            go_StandBook = other.gameObject;
        }
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "BookStand")
        {
            m_IsTrigger = false;
            go_StandBook = null;
        }
    }
}

GestureRecognition

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Edwon.VR;
using Edwon.VR.Gesture;


/// <summary>
/// 检测手势
/// </summary>
public class GestureRecognition : BaseGestureRecognition
{
    public override void Awake()
    {
        base.Awake();
        EventCenter.AddListener<bool>(EventDefine.IsStartGestureRecognition, IsStartGestureRecognition); //后面可以是方法或者直接赋值  布尔变量
    }
    public override void OnDestroy()
    {
        base.OnDestroy();
        EventCenter.RemoveListener<bool>(EventDefine.IsStartGestureRecognition, IsStartGestureRecognition);
    }
    /// <summary>
    /// 是否开始手势识别
    /// </summary>
    /// <param name="value"></param>
    private void IsStartGestureRecognition(bool value)
    {
        if (value)
        {
            BeginRecognition();
        }
        else
        {
            gestureRig.uiState = VRGestureUIState.Idle;
        }
    }

    public override void OnGestureDetectedEvent(string gestureName, double confidence) //检测手势事件
    {
        string skillName = GestureSkillManager.GetSkillNameByGestureName(gestureName); //获取技能的名称
        GameObject skill = ResourcesManager.LoadObj(skillName); //加载技能预制体
        Instantiate(skill, new Vector3(Camera.main.transform.position.x, 0, Camera.main.transform.position.z), skill.transform.rotation);  //实例化技能 注意生成的位置
    }
}

GestureSkillManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Edwon.VR;
using Edwon.VR.Gesture;

/// <summary>
/// 手势技能管理
/// </summary>
public class GestureSkillManager
{
    /// <summary>
    /// 手势名与技能名的字典
    /// </summary>
    private static Dictionary<string, string> m_GestureSkillDic = new Dictionary<string, string>();
    private static VRGestureSettings gestureSettings;
    private static string SkillName = "Skill";

    static GestureSkillManager()
    {
        gestureSettings = Utils.GetGestureSettings();
        m_GestureSkillDic = GetGestureSkillDic();
    }
    /// <summary>
    /// 获取手势名与技能名之间的关系
    /// </summary>
    /// <returns></returns>
    private static Dictionary<string, string> GetGestureSkillDic()
    {
        Dictionary<string, string> gestureSkillDic = new Dictionary<string, string>();
        //规则:手势名-技能名;手势名-技能名
        if (PlayerPrefs.HasKey("GestureSkill"))
        {
            string gestureSkill = PlayerPrefs.GetString("GestureSkill");
            string[] arr = gestureSkill.Split(';');
            foreach (var item in arr)
            {
                string[] tempArr = item.Split('-');
                gestureSkillDic.Add(tempArr[0], tempArr[1]);
            }
        }
        else
        {
            for (int i = 0; i < gestureSettings.gestureBank.Count; i++)
            {
                gestureSkillDic.Add(gestureSettings.gestureBank[i].name, SkillName + (i + 1).ToString());
            }
            SaveGestureSkillDic(gestureSkillDic);
        }
        return gestureSkillDic;
    }
    /// <summary>
    /// 保存手势与技能之间的关系
    /// </summary>
    private static void SaveGestureSkillDic(Dictionary<string, string> dic)
    {
        string temp = "";
        int index = 0;
        foreach (var item in dic)
        {
            //规则:手势名-技能名;手势名-技能名
            temp += item.Key + "-" + item.Value;
            index++;
            if (index != dic.Count)
                temp += ";";
        }
        PlayerPrefs.SetString("GestureSkill", temp);
    }
    /// <summary>
    /// 通过手势名获取技能名
    /// </summary>
    public static string GetSkillNameByGestureName(string gestureName)
    {
        if (m_GestureSkillDic.ContainsKey(gestureName))
        {
            return m_GestureSkillDic[gestureName];
        }
        return null;
    }
    /// <summary>
    /// 更换手势与技能之间的关系(更换技能)
    /// </summary>
    /// <param name="gestureName"></param>
    /// <param name="newSkillName"></param>
    public static void ChangeSkill(string gestureName, string newSkillName)
    {
        if (m_GestureSkillDic.ContainsKey(gestureName))
        {
            m_GestureSkillDic[gestureName] = newSkillName;
            SaveGestureSkillDic(m_GestureSkillDic);
        }
    }
    /// <summary>
    /// 通过手势名获取技能图片
    /// </summary>
    /// <param name="gestureName"></param>
    public static Sprite GetSkilSpriteByGestureName(string gestureName)
    {
        if (m_GestureSkillDic.ContainsKey(gestureName))
        {
            return ResourcesManager.LoadSprite(m_GestureSkillDic[gestureName]);
        }
        return null;
    }
}

HPManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;


/// <summary>
/// 血量管理类
/// </summary>
public class HPManager : MonoBehaviour
{
    public int HP = 10000;

    private void Awake()
    {
        EventCenter.AddListener<int>(EventDefine.UpdateHP, UpdateHP);
        EventCenter.AddListener<Vector3>(EventDefine.BombBrust, BombBrust);
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener<int>(EventDefine.UpdateHP, UpdateHP);
        EventCenter.RemoveListener<Vector3>(EventDefine.BombBrust, BombBrust);
    }
    /// <summary>
    /// 炸弹爆炸
    /// </summary>
    /// <param name="brustPos"></param>
    private void BombBrust(Vector3 brustPos)
    {
        if (Vector3.Distance(transform.position, brustPos) < 10.0f)
        {
            UpdateHP(-20);
        }
    }
    /// <summary>
    /// 更新血量
    /// </summary>
    /// <param name="count"></param>
    private void UpdateHP(int count)
    {
        if (count < 0)
        {
            if (HP <= Mathf.Abs(count))//血量小于0  挂掉
            {
                //死亡
                HP = 0;  
                Death(); //加载当前活跃的场景
            }
            else
            {
                HP += count;  //增加血量
            }
        }
        else
        {
            HP += count;
        }
        EventCenter.Broadcast(EventDefine.UpdateHpUI, HP);  //广播更新血量的事件码
    }
    private void Death()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);  //加载当前场景
    }
}

Magazine

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


/// <summary>
/// 弹夹管理类
/// </summary>
public class Magazine : MonoBehaviour
{
    /// <summary>
    /// 子弹数量
    /// </summary>
    private int BulletCount = 6;

    /// <summary>
    /// 设置子弹的方法
    /// </summary>
    /// <param name="count"></param>
    public void SetBulletCount(int count)
    {
        BulletCount = count;
    }
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Backet")
        {
            if (transform.parent != null && transform.parent.GetComponentInChildren<HandManger>() != null)
            {
                Destroy(gameObject);
                transform.parent.GetComponentInChildren<HandManger>().Catch(false);
                //加子弹
                AmmoManager.Instance.UpdateBullet(BulletCount);
            }
        }
    }
}

Pistol

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;


/// <summary>
/// 手枪
/// </summary>
public class Pistol : MonoBehaviour
{
    public EventDefine ShotEvent;//射击事件  这两个要在面板上指定是哪个事件  分别是左右手  
    public EventDefine ReloadMagazineEvent;//换弹夹  指定类型在面板上赋值  为了方便广播事件

    public Transform m_StartPos; //起始位置
    public GameObject go_Point;
    public GameObject effect_HitOtherMask; //黑洞
    public GameObject effect_HitOther;//烟雾特效
    public GameObject effect_Fire; //火的特效
    public GameObject effect_Blood; //血特效
    public GameObject go_Magazine;//弹夹
    public AudioClip audio_Shot; //射击声音片段

    private LineRenderer m_LineRenderer; //渲染
    private Animator m_Anim;//动画
    private RaycastHit m_Hit;//射线
    public int m_CurrentBulletCount = 6; //弹夹数量
    private AudioSource m_AudioSource; 

    private void Awake()
    {
        m_AudioSource = GetComponent<AudioSource>(); //获取组件
        m_LineRenderer = GetComponent<LineRenderer>();
        m_Anim = GetComponent<Animator>();
        EventCenter.AddListener(ShotEvent, Shot);//添加射击监听  广播事件是分别执行左右手
        EventCenter.AddListener(ReloadMagazineEvent, ReloadMagazine);//添加换弹夹事件  广播时候左右手按下圆盘时候
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(ShotEvent, Shot);//移除监听射击事件
        EventCenter.RemoveListener(ReloadMagazineEvent, ReloadMagazine); //移除换弹夹事件
    }
    /// <summary>
    /// 换弹夹
    /// </summary>
    private void ReloadMagazine()
    {
        //代表是Main场景 则忽略
        if (SceneManager.GetActiveScene().buildIndex == 0) return;
        //如果手枪是隐藏的,则忽略
        if (gameObject.activeSelf == false) return;
        //如果当前正在播放开火的动画,则忽略
        if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("Fire")) return;
        //如果当前正在播放换弹夹的动画,则忽略
        if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("Reload")) return;

        if (GameObject.FindObjectOfType<RadialMenuManager>() != null)
            if (GameObject.FindObjectOfType<RadialMenuManager>().transform.localScale != Vector3.zero)
                return;

        int temp = m_CurrentBulletCount;  //当前数量
        m_CurrentBulletCount = AmmoManager.Instance.ReloadMagazine(); //单例模式
        if (m_CurrentBulletCount != 0)
        {
            m_Anim.SetTrigger("Reload");
            GameObject go = Instantiate(go_Magazine, transform.Find("Magazine").position, transform.Find("Magazine").rotation);
            go.GetComponent<Magazine>().SetBulletCount(temp);
        }
    }
    /// <summary>
    /// 射击
    /// </summary>
    private void Shot()
    {
        //代表是Main场景 则忽略
        if (SceneManager.GetActiveScene().buildIndex == 0) return;
        //如果手枪是隐藏的,则忽略
        if (gameObject.activeSelf == false) return;
        //如果当前正在播放开火的动画,则忽略
        if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("Fire")) return;
        //如果当前正在播放换弹夹的动画,则忽略
        if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("Reload")) return;

        if (m_CurrentBulletCount <= 0) return;
        m_CurrentBulletCount--; //子弹数量减减
        //播放射击动画
        m_Anim.SetTrigger("Shot");

        if (m_AudioSource.isPlaying == false)
        {
            m_AudioSource.clip = audio_Shot;
            m_AudioSource.Play();
        }
        Destroy(Instantiate(effect_Fire, m_StartPos.position, m_StartPos.rotation), 1.5f);

        if (m_Hit.collider != null)
        {
            //是否是僵尸
            if (m_Hit.collider.tag == "Zombie")
            {
                if (m_Hit.transform.GetComponent<BodyPartDrop>() != null)
                {
                    m_Hit.transform.GetComponent<BodyPartDrop>().Hit();
                }
                if (m_Hit.transform.GetComponent<ZombieHit>() != null)
                {
                    m_Hit.transform.GetComponent<ZombieHit>().Hit();
                }
                //实例化血的特效,1.5秒之后销毁
                Destroy(Instantiate(effect_Blood, m_Hit.point, Quaternion.LookRotation(m_Hit.normal)), 2f);  //旋转看向受伤的地方
            }
            else
            {
                GameObject mask = Instantiate(effect_HitOtherMask, m_Hit.point, Quaternion.LookRotation(m_Hit.normal));//黑洞遮罩
                mask.transform.parent = m_Hit.transform;
                Destroy(Instantiate(effect_HitOther, m_Hit.point, Quaternion.LookRotation(m_Hit.normal)), 2);//实例化烟雾 2秒后消失
            }
        }
    }
    /// <summary>
    /// 画线
    /// </summary>
    private void DrawLine(Vector3 startPos, Vector3 endPos, Color color)
    {
        m_LineRenderer.positionCount = 2;
        m_LineRenderer.SetPosition(0, startPos);
        m_LineRenderer.SetPosition(1, endPos);
        m_LineRenderer.startWidth = 0.001f;
        m_LineRenderer.endWidth = 0.001f;
        m_LineRenderer.material.color = color;
    }
    private void FixedUpdate()
    {
        if (Physics.Raycast(m_StartPos.position, m_StartPos.forward, out m_Hit, 100000, 1 << 0 | 1 << 2))  //检测第一层 第二层 如果1是0 的话表示是不检测
        {
            DrawLine(m_StartPos.position, m_Hit.point, Color.green);
            go_Point.SetActive(true);
            go_Point.transform.position = m_Hit.point;
        }
        else
        {
            DrawLine(m_StartPos.position, m_StartPos.forward * 100000, Color.red);
            go_Point.SetActive(false);
        }
    }
}

ZombieController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;



/// <summary>
/// 控制器
/// </summary>
public class ZombieController : MonoBehaviour
{
    public float m_WalkSpeed = 0.8f;//走路速度
    public float m_RunSpeed = 2;//跑步速度
    public float m_DistanceJudge = 1f;  //距离判断
    public float m_HitDealyTime = 2f;  //受伤延迟

    /// <summary>
    /// 攻击时间间隔
    /// </summary>
    public float m_AttackInterval = 3f;
    public AudioClip audio_Attack;
    public AudioClip audio_Walk;

    private NavMeshAgent m_Agent; //导航网格
    private Animator m_Anim;
    private Transform m_Target; //相机位置
    /// <summary>
    /// 是否第一次攻击
    /// </summary>
    private bool m_IsFirstAttack = true;
    private float m_Timer = 0.0f;
    /// <summary>
    /// 是否正在攻击
    /// </summary>
    private bool m_IsAttacking = false;
    /// <summary>
    /// 是否正在受伤中
    /// </summary>
    private bool m_IsHitting = false;
    /// <summary>
    /// 僵尸是否死亡
    /// </summary>
    private bool m_IsDeath = false;
    public bool IsDeath
    {
        get
        {
            return m_IsDeath;
        }
    }
    private AudioSource m_AudioSource;  //声效

    private void Awake()
    {
        m_AudioSource = GetComponent<AudioSource>();
        m_Anim = GetComponent<Animator>();
        m_Agent = GetComponent<NavMeshAgent>();
        m_Target = Camera.main.transform;  //相机信息
        EventCenter.AddListener<Vector3>(EventDefine.BombBrust, BombBrust); //监听炸弹爆炸的事件码
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener<Vector3>(EventDefine.BombBrust, BombBrust);//移除炸弹爆炸的事件码
    }
    private void Start()
    {
        RandomWalkOrRun();
    }
    private void FixedUpdate()
    {
        if (m_IsDeath) return;
        if (m_IsHitting) return;

        Vector3 tempTargetPos = new Vector3(m_Target.position.x, transform.position.y, m_Target.position.z); //获取物体的位置

        if (Vector3.Distance(transform.position, tempTargetPos) < m_DistanceJudge) //判断相机跟物体之间的距离
        {
            if (m_Agent.isStopped == false)
            {
                m_Agent.isStopped = true; //停止导航
            }

            if (m_IsAttacking == false)
            {
                m_Timer += Time.deltaTime;
                if (m_Timer >= m_AttackInterval)
                {
                    m_Timer = 0.0f;
                    m_IsAttacking = true; //调用攻击的函数
                    Attack();
                }
            }
            if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("AttackBlendTree") == false) //第0层的播放动画名称
            {
                if (m_IsFirstAttack)  //标志位 只加一次0.5f  之后不加 避免判断距离越来越大
                {
                    m_DistanceJudge += 0.5f; //避免根据距离动画来回播放
                    m_IsFirstAttack = false; 
                    m_IsAttacking = true;
                    Attack();
                }
                else
                {
                    m_IsAttacking = false;
                }
            }
        }
        else
        {  //没达到距离 继续寻路
            if (m_Agent.isStopped)
            {
                m_DistanceJudge -= 0.5f; //避免根据距离动画来回播放
                m_Agent.isStopped = false;  //继续寻路
                m_IsFirstAttack = true;
                RandomWalkOrRun();//随机走或跑
            }
            m_Agent.SetDestination(Camera.main.transform.position);  //设置新的目的地
        }
    }
    /// <summary>
    /// 炸弹爆炸
    /// </summary>
    /// <param name="brustPos"></param>
    private void BombBrust(Vector3 brustPos)
    {
        if (Vector3.Distance(transform.position, brustPos) < 10.0f)  //爆炸距离之内
        {
            BodyPartDrop[] drops = transform.GetComponentsInChildren<BodyPartDrop>();//查找子物体上的所有组件 数组
            foreach (var item in drops)
            {
                item.Hit();
            }
            Death();
        }
    }
    /// <summary>
    /// 死亡
    /// </summary>
    public void Death()
    {
        if (m_IsDeath) return; //如果挂掉就返回 不用重复执行
        PlayAnim(4, "Death", "DeathValue"); //4中随机死亡动画 
        m_Agent.isStopped = true; //停止寻路
        m_IsDeath = true;  
        Destroy(m_Agent); //销毁寻路组件  不销毁的话可能悬在半空中
        EventCenter.Broadcast(EventDefine.ZombieDeath);
    }
    /// <summary>
    /// 左边受伤
    /// </summary>
    public void HitLeft()
    {
        m_Anim.SetTrigger("HitLeft");
        m_Agent.isStopped = true;
        m_Anim.SetTrigger("Idle");
        m_IsHitting = true;
        StartCoroutine(HitDealy());  //受伤之后有一定的延迟
    }
    /// <summary>
    /// 右边受伤
    /// </summary>
    public void HitRight()
    {
        m_Anim.SetTrigger("HitRight");
        m_Agent.isStopped = true;
        m_Anim.SetTrigger("Idle");
        m_IsHitting = true;
        StartCoroutine(HitDealy());
    }
    /// <summary>
    /// 随机受伤动画
    /// </summary>
    public void Hit()
    {
        PlayAnim(3, "Hit", "HitValue"); //3种随机动画
        m_Agent.isStopped = true;
        m_Anim.SetTrigger("Idle");
        m_IsHitting = true;
        StartCoroutine(HitDealy());
    }
    IEnumerator HitDealy()
    {
        yield return new WaitForSeconds(m_HitDealyTime);
        m_Agent.isStopped = false;
        m_IsHitting = false;
        RandomWalkOrRun();
    }
    /// <summary>
    /// 攻击
    /// </summary>
    private void Attack()
    {
        if (m_AudioSource.isPlaying == false)
        {
            m_AudioSource.clip = audio_Attack;
            m_AudioSource.Play();
        }
        EventCenter.Broadcast(EventDefine.UpdateHP, -10);  //广播减少血量事件
        EventCenter.Broadcast(EventDefine.ScreenBlood);
        Vector3 targetPos = new Vector3(m_Target.position.x, transform.position.y, m_Target.position.z);
        transform.LookAt(targetPos);

        PlayAnim(6, "Attack", "AttackValue"); //随机攻击动画片段
    }
    /// <summary>
    /// 随机播放跑或者走的动画
    /// </summary>
    private void RandomWalkOrRun()
    {
        int ran = Random.Range(0, 2); //只随机0,1
        if (ran == 0)
        {
            //走
            WalkAnim();
            m_Agent.speed = m_WalkSpeed;
        }
        else
        {
            //跑
            RunAnim();
            m_Agent.speed = m_RunSpeed;
        }
    }
    /// <summary>
    /// 走路动画
    /// </summary>
    private void WalkAnim() 
    {
        if (m_AudioSource.isPlaying == false)
        {
            m_AudioSource.clip = audio_Walk;
            m_AudioSource.Play();
        }
        PlayAnim(3, "Walk", "WalkValue");
    }
    /// <summary>
    /// 跑的动画
    /// </summary>
    private void RunAnim()
    {
        PlayAnim(2, "Run", "RunValue");
    }

    /// <summary>
    /// 随机动画片段  走路3个  跑步2个 0.5f递增
    /// </summary>
    /// <param name="clipCount"></param>
    /// <param name="triggerName"></param>
    /// <param name="floatName"></param>
    private void PlayAnim(int clipCount, string triggerName, string floatName)
    {
        float rate = 1.0f / (clipCount - 1);  //等于0.5  或者 1
        m_Anim.SetTrigger(triggerName);  //设置动画触发器
        m_Anim.SetFloat(floatName, rate * Random.Range(0, clipCount));  //0.5*0、1、2 或 1*0、1
    }
}

HandManger:

HandManger

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HighlightingSystem;
using VRTK;

public enum GrabObjectType
{
    None,
    Other,
    Book,
    Pistol,
    Belt,
    Magazine,
    Bomb,
}
public enum HandAnimStateType
{
    None,
    Pistol,
}


/// <summary>
/// 左右手管理类
/// </summary>
public class HandManger : MonoBehaviour
{
    /// <summary>
    /// 监听抓取按键按下的事件码
    /// </summary>
    public EventDefine GrabEvent;  //定义不同类型 监听左右手柄不同的事件
    public EventDefine ShotEvent;
    public EventDefine UseBombEvent;
    public EventDefine UsePistolEvent;

    public GameObject go_Bomb;  //炸弹
    public float m_ThrowMulitiple = 1f;

    private Animator m_Anim; //动画
    /// <summary>
    /// 是否可以抓取
    /// </summary>
    private bool m_IsCanGrab = false;
    /// <summary>
    /// 当前抓取的物体
    /// </summary>
    public GameObject m_GrabObj = null;
    public GrabObjectType m_GrabObjectType = GrabObjectType.None;

    public StateModel[] m_StateModels;

    [System.Serializable]  //序列化
    public class StateModel
    {
        public HandAnimStateType StateType;
        public GameObject go;
    }

    /// <summary>
    /// 判断手是否触碰到可以抓取的物体
    /// </summary>
    private bool m_IsTrigger = false;
    /// <summary>
    /// 是否使用手枪
    /// </summary>
    private bool m_IsUsePistol = false;
    /// <summary>
    /// 是否使用炸弹
    /// </summary>
    private bool m_IsUseBomb = false;
    private VRTK_ControllerEvents controllerEvents;

    private void Awake()
    {
        m_Anim = GetComponent<Animator>();
        controllerEvents = GetComponentInParent<VRTK_ControllerEvents>();
        EventCenter.AddListener<bool>(GrabEvent, IsCanGrab);
        EventCenter.AddListener(ShotEvent, Shot);
        EventCenter.AddListener(UseBombEvent, UseBomb);
        EventCenter.AddListener(UsePistolEvent, UsePistol);
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener<bool>(GrabEvent, IsCanGrab);
        EventCenter.RemoveListener(ShotEvent, Shot);
        EventCenter.RemoveListener(UseBombEvent, UseBomb);
        EventCenter.RemoveListener(UsePistolEvent, UsePistol);
    }
    /// <summary>
    /// 射击
    /// </summary>
    private void Shot()
    {
        if (m_Anim.GetInteger("State") != (int)HandAnimStateType.Pistol) return;

        m_Anim.SetTrigger("Shot");
    }
    /// <summary>
    /// 是否可以抓取物体的监听方法
    /// </summary>
    /// <param name="value"></param>
    private void IsCanGrab(bool value)
    {
        if (value == false)
        {
            if (m_GrabObj != null && m_GrabObjectType == GrabObjectType.Bomb)
            {
                //代表拿的是炸弹
                ThrowBomb();
            }
        }
        //释放抓取的物体
        if (value)
        {
            if (m_GrabObj != null)
            {
                if (m_GrabObjectType == GrabObjectType.Other)
                {
                    m_GrabObj.transform.parent = null;
                    m_GrabObj = null;
                    m_GrabObjectType = GrabObjectType.None;
                }
                else if (m_GrabObjectType == GrabObjectType.Book)
                {
                    if (m_GrabObj.GetComponent<Book>().m_IsTrigger)
                    {
                        m_GrabObj.GetComponent<Book>().Put();
                    }
                    else
                    {
                        m_GrabObj.transform.parent = null;
                        m_GrabObj.transform.position = m_GrabObj.GetComponent<Book>().m_StratPos;
                        m_GrabObj.transform.rotation = m_GrabObj.GetComponent<Book>().m_StartRot;
                    }

                    m_GrabObj = null;
                    m_GrabObjectType = GrabObjectType.None;
                }
                else if (m_GrabObjectType == GrabObjectType.Belt || m_GrabObjectType == GrabObjectType.Magazine)
                {
                    m_GrabObj.transform.parent = null;
                    m_GrabObj.GetComponent<Rigidbody>().useGravity = true;
                    m_GrabObj.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
                    m_GrabObj = null;
                    m_GrabObjectType = GrabObjectType.None;
                }
                return;
            }
        }
        if (m_GrabObj == null)
            m_Anim.SetBool("Catch", value);
        m_IsCanGrab = value;

        PistolOrBombChangeHand();
    }
    /// <summary>
    /// 投掷炸弹
    /// </summary>
    private void ThrowBomb()
    {
        //更新炸弹数量
        AmmoManager.Instance.UpdateBomb();

        m_GrabObj.transform.parent = null; //设置父物体为空
        m_GrabObj.AddComponent<Rigidbody>();//添加刚体
        m_Anim.SetBool("Catch", false);//播放动画
        m_GrabObjectType = GrabObjectType.None; //抓取物体类型为空

        Vector3 velocity = controllerEvents.GetVelocity(); //获取手柄速度
        Vector3 angularVelocity = controllerEvents.GetAngularVelocity();//获取手柄角速度

        m_GrabObj.GetComponent<Rigidbody>().velocity = transform.parent.parent.TransformDirection(velocity) * m_ThrowMulitiple;//速度
        m_GrabObj.GetComponent<Rigidbody>().angularVelocity = transform.parent.parent.TransformDirection(angularVelocity); //角速度
        m_GrabObj.GetComponent<Bomb>().IsThrow = true;//可以扔
        m_GrabObj = null;
        m_IsUseBomb = false;

        UsePistol();//投掷玩之后切换成手枪
    }
    /// <summary>
    /// 手枪换手 炸弹换手
    /// </summary>
    private void PistolOrBombChangeHand()
    {
        //1.满足当前手没有抓取任何物体
        //2.当前手没有触碰到任何可以抓取的物体
        //3.另外一只手要保证拿着枪
        if (m_GrabObj == null && m_IsTrigger == false && m_IsCanGrab == false)
        {
            HandManger[] handMangers = GameObject.FindObjectsOfType<HandManger>();
            foreach (var handManger in handMangers)
            {
                if (handManger != this)
                {
                    //手枪换手
                    if (handManger.m_IsUsePistol)
                    {
                        UsePistol();
                        handManger.UnUsePistol();
                        m_StateModels[0].go.GetComponent<Pistol>().m_CurrentBulletCount =
                            handManger.m_StateModels[0].go.GetComponent<Pistol>().m_CurrentBulletCount;  //手枪换手 子弹同步
                    }
                    //炸弹换手
                    if (handManger.m_IsUseBomb)
                    {
                        handManger.UnUseBomb();
                        UseBomb();
                    }
                }
            }
        }
    }
    /// <summary>
    /// 抓取
    /// 作用:一只手拿另外一种手的物体的一些逻辑处理
    /// </summary>
    /// <param name="value"></param>
    public void Catch(bool value)
    {
        if (m_GrabObj != null)
        {
            m_GrabObj = null;
            m_GrabObjectType = GrabObjectType.None;
        }
        m_Anim.SetBool("Catch", value);
    }
    /// <summary>
    /// 使用炸弹
    /// </summary>
    private void UseBomb()
    {
        if (AmmoManager.Instance.IsHasBomb() == false) return;

        //判断当前右手是否拿着物品,如果拿着则卸掉
        if (m_GrabObj != null)
        {
            if (m_GrabObjectType == GrabObjectType.Pistol)
            {
                UnUsePistol();
            }
            else if (m_GrabObjectType == GrabObjectType.Belt || m_GrabObjectType == GrabObjectType.Magazine)
            {
                m_GrabObj.transform.parent = null;
                m_GrabObj.GetComponent<Rigidbody>().useGravity = true;
                m_GrabObj.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None; //解除
                m_GrabObj = null;
                m_GrabObjectType = GrabObjectType.None;
            }
            else if (m_GrabObjectType == GrabObjectType.Bomb)  //拿到炸弹就返回
            {
                return;
            }
        }
        Transform target = transform.parent.Find("BombTarget");  //重置炸弹位置
        GameObject bomb = Instantiate(go_Bomb, transform.parent);
        bomb.transform.localPosition = target.localPosition;
        bomb.transform.localRotation = target.localRotation;
        bomb.transform.localScale = target.localScale;

        m_GrabObj = bomb;
        m_GrabObjectType = GrabObjectType.Bomb;
        m_Anim.SetBool("Catch", true);
        m_IsUseBomb = true;
    }
    /// <summary>
    /// 卸载炸弹
    /// </summary>
    public void UnUseBomb()
    {
        m_IsUseBomb = false;
        Destroy(m_GrabObj);
        m_GrabObj = null;
        m_GrabObjectType = GrabObjectType.None;
        m_Anim.SetBool("Catch", false);
    }
    /// <summary>
    /// 使用手枪
    /// </summary>
    private void UsePistol()
    {
        if (m_GrabObj != null)
        {
            if (m_GrabObjectType == GrabObjectType.Belt || m_GrabObjectType == GrabObjectType.Magazine)
            {
                m_GrabObj.transform.parent = null;
                m_GrabObj.GetComponent<Rigidbody>().useGravity = true;
                m_GrabObj.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
                m_GrabObj = null;
                m_GrabObjectType = GrabObjectType.None;
            }
            else if (m_GrabObjectType == GrabObjectType.Bomb)
            {
                UnUseBomb();
            }
            else if (m_GrabObjectType == GrabObjectType.Pistol)
            {
                return;
            }
        }
        m_IsUsePistol = true;
        m_Anim.SetBool("Catch", false);
        m_GrabObjectType = GrabObjectType.Pistol;
        m_GrabObj = m_StateModels[0].go;
        //切换成拿枪的动画
        //显示手枪
        TurnState(HandAnimStateType.Pistol);
    }
    /// <summary>
    /// 卸下手枪
    /// </summary>
    public void UnUsePistol()
    {
        m_IsUsePistol = false;
        m_Anim.SetBool("Catch", false);
        m_GrabObjectType = GrabObjectType.None;
        m_GrabObj = null;
        TurnState(HandAnimStateType.None);
    }
    private void TurnState(HandAnimStateType stateType)
    {
        m_Anim.SetInteger("State", (int)stateType);
        foreach (var item in m_StateModels)
        {
            if (item.StateType == stateType && item.go.activeSelf == false)
            {
                item.go.SetActive(true);
            }
            else if (item.go.activeSelf)
            {
                item.go.SetActive(false);
            }
        }
    }
    private void OnTriggerStay(Collider other)
    {
        if (other.tag == "Others" || other.tag == "Book" || other.tag == "Pistol" || other.tag == "Belt" || other.tag == "Magazine")
        {
            m_IsTrigger = true;
        }
        if (other.GetComponent<Highlighter>() != null)  //开启高亮  红色  注意引入命名空间
        {
            other.GetComponent<Highlighter>().On(Color.red); 
        }
        if (other.tag == "Others" && m_IsCanGrab && m_GrabObj == null)
        {
            ProcessGrab(other);
            m_GrabObjectType = GrabObjectType.Other; //抓取物体的类型
        }
        else if (other.tag == "Book" && m_IsCanGrab && m_GrabObj == null)
        {
            ProcessGrab(other);
            m_GrabObjectType = GrabObjectType.Book; //抓取书
        }
        else if (other.tag == "Pistol" && m_IsCanGrab && m_GrabObj == null)
        {
            EventCenter.Broadcast(EventDefine.WearPistol);
            Destroy(other.gameObject);
            UsePistol();
        }
        else if (other.tag == "Belt" && m_IsCanGrab && m_GrabObj == null)
        {
            ProcessGrab(other);
            other.GetComponent<Rigidbody>().useGravity = false;
            other.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            m_GrabObjectType = GrabObjectType.Belt;
        }
        else if (other.tag == "Magazine" && m_IsCanGrab && m_GrabObj == null)
        {
            ProcessGrab(other);
            other.GetComponent<Rigidbody>().useGravity = false;
            other.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            m_GrabObjectType = GrabObjectType.Magazine;
        }
    }
    /// <summary>
    /// 处理抓取
    /// </summary>
    /// <param name="other"></param>
    private void ProcessGrab(Collider other)
    {
        //一只手拿另外一种手的物体的一些逻辑处理
        if (other.transform.parent != null)
        {
            if (other.transform.parent.tag == "ControllerRight" || other.transform.parent.tag == "ControllerLeft")
            {
                other.transform.parent.GetComponentInChildren<HandManger>().Catch(false);
            }
        }
        Catch(true);
        other.gameObject.transform.parent = transform.parent;
        m_GrabObj = other.gameObject;
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.GetComponent<Highlighter>() != null)
        {
            other.GetComponent<Highlighter>().Off();  //关闭高亮
        }
        m_IsTrigger = false;
    }
}

UI:

AmmoManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 弹药库的管理
/// </summary>
public class AmmoManager : MonoBehaviour
{
    public static AmmoManager Instance;  //单例模式
    /// <summary>
    /// 子弹数量
    /// </summary>
    public int BulletCount;
    /// <summary>
    /// 炸弹数量
    /// </summary>
    public int BombCount;

    private Transform target; //目标物体
    private Text txt_Bullet;//子弹数量
    private Text txt_Bomb;//炸弹数量

    private void Awake()
    {
        Instance = this; //单例赋值
    }
    private void Start()
    {
        txt_Bullet = transform.Find("Bullet/Text").GetComponent<Text>();
        txt_Bomb = transform.Find("Bomb/Text").GetComponent<Text>();
        target = GameObject.FindGameObjectWithTag("CameraRig").transform; //查找目标物体
        EventCenter.AddListener(EventDefine.WearBelt, Show); //监听穿戴事件
        gameObject.SetActive(false); //默认隐藏
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(EventDefine.WearBelt, Show); //移除监听事件
    }

    /// <summary>
    /// 实时跟踪位置和旋转信息
    /// </summary>
    private void FixedUpdate()
    {
        float height = target.GetComponent<CapsuleCollider>().height;
        transform.position = new Vector3(Camera.main.transform.position.x, height, Camera.main.transform.position.z);
        transform.eulerAngles = new Vector3(transform.eulerAngles.x, Camera.main.transform.eulerAngles.y, 0);
    }
    /// <summary>
    /// 显示弹药库界面
    /// </summary>
    private void Show()
    {
        gameObject.SetActive(true); //显示界面
        UpdateBullet(0);//执行更新子弹的函数
        UpdateBomb(0);//执行更新炸弹的函数
    }
    /// <summary>
    /// 重装弹夹
    /// </summary>
    public int ReloadMagazine()
    {
        if (BulletCount >= 6)
        {
            UpdateBullet(-6);
            return 6;
        }
        else
        {
            int temp = BulletCount;
            BulletCount = 0;
            UpdateBullet(0);
            return temp;
        }
    }
    /// <summary>
    /// 是否有手榴弹
    /// </summary>
    /// <returns></returns>
    public bool IsHasBomb()
    {
        if (BombCount <= 0)
        {
            return false;
        }
        return true;
    }
    /// <summary>
    /// 更新子弹数量
    /// </summary>
    /// <param name="count"></param>
    public void UpdateBullet(int count)
    {
        BulletCount += count;
        txt_Bullet.text = BulletCount.ToString();
    }
    /// <summary>
    /// 更新手榴弹数量
    /// </summary>
    /// <param name="count"></param>
    public void UpdateBomb(int count = -1)
    {
        BombCount += count;
        txt_Bomb.text = BombCount.ToString();
    }
}

Loading

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;



/// <summary>
/// 异步加载
/// </summary>
public class Loading : MonoBehaviour
{
    public string LoadSceneName;
    private Text text;
    private AsyncOperation m_Ao; //异步加载操作
    private bool m_IsLoad = false;

    private void Awake()
    {
        text = GetComponent<Text>();
        gameObject.SetActive(false);
        EventCenter.AddListener(EventDefine.StartLoadScene, StartLoadScene);  //监听异步加载的事件
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(EventDefine.StartLoadScene, StartLoadScene); //移除监听事件
    }
    private void StartLoadScene()
    {
        gameObject.SetActive(true);
        StartCoroutine("Load");//只有引号才能停止协程
    }
    IEnumerator Load()
    {
        int startProcess = -1;
        int endProcess = 100;
        while (startProcess < endProcess)
        {
            startProcess++;
            Show(startProcess);
            if (m_IsLoad == false)
            {
                m_Ao = SceneManager.LoadSceneAsync(LoadSceneName); //异步加载场景
                m_Ao.allowSceneActivation = false;//先没有激活场景  加载完成后再激活
                m_IsLoad = true;
            }
            yield return new WaitForEndOfFrame(); //等待加载完成
        }
        if (startProcess == 100)
        {
            m_Ao.allowSceneActivation = true;
            StopCoroutine("Load");
        }
    }
    private void Show(int value)
    {
        text.text = value.ToString() + "%";
    }
}

RadialMenuManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;



/// <summary>
/// 圆环控制器
/// </summary>
public class RadialMenuManager : MonoBehaviour
{
    private bool m_IsWearBelt = false;
    private bool m_IsWearPistol = false;

    private void Awake()
    {
        gameObject.SetActive(false); //默认圆环是隐藏的
        EventCenter.AddListener(EventDefine.WearBelt, WearBelt);  //监听手枪拿起和腰带佩戴上才可以 显示圆环
        EventCenter.AddListener(EventDefine.WearPistol, WearPistol);
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(EventDefine.WearBelt, WearBelt);
        EventCenter.RemoveListener(EventDefine.WearPistol, WearPistol);
    }
    /// <summary>
    /// 腰带穿戴上的监听方法
    /// </summary>
    private void WearBelt()
    {
        m_IsWearBelt = true;
        if (m_IsWearBelt && m_IsWearPistol)
        {
            gameObject.SetActive(true);  //腰带和枪都带上才能显示圆环  在广播带上枪和腰带的方法  Belt 17  HandManger 357
        }
    }
    /// <summary>
    /// 枪拿起的监听方法
    /// </summary>
    private void WearPistol()
    {
        m_IsWearPistol = true;
        if (m_IsWearBelt && m_IsWearPistol)
        {
            gameObject.SetActive(true);
        }
    }

    /// <summary>
    /// 圆盘上点击手枪的函数
    /// </summary>
    public void OnUsePistolClick()
    {
        HandManger handManger = transform.parent.parent.parent.GetComponentInChildren<HandManger>();  //获取手管理类 注意父级关系
        if (handManger.m_GrabObj != null && handManger.m_GrabObjectType == GrabObjectType.Pistol)   //判断手上握的是手枪 则卸载手枪
        {
            handManger.UnUsePistol();
        }
        if (handManger.m_GrabObj != null && handManger.m_GrabObjectType == GrabObjectType.Bomb) //握住炸弹则卸载炸弹
        {
            handManger.UnUseBomb();
        }
        EventCenter.Broadcast(EventDefine.UsePistol);  //广播使用手枪的事件码
        EventCenter.Broadcast(EventDefine.IsStartGestureRecognition, false);//广播使用手势的事件码  不可以使用
    }

    /// <summary>
    /// 圆盘上点击炸弹的函数
    /// </summary>
    public void OnUseBombClick()
    {
        HandManger handManger = transform.parent.parent.parent.GetComponentInChildren<HandManger>();  //避免左手拿到手枪之后不能继续切换
        if (handManger.m_GrabObj != null && handManger.m_GrabObjectType == GrabObjectType.Pistol)  
        {
            handManger.UnUsePistol(); //卸载手枪
        }
        if (handManger.m_GrabObj != null && handManger.m_GrabObjectType == GrabObjectType.Bomb)
        {
            handManger.UnUseBomb();//卸载炸弹
        }
        EventCenter.Broadcast(EventDefine.UseBomb); //广播使用炸弹的事件码 
        EventCenter.Broadcast(EventDefine.IsStartGestureRecognition, false); //广播使用手势的事件码 不可使用
    }
    /// <summary>
    /// 圆盘上点击手势的按钮
    /// </summary>
    public void OnUseGestureClick()
    {
        HandManger[] handMangers = GameObject.FindObjectsOfType<HandManger>();  //无论手上握住任何东西都卸载
        foreach (var handManger in handMangers)
        {
            if (handManger.m_GrabObj != null && handManger.m_GrabObjectType == GrabObjectType.Pistol)
            {
                handManger.UnUsePistol(); //手上有枪则卸载
            }
            if (handManger.m_GrabObj != null && handManger.m_GrabObjectType == GrabObjectType.Bomb)
            {
                handManger.UnUseBomb();//手上有炸弹则卸载
            }
        }
        EventCenter.Broadcast(EventDefine.IsStartGestureRecognition, true); //广播使用手势的事件码  可以使用手势  点击圆盘其他按钮的时候广播 不可用手势识别 false
    }
}

Gesture:

BaseGestureRecognition

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Edwon.VR;
using Edwon.VR.Gesture;


/// <summary>
/// 手势识别基类  抽象类
/// </summary>
public abstract class BaseGestureRecognition : MonoBehaviour
{
    private VRGestureSettings gestureSettings;
    protected VRGestureRig gestureRig;

    public abstract void OnGestureDetectedEvent(string gestureName, double confidence); //抽象的方法

    public virtual void Awake()  //虚方法 要重写
    {
        gestureSettings = Utils.GetGestureSettings();
        gestureRig = VRGestureRig.GetPlayerRig(gestureSettings.playerID);
        GestureRecognizer.GestureDetectedEvent += GestureRecognizer_GestureDetectedEvent; //监听事件 自动补全方法
    }
    public virtual void OnDestroy()  //虚方法
    {
        GestureRecognizer.GestureDetectedEvent -= GestureRecognizer_GestureDetectedEvent;//移除监听
    }
    /// <summary>
    /// 当手势被检测到
    /// </summary>
    /// <param name="gestureName"></param>
    /// <param name="confidence"></param>
    /// <param name="hand"></param>
    /// <param name="isDouble"></param>
    private void GestureRecognizer_GestureDetectedEvent(string gestureName, double confidence, Handedness hand, bool isDouble = false) //手势名称  识别精度 左右手  是否双手
    {
        OnGestureDetectedEvent(gestureName, confidence);
    }
    /// <summary>
    /// 开始手势识别  受保护
    /// </summary>
    protected void BeginRecognition()
    {
        gestureRig.BeginDetect();
    }
}

GestureDetectedPanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Edwon.VR;
using Edwon.VR.Gesture;

public class GestureDetectedPanel : BaseGestureRecognition
{
    private Button btn_Back;
    private Text txt_GestureName;
    private Text txt_GestureConfidence;

    public override void Awake()
    {
        base.Awake();
        btn_Back = transform.Find("btn_Back").GetComponent<Button>();
        btn_Back.onClick.AddListener(() =>
        {
            EventCenter.Broadcast(EventDefine.ShowGestureMainPanel);
            gameObject.SetActive(false);
        });
        txt_GestureName = transform.Find("txt_GestureName").GetComponent<Text>();
        txt_GestureConfidence = transform.Find("txt_GestureConfidence").GetComponent<Text>();
        EventCenter.AddListener(EventDefine.ShowGextureDetectedPanel, Show);

        gameObject.SetActive(false);
    }

    IEnumerator Dealy(string gestureName, double confidence)
    {
        txt_GestureName.text = gestureName;
        txt_GestureConfidence.text = confidence.ToString("F3"); //小数点后面三位
        yield return new WaitForSeconds(0.5f);
        txt_GestureName.text = "";
        txt_GestureConfidence.text = "";
    }
    public override void OnDestroy()
    {
        base.OnDestroy();
        EventCenter.RemoveListener(EventDefine.ShowGextureDetectedPanel, Show);
    }
    private void Show()
    {
        gameObject.SetActive(true);
        BeginRecognition();
    }

    public override void OnGestureDetectedEvent(string gestureName, double confidence)
    {
        StartCoroutine(Dealy(gestureName, confidence));
    }
}

GestureEditPanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Edwon.VR;
using Edwon.VR.Gesture;

public class GestureEditPanel : MonoBehaviour
{
    public GameObject go_GestureExampleItem;
    public Material m_Mat;
    private Text txt_GestureName;
    private Button btn_Back;
    private Button btn_Record;

    private VRGestureSettings gestureSettings;
    private VRGestureRig gestureRig;
    /// <summary>
    /// 手势所对应的所有的手势记录list
    /// </summary>
    private List<GestureExample> gestureExamples = new List<GestureExample>();
    private List<GameObject> exampleObjList = new List<GameObject>();
    /// <summary>
    /// 手势名字
    /// </summary>
    private string m_GestureName;
    /// <summary>
    /// 判断是否开始录制
    /// </summary>
    private bool m_IsStartRecord = false;

    private void Awake()
    {
        gestureSettings = Utils.GetGestureSettings();
        gestureRig = VRGestureRig.GetPlayerRig(gestureSettings.playerID);

        EventCenter.AddListener<string>(EventDefine.ShowGestureEditPanel, Show);
        EventCenter.AddListener(EventDefine.FinishedOnceRecord, FinishedOnceRecord);
        EventCenter.AddListener<bool>(EventDefine.UIPointHovering, UIPointHovering);
        Init();
    }
    private void Init()
    {
        txt_GestureName = transform.Find("txt_GestureName").GetComponent<Text>();
        btn_Back = transform.Find("btn_Back").GetComponent<Button>();
        btn_Back.onClick.AddListener(() =>
        {
            BeginTraining();
            m_IsStartRecord = false;
            EventCenter.Broadcast(EventDefine.ShowGestureInfoPanel);
            gameObject.SetActive(false);
        });
        btn_Record = transform.Find("btn_Record").GetComponent<Button>();
        btn_Record.onClick.AddListener(BeginRecord);

        gameObject.SetActive(false);
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener<string>(EventDefine.ShowGestureEditPanel, Show);
        EventCenter.RemoveListener(EventDefine.FinishedOnceRecord, FinishedOnceRecord);
        EventCenter.RemoveListener<bool>(EventDefine.UIPointHovering, UIPointHovering);
    }
    private void Show(string gestureName)
    {
        m_GestureName = gestureName;
        gameObject.SetActive(true);
        txt_GestureName.text = gestureName;
        BeginEditGesture(gestureName);
    }
    /// <summary>
    /// 开始训练  重新学习一次
    /// </summary>
    private void BeginTraining()
    {
        gestureSettings.BeginTraining(FinishTraining);
    }
    private void FinishTraining(string netName)
    {

    }
    /// <summary>
    /// 获取射线是否检测到UI
    /// </summary>
    /// <param name="value"></param>
    private void UIPointHovering(bool value)
    {
        if (m_IsStartRecord)
        {
            if (value)
            {
                gestureRig.uiState = VRGestureUIState.Gestures;
            }
            else
            {
                BeginRecord();
            }
        }
    }
    /// <summary>
    /// 完成一次手势记录得录制会调用到这个方法
    /// </summary>
    private void FinishedOnceRecord()
    {
        GetGestureAllExample(m_GestureName);
        GenerateExamplesGrid();
    }
    /// <summary>
    /// 开始录制手势
    /// </summary>
    private void BeginRecord()
    {
        m_IsStartRecord = true;
        gestureRig.BeginReadyToRecord(m_GestureName);//录制手势
    }
    /// <summary>
    /// 开始编辑手势
    /// </summary>
    /// <param name="gestureName"></param>
    private void BeginEditGesture(string gestureName)
    {
        gestureRig.uiState = VRGestureUIState.Editing;
        gestureRig.BeginEditing(gestureName);
        GetGestureAllExample(gestureName);
        GenerateExamplesGrid();
    }
    /// <summary>
    /// 获取手势的所有记录
    /// </summary>
    /// <param name="gestureName"></param>
    public void GetGestureAllExample(string gestureName)
    {
        gestureExamples = Utils.GetGestureExamples(gestureName, gestureSettings.currentNeuralNet);
        foreach (var item in gestureExamples)
        {
            if (item.raw)  //规整大小
            {
                item.data = Utils.SubDivideLine(item.data);
                item.data = Utils.DownScaleLine(item.data);
            }
        }
    }
    /// <summary>
    /// 生成所有得记录
    /// </summary>
    public void GenerateExamplesGrid()
    {
        foreach (var obj in exampleObjList)
        {
            Destroy(obj);
        }
        exampleObjList.Clear();

        for (int i = 0; i < gestureExamples.Count; i++)
        {
            GameObject go = Instantiate(go_GestureExampleItem, transform.Find("Parent"));
            go.GetComponent<GestureExampleItem>().Init(gestureExamples[i].name, i);
            LineRenderer line = go.GetComponentInChildren<LineRenderer>();
            line.useWorldSpace = false;
            line.material = m_Mat;
            line.startColor = Color.blue;
            line.endColor = Color.green;
            float lineWidth = 0.01f;
            line.startWidth = lineWidth - (lineWidth * 0.5f);
            line.endWidth = lineWidth + (lineWidth * 0.5f);
            line.positionCount = gestureExamples[i].data.Count;

            for (int j = 0; j < gestureExamples[i].data.Count; j++)
            {
                gestureExamples[i].data[j] = gestureExamples[i].data[j] * 40;
            }
            line.SetPositions(gestureExamples[i].data.ToArray());

            exampleObjList.Add(go);
        }
    }
}

GestureMainPanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Edwon.VR;
using Edwon.VR.Gesture;

/// <summary>
/// 手势识别
/// </summary>
public class GestureMainPanel : MonoBehaviour
{
    private Button btn_GestureInfo;
    private Button btn_GestureDetect;
    private VRGestureSettings gestureSettings;
    private VRGestureRig gestureRig;

    private void Awake()
    {
        gestureSettings = Utils.GetGestureSettings();//赋值
        gestureRig = VRGestureRig.GetPlayerRig(gestureSettings.playerID);

        btn_GestureInfo = transform.Find("btn_GestureInfo").GetComponent<Button>();
        btn_GestureInfo.onClick.AddListener(() => //手势按钮点击事件设置
        {
            //进入手势信息页面
            EventCenter.Broadcast(EventDefine.ShowGestureInfoPanel);
            gameObject.SetActive(false);
        });
        btn_GestureDetect = transform.Find("btn_GestureDetect").GetComponent<Button>();
        btn_GestureDetect.onClick.AddListener(() =>
        {
            //进入手势测试界面
            EventCenter.Broadcast(EventDefine.ShowGextureDetectedPanel);
            gameObject.SetActive(false);
        });
        EventCenter.AddListener(EventDefine.ShowGestureMainPanel, Show);

        Show();
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(EventDefine.ShowGestureMainPanel, Show);
    }
    private void Show()
    {
        gestureRig.uiState = VRGestureUIState.Idle;
        gameObject.SetActive(true);
    }
}

SkillChoosePanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SkillChoosePanel : MonoBehaviour
{
    private string m_GestureName;

    private void Awake()
    {
        EventCenter.AddListener<string>(EventDefine.ShowSkillChoosePanel, Show);
        Init();
    }
    private void Init()
    {
        transform.Find("btn_Back").GetComponent<Button>().onClick.AddListener(() =>
        {
            //更换技能
            for (int i = 0; i < transform.Find("Parent").childCount; i++)
            {
                if (transform.Find("Parent").GetChild(i).GetComponent<Toggle>().isOn)
                {
                    GestureSkillManager.ChangeSkill(m_GestureName, transform.Find("Parent").GetChild(i).name); //获取名字
                }
            }
            EventCenter.Broadcast(EventDefine.ShowGestureInfoPanel); //注意顺序
            gameObject.SetActive(false);
        });
        gameObject.SetActive(false);
    }
    private void OnDestroy()
    {
        //EventCenter.AddListener<string>(EventDefine.ShowSkillChoosePanel, Show);
        EventCenter.RemoveListener<string>(EventDefine.ShowSkillChoosePanel, Show);
    }
    private void Update()
    {
        for (int i = 0; i < transform.Find("Parent").childCount; i++)
        {
            if (transform.Find("Parent").GetChild(i).GetComponent<Toggle>().isOn)
            {
                transform.Find("Parent").GetChild(i).GetChild(0).gameObject.SetActive(true);
            }
            else
            {
                transform.Find("Parent").GetChild(i).GetChild(0).gameObject.SetActive(false);
            }
        }
    }
    private void Show(string gestureName)
    {
        gameObject.SetActive(true);
        m_GestureName = gestureName;
        string skillName = GestureSkillManager.GetSkillNameByGestureName(gestureName);

        for (int i = 0; i < transform.Find("Parent").childCount; i++)
        {
            if (transform.Find("Parent").GetChild(i).name == skillName)
            {
                transform.Find("Parent").GetChild(i).GetComponent<Toggle>().isOn = true;
                transform.Find("Parent").GetChild(i).GetChild(0).gameObject.SetActive(true);
            }
            else
            {
                transform.Find("Parent").GetChild(i).GetComponent<Toggle>().isOn = false;
                transform.Find("Parent").GetChild(i).GetChild(0).gameObject.SetActive(false);
            }
        }
    }
}

三、效果图

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容