VR開發(fā)實(shí)戰(zhàn)HTC Vive項(xiàng)目之僵尸大戰(zhàn)(語音喚醒與手勢識別)

一、框架視圖

二刺下、主要代碼

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; //爆炸等待時(shí)間
    public GameObject effect_Brust; //爆炸特效

    private float m_Timer = 0.0f; //計(jì)時(shí)器

    private void FixedUpdate()
    {
        if (IsThrow) //判斷是否可以爆炸  在手管理類的時(shí)候投擲炸彈設(shè)置為true  HandManger170 
        {
            m_Timer += Time.deltaTime;
            if (m_Timer >= BrustTime)
            {
                Instantiate(effect_Brust, transform.position, transform.rotation); //實(shí)例化特效
                Destroy(gameObject);
                EventCenter.Broadcast(EventDefine.BombBrust, transform.position);//廣播發(fā)生爆炸特效的事件 當(dāng)前爆炸位置的信息
            }
        }
    }
}

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>
    /// 判斷書本是否觸發(fā)到書臺(tái)
    /// </summary>
    public bool m_IsTrigger = false;
    /// <summary>
    /// 觸發(fā)的書臺(tái)物體
    /// </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>
    /// 是否激活當(dāng)前書本對應(yīng)的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); //加載技能預(yù)制體
        Instantiate(skill, new Vector3(Camera.main.transform.position.x, 0, Camera.main.transform.position.z), skill.transform.rotation);  //實(shí)例化技能 注意生成的位置
    }
}

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>
    /// 獲取手勢名與技能名之間的關(guān)系
    /// </summary>
    /// <returns></returns>
    private static Dictionary<string, string> GetGestureSkillDic()
    {
        Dictionary<string, string> gestureSkillDic = new Dictionary<string, string>();
        //規(guī)則:手勢名-技能名绑嘹;手勢名-技能名
        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>
    /// 保存手勢與技能之間的關(guān)系
    /// </summary>
    private static void SaveGestureSkillDic(Dictionary<string, string> dic)
    {
        string temp = "";
        int index = 0;
        foreach (var item in dic)
        {
            //規(guī)則:手勢名-技能名;手勢名-技能名
            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>
    /// 更換手勢與技能之間的關(guān)系(更換技能)
    /// </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(); //加載當(dāng)前活躍的場景
            }
            else
            {
                HP += count;  //增加血量
            }
        }
        else
        {
            HP += count;
        }
        EventCenter.Broadcast(EventDefine.UpdateHpUI, HP);  //廣播更新血量的事件碼
    }
    private void Death()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);  //加載當(dāng)前場景
    }
}

Magazine

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


/// <summary>
/// 彈夾管理類
/// </summary>
public class Magazine : MonoBehaviour
{
    /// <summary>
    /// 子彈數(shù)量
    /// </summary>
    private int BulletCount = 6;

    /// <summary>
    /// 設(shè)置子彈的方法
    /// </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;//射擊事件  這兩個(gè)要在面板上指定是哪個(gè)事件  分別是左右手  
    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;//動(dòng)畫
    private RaycastHit m_Hit;//射線
    public int m_CurrentBulletCount = 6; //彈夾數(shù)量
    private AudioSource m_AudioSource; 

    private void Awake()
    {
        m_AudioSource = GetComponent<AudioSource>(); //獲取組件
        m_LineRenderer = GetComponent<LineRenderer>();
        m_Anim = GetComponent<Animator>();
        EventCenter.AddListener(ShotEvent, Shot);//添加射擊監(jiān)聽  廣播事件是分別執(zhí)行左右手
        EventCenter.AddListener(ReloadMagazineEvent, ReloadMagazine);//添加換彈夾事件  廣播時(shí)候左右手按下圓盤時(shí)候
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(ShotEvent, Shot);//移除監(jiān)聽射擊事件
        EventCenter.RemoveListener(ReloadMagazineEvent, ReloadMagazine); //移除換彈夾事件
    }
    /// <summary>
    /// 換彈夾
    /// </summary>
    private void ReloadMagazine()
    {
        //代表是Main場景 則忽略
        if (SceneManager.GetActiveScene().buildIndex == 0) return;
        //如果手槍是隱藏的橘茉,則忽略
        if (gameObject.activeSelf == false) return;
        //如果當(dāng)前正在播放開火的動(dòng)畫工腋,則忽略
        if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("Fire")) return;
        //如果當(dāng)前正在播放換彈夾的動(dòng)畫蛤克,則忽略
        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;  //當(dāng)前數(shù)量
        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;
        //如果當(dāng)前正在播放開火的動(dòng)畫夷蚊,則忽略
        if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("Fire")) return;
        //如果當(dāng)前正在播放換彈夾的動(dòng)畫夭苗,則忽略
        if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("Reload")) return;

        if (m_CurrentBulletCount <= 0) return;
        m_CurrentBulletCount--; //子彈數(shù)量減減
        //播放射擊動(dòng)畫
        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();
                }
                //實(shí)例化血的特效添瓷,1.5秒之后銷毀
                Destroy(Instantiate(effect_Blood, m_Hit.point, Quaternion.LookRotation(m_Hit.normal)), 2f);  //旋轉(zhuǎn)看向受傷的地方
            }
            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);//實(shí)例化煙霧 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>
    /// 攻擊時(shí)間間隔
    /// </summary>
    public float m_AttackInterval = 3f;
    public AudioClip audio_Attack;
    public AudioClip audio_Walk;

    private NavMeshAgent m_Agent; //導(dǎo)航網(wǎng)格
    private Animator m_Anim;
    private Transform m_Target; //相機(jī)位置
    /// <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;  //相機(jī)信息
        EventCenter.AddListener<Vector3>(EventDefine.BombBrust, BombBrust); //監(jiān)聽炸彈爆炸的事件碼
    }
    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) //判斷相機(jī)跟物體之間的距離
        {
            if (m_Agent.isStopped == false)
            {
                m_Agent.isStopped = true; //停止導(dǎo)航
            }

            if (m_IsAttacking == false)
            {
                m_Timer += Time.deltaTime;
                if (m_Timer >= m_AttackInterval)
                {
                    m_Timer = 0.0f;
                    m_IsAttacking = true; //調(diào)用攻擊的函數(shù)
                    Attack();
                }
            }
            if (m_Anim.GetCurrentAnimatorStateInfo(0).IsName("AttackBlendTree") == false) //第0層的播放動(dòng)畫名稱
            {
                if (m_IsFirstAttack)  //標(biāo)志位 只加一次0.5f  之后不加 避免判斷距離越來越大
                {
                    m_DistanceJudge += 0.5f; //避免根據(jù)距離動(dòng)畫來回播放
                    m_IsFirstAttack = false; 
                    m_IsAttacking = true;
                    Attack();
                }
                else
                {
                    m_IsAttacking = false;
                }
            }
        }
        else
        {  //沒達(dá)到距離 繼續(xù)尋路
            if (m_Agent.isStopped)
            {
                m_DistanceJudge -= 0.5f; //避免根據(jù)距離動(dòng)畫來回播放
                m_Agent.isStopped = false;  //繼續(xù)尋路
                m_IsFirstAttack = true;
                RandomWalkOrRun();//隨機(jī)走或跑
            }
            m_Agent.SetDestination(Camera.main.transform.position);  //設(shè)置新的目的地
        }
    }
    /// <summary>
    /// 炸彈爆炸
    /// </summary>
    /// <param name="brustPos"></param>
    private void BombBrust(Vector3 brustPos)
    {
        if (Vector3.Distance(transform.position, brustPos) < 10.0f)  //爆炸距離之內(nèi)
        {
            BodyPartDrop[] drops = transform.GetComponentsInChildren<BodyPartDrop>();//查找子物體上的所有組件 數(shù)組
            foreach (var item in drops)
            {
                item.Hit();
            }
            Death();
        }
    }
    /// <summary>
    /// 死亡
    /// </summary>
    public void Death()
    {
        if (m_IsDeath) return; //如果掛掉就返回 不用重復(fù)執(zhí)行
        PlayAnim(4, "Death", "DeathValue"); //4中隨機(jī)死亡動(dòng)畫 
        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>
    /// 隨機(jī)受傷動(dòng)畫
    /// </summary>
    public void Hit()
    {
        PlayAnim(3, "Hit", "HitValue"); //3種隨機(jī)動(dòng)畫
        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"); //隨機(jī)攻擊動(dòng)畫片段
    }
    /// <summary>
    /// 隨機(jī)播放跑或者走的動(dòng)畫
    /// </summary>
    private void RandomWalkOrRun()
    {
        int ran = Random.Range(0, 2); //只隨機(jī)0,1
        if (ran == 0)
        {
            //走
            WalkAnim();
            m_Agent.speed = m_WalkSpeed;
        }
        else
        {
            //跑
            RunAnim();
            m_Agent.speed = m_RunSpeed;
        }
    }
    /// <summary>
    /// 走路動(dòng)畫
    /// </summary>
    private void WalkAnim() 
    {
        if (m_AudioSource.isPlaying == false)
        {
            m_AudioSource.clip = audio_Walk;
            m_AudioSource.Play();
        }
        PlayAnim(3, "Walk", "WalkValue");
    }
    /// <summary>
    /// 跑的動(dòng)畫
    /// </summary>
    private void RunAnim()
    {
        PlayAnim(2, "Run", "RunValue");
    }

    /// <summary>
    /// 隨機(jī)動(dòng)畫片段  走路3個(gè)  跑步2個(gè) 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);  //設(shè)置動(dòng)畫觸發(fā)器
        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>
    /// 監(jiān)聽抓取按鍵按下的事件碼
    /// </summary>
    public EventDefine GrabEvent;  //定義不同類型 監(jiān)聽左右手柄不同的事件
    public EventDefine ShotEvent;
    public EventDefine UseBombEvent;
    public EventDefine UsePistolEvent;

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

    private Animator m_Anim; //動(dòng)畫
    /// <summary>
    /// 是否可以抓取
    /// </summary>
    private bool m_IsCanGrab = false;
    /// <summary>
    /// 當(dāng)前抓取的物體
    /// </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>
    /// 是否可以抓取物體的監(jiān)聽方法
    /// </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()
    {
        //更新炸彈數(shù)量
        AmmoManager.Instance.UpdateBomb();

        m_GrabObj.transform.parent = null; //設(shè)置父物體為空
        m_GrabObj.AddComponent<Rigidbody>();//添加剛體
        m_Anim.SetBool("Catch", false);//播放動(dòng)畫
        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.滿足當(dāng)前手沒有抓取任何物體
        //2.當(dāng)前手沒有觸碰到任何可以抓取的物體
        //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;

        //判斷當(dāng)前右手是否拿著物品彤枢,如果拿著則卸掉
        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;
        //切換成拿槍的動(dòng)畫
        //顯示手槍
        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();  //關(guān)閉高亮
        }
        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>
    /// 子彈數(shù)量
    /// </summary>
    public int BulletCount;
    /// <summary>
    /// 炸彈數(shù)量
    /// </summary>
    public int BombCount;

    private Transform target; //目標(biāo)物體
    private Text txt_Bullet;//子彈數(shù)量
    private Text txt_Bomb;//炸彈數(shù)量

    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; //查找目標(biāo)物體
        EventCenter.AddListener(EventDefine.WearBelt, Show); //監(jiān)聽穿戴事件
        gameObject.SetActive(false); //默認(rèn)隱藏
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(EventDefine.WearBelt, Show); //移除監(jiān)聽事件
    }

    /// <summary>
    /// 實(shí)時(shí)跟蹤位置和旋轉(zhuǎn)信息
    /// </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);//執(zhí)行更新子彈的函數(shù)
        UpdateBomb(0);//執(zhí)行更新炸彈的函數(shù)
    }
    /// <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>
    /// 更新子彈數(shù)量
    /// </summary>
    /// <param name="count"></param>
    public void UpdateBullet(int count)
    {
        BulletCount += count;
        txt_Bullet.text = BulletCount.ToString();
    }
    /// <summary>
    /// 更新手榴彈數(shù)量
    /// </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);  //監(jiān)聽異步加載的事件
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(EventDefine.StartLoadScene, StartLoadScene); //移除監(jiān)聽事件
    }
    private void StartLoadScene()
    {
        gameObject.SetActive(true);
        StartCoroutine("Load");//只有引號才能停止協(xié)程
    }
    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>
/// 圓環(huán)控制器
/// </summary>
public class RadialMenuManager : MonoBehaviour
{
    private bool m_IsWearBelt = false;
    private bool m_IsWearPistol = false;

    private void Awake()
    {
        gameObject.SetActive(false); //默認(rèn)圓環(huán)是隱藏的
        EventCenter.AddListener(EventDefine.WearBelt, WearBelt);  //監(jiān)聽手槍拿起和腰帶佩戴上才可以 顯示圓環(huán)
        EventCenter.AddListener(EventDefine.WearPistol, WearPistol);
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener(EventDefine.WearBelt, WearBelt);
        EventCenter.RemoveListener(EventDefine.WearPistol, WearPistol);
    }
    /// <summary>
    /// 腰帶穿戴上的監(jiān)聽方法
    /// </summary>
    private void WearBelt()
    {
        m_IsWearBelt = true;
        if (m_IsWearBelt && m_IsWearPistol)
        {
            gameObject.SetActive(true);  //腰帶和槍都帶上才能顯示圓環(huán)  在廣播帶上槍和腰帶的方法  Belt 17  HandManger 357
        }
    }
    /// <summary>
    /// 槍拿起的監(jiān)聽方法
    /// </summary>
    private void WearPistol()
    {
        m_IsWearPistol = true;
        if (m_IsWearBelt && m_IsWearPistol)
        {
            gameObject.SetActive(true);
        }
    }

    /// <summary>
    /// 圓盤上點(diǎn)擊手槍的函數(shù)
    /// </summary>
    public void OnUsePistolClick()
    {
        HandManger handManger = transform.parent.parent.parent.GetComponentInChildren<HandManger>();  //獲取手管理類 注意父級關(guān)系
        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>
    /// 圓盤上點(diǎn)擊炸彈的函數(shù)
    /// </summary>
    public void OnUseBombClick()
    {
        HandManger handManger = transform.parent.parent.parent.GetComponentInChildren<HandManger>();  //避免左手拿到手槍之后不能繼續(xù)切換
        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>
    /// 圓盤上點(diǎn)擊手勢的按鈕
    /// </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); //廣播使用手勢的事件碼  可以使用手勢  點(diǎn)擊圓盤其他按鈕的時(shí)候廣播 不可用手勢識別 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; //監(jiān)聽事件 自動(dòng)補(bǔ)全方法
    }
    public virtual void OnDestroy()  //虛方法
    {
        GestureRecognizer.GestureDetectedEvent -= GestureRecognizer_GestureDetectedEvent;//移除監(jiān)聽
    }
    /// <summary>
    /// 當(dāng)手勢被檢測到
    /// </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>
    /// 開始手勢識別  受保護(hù)
    /// </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"); //小數(shù)點(diǎn)后面三位
        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>
    /// 手勢所對應(yīng)的所有的手勢記錄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>
    /// 開始訓(xùn)練  重新學(xué)習(xí)一次
    /// </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>
    /// 完成一次手勢記錄得錄制會(huì)調(diào)用到這個(gè)方法
    /// </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)  //規(guī)整大小
            {
                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(() => //手勢按鈕點(diǎn)擊事件設(shè)置
        {
            //進(jìn)入手勢信息頁面
            EventCenter.Broadcast(EventDefine.ShowGestureInfoPanel);
            gameObject.SetActive(false);
        });
        btn_GestureDetect = transform.Find("btn_GestureDetect").GetComponent<Button>();
        btn_GestureDetect.onClick.AddListener(() =>
        {
            //進(jìn)入手勢測試界面
            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);
            }
        }
    }
}

三炎功、效果圖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末栓拜,一起剝皮案震驚了整個(gè)濱河市一膨,隨后出現(xiàn)的幾起案子呀邢,更是在濱河造成了極大的恐慌,老刑警劉巖豹绪,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件价淌,死亡現(xiàn)場離奇詭異,居然都是意外死亡瞒津,警方通過查閱死者的電腦和手機(jī)蝉衣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巷蚪,“玉大人病毡,你說我怎么就攤上這事∑ò兀” “怎么了啦膜?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長淌喻。 經(jīng)常有香客問我僧家,道長,這世上最難降的妖魔是什么裸删? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任啸臀,我火速辦了婚禮,結(jié)果婚禮上烁落,老公的妹妹穿的比我還像新娘乘粒。我一直安慰自己,他們只是感情好伤塌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布灯萍。 她就那樣靜靜地躺著,像睡著了一般每聪。 火紅的嫁衣襯著肌膚如雪旦棉。 梳的紋絲不亂的頭發(fā)上齿风,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音绑洛,去河邊找鬼救斑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛真屯,可吹牛的內(nèi)容都是我干的脸候。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绑蔫,長吁一口氣:“原來是場噩夢啊……” “哼运沦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起配深,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤携添,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后篓叶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烈掠,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年缸托,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了向叉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嗦董,死狀恐怖母谎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情京革,我是刑警寧澤奇唤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站匹摇,受9級特大地震影響咬扇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一堡妒、第九天 我趴在偏房一處隱蔽的房頂上張望忿檩。 院中可真熱鬧,春花似錦窖壕、人聲如沸晌砾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叭爱。三九已至,卻和暖如春漱病,著一層夾襖步出監(jiān)牢的瞬間买雾,已是汗流浹背把曼。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漓穿,地道東北人嗤军。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像晃危,于是被迫代替她去往敵國和親叙赚。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容