Unity | VRTK游戏的基础设

文章目录

  • HTCVIVE与设备连接
  • 实现手柄控制发射子弹功能
  • VRTK插件与UGUI的交互:Menu Scene制作
    • [VRTK_SDKManager]
    • [VRTK_Scripts]
      • VRTK_Controller Events
      • VRTK_Pointer
      • VRTK_Straight Pointer Renderer
      • VRTK_UI Pointer
    • [Canvas]
    • [EventSystem]
    • Menu Scene制作步骤概括
  • 加入VRTK的遊戲製作:模擬水果忍者
    • 修改Canvas
    • 设置Controller
    • 制作游戏
    • 测试
    • 参考设置图片

HTCVIVE与设备连接

  1. 汇入SteamVR和VRTK
    • 注意点选I made a back up/ Go ahead更新
    • 对于汇入后报错需要跳转到 line 146: new Texture改为new Texture2D(0,0)

  2. 新建场景Pro1使用SteamVR/Prefabs/[CameraRig],同时删除主摄影机

  3. 给[CameraRig]/Controller(Left)/Model和[CameraRig]/Controller(Right)/Model分别添加BoxCollider(isTrigger=True,Size=(0,0,0)))和Rigibody(useGravity=false)

  4. 場景中加入Cube,添加BoxCollider(isTrigger=T,rigibody(useGravity=F) ,设置Cube.Tag=(new)Object

  5. [CameraRig]/Controller(Left)/Model和[CameraRig]/Controller(Right)/Model添加SteamVRController.cs,将Controller(Left)和Controller(Right)拖入Trackobj槽中

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

public class SteamVRController : MonoBehaviour
{
     
    [Header("VIVE手把控制器")]
    public SteamVR_TrackedObject trackobj;

    //当HTC VIVE 手把碰到物件
    void OnTriggerStay(Collider hit)
    {
     
        var steam_state=SteamVR_Controller.Input((int)trackobj.index) ;

        /* 更改ButtonMask.Grip/Trigger/System/Touchpad
         * 
         *steam_state.GetPress(SteamVR_Controller.ButtonMask.Grip);
         * steam_state.GetPress(SteamVR_Controller.ButtonMask.Trigger);
         *steam_state.GetPress(SteamVR_Controller.ButtonMask.System);
         *steam_state.GetPress(SteamVR_Controller.ButtonMask.Touchpad);
         *GetPressDown;
         *GetPressUp;
         *if(steam_state.GetPress(SteamXR_Controller.ButtonMask.Grip))
         * 
        */

        //持续按下HTC VIVE 手把上的Grip键
        if(steam_state.GetPress(SteamVR_Controller.ButtonMask.Grip))
        {
     
            //如果手把碰到的物件标签为Object(如Cube物件)
            if(hit.GetComponent<Collider>().tag=="Object")
            {
     
                //碰撞物的坐标位置/角度=手把位置/角度
                hit.transform.position=transform.position;
                hit.transform.rotation=transform.rotation;
                //手把先开机的为左手把
                //如果按下左手把Grip按钮
                if(trackobj.name=="Controller (left)")
                {
     
                    print("Controller (left) Grip");
                    var deviceIndex=SteamVR_Controller.GetDeviceIndex(SteamVR_Controller.DeviceRelation.Leftmost);
                    //手把震动幅度0-3999,如果值小于100,基本上没啥感觉
                    SteamVR_Controller.Input(deviceIndex).TriggerHapticPulse(3000);
                }
                else
                {
     
                    print("Controller (right) Grip");
                    var deviceIndex=SteamVR_Controller.GetDeviceIndex(SteamVR_Controller.DeviceRelation.Rightmost);
                    //手把震动幅度0-3999,如果值小于100,基本上没啥感觉  
                    SteamVR_Controller.Input(deviceIndex).TriggerHapticPulse(3000);               
                }
            }
        } 

    }
}

实现手柄控制发射子弹功能

  1. 新建场景Pro2,使用SteamVR/Prefabs/[CameraRig],同时删除主摄影机
  2. 在[CameraRig]/Controller(Left)/Model或[CameraRig]/Controller(Right)/Model下建立Sphere. 設置Rigibody.UseGravity=F;SphereCollider.IsTrigger=F
  3. 给[CameraRig]/Controller(Left)/Model或[CameraRig]/Controller(Right)/Model添加SteamVRFire.cs。将Sphere拖入Bullet槽,Controller(Left)或Controller(Right)拖入Trackobj槽。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SteamVRFire : MonoBehaviour
{
    [Header("子弹物件")]
    public GameObject Bullet;
    //子弹移速
    private float Speed=1000f;
    //设定子弹销毁时间
    private float BulletLife=5f;
    [Header("VIVE手把控制器")]
    public SteamVR_TrackedObject trackobj;
    void Start(){}
    // Update is called once per frame
    void Update()
    {
        var steam_state=SteamVR_Controller.Input((int)trackobj.index);
        //如果按下滑鼠左键或HTC VIVE 手把Trigger按键
        //if(Input.GetMouseButton(0)||steam_state.GetPress(SteamVR_Controller.ButtonMask.Trigger))//
        if(Input.GetMouseButton(0)||steam_state.GetPressDown(SteamVR_Controller.ButtonMask.Trigger))
        {
            //动态生成一颗子弹(强制转换)
            GameObject BulletPrefab=Instantiate(Bullet,Bullet.transform.position,Bullet.transform.rotation) as GameObject;
            //开启动态生成的物件(子弹)
            BulletPrefab.SetActive(true);
            //抓取子弹的RigiBody属性
            Rigidbody rb=BulletPrefab.GetComponent();
            //透过AddForce给子弹某一个方向力,让物体继续往前
            rb.AddForce(BulletPrefab.transform.forward*Speed);
            //子弹没有达到任何东西的情况下就等待5秒删除子弹
            Destroy(BulletPrefab,BulletLife);
        }
    }
}

VRTK插件与UGUI的交互:Menu Scene制作

必备Game Object如下。Directional Light,Floor分别控制打光和场景的“地面”,这里不再作具体说明。
[外链图片转存失败(img-W6cQ1R6Q-1562839244125)(https://i.imgur.com/GoauE0Q.png)]

[VRTK_SDKManager]

為VR SDK提供支持。核心是SDKSetups,SDKSetupSwitcher將SDKSetups轉換為便於操作的GUI界面。

[VRTK_Scripts]

设定手柄等控制器与UGUI的交互。
这里只赋予右手把(RightController)交互功能,需要添加的Component如下。

[外链图片转存失败(img-GeoHCATK-1562839057544)(https://i.imgur.com/6k8KLym.png)]

VRTK_Controller Events

[外链图片转存失败(img-mmwFUE6G-1562839057544)(https://i.imgur.com/bhiuPlB.png)]
details

VRTK_Pointer

Provides a basis of being able to emit a pointer from a specified GameObject.

[外链图片转存失败(img-MIpJIiaD-1562839057545)(https://i.imgur.com/57miUrZ.png)]

details

VRTK_Straight Pointer Renderer

A visual pointer representation of a straight beam with an optional cursor at the end.

[外链图片转存失败(img-Z9qILiV3-1562839057546)(https://i.imgur.com/piycVQW.png)]
details
記得更改設置
General Appearance Settings.Tracer Visibility = Always On;
General Appearance Settings.Cursor Visibility = Always On;

VRTK_UI Pointer

Provides the ability to interact with UICanvas elements and the contained Unity UI elements within.

[外链图片转存失败(img-F4DIAIaF-1562839057547)(https://i.imgur.com/54S5Yb3.png)]
details
記得更改設置
Activation.Activation Mode = Always On;

[Canvas]

[外链图片转存失败(img-AQxN2VMm-1562839057548)(https://i.imgur.com/uMd8RqO.png)]

  • Canvas基礎設置
    Canvas.RenderMode = WorldSpace;
  • 添加VRTK_UI Canvas
  • 綁定Menu.cs控制程式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Menu : MonoBehaviour
{
     
    public void ToProj1()
    {
     
        Application.LoadLevel("Pro1");
    }

    public void ToProj2()
    {
     
        Application.LoadLevel("Pro2");
    }

    public void ToProj4()
    {
     
        Application.LoadLevel("Pro4");
    }
}
  • Button添加對應的OnClick()事件
    [外链图片转存失败(img-9PSDSA4w-1562839057549)(https://i.imgur.com/0stbZYS.png)]

[EventSystem]

Menu Scene制作步骤概括

  1. 新建保存一个Scene。
  2. 匯入[VRTK_SDKManager]預知
  3. 創建Empty Game Object作為Right Controller和Left Controller。
  4. 添加Canvas及其下的Button。
  5. 确保有EventSystem生成(若没有从Scene/[SDKSetupManager]/SDKSetupSwitcher/EventSystem 中复制粘贴一个EventSystem到Scene/EventSystem并勾选Inspector的小方框使之有效)。
  6. 設置Canvas模式為World Space,并添加VRTK_UIPointer元件
  7. 编写menu.cs程式,并将之绑定在Canvas上,然后为Button增添Click事件。
  8. Scene/LeftController添加VRTK_Controller Events
  9. Scene/RighttController添加
  • VRTK_Controller Events
  • VRTK_UI Pointer:
    .ActivationMode = Always On;
  • VRTK_Controller UI Pointer Events_Listener Example
  • VRTK_Pointer
  • VRTK_Straight Pointer Renderer:
    .TracerVisibility = Always On;
    .CursorVisibility = Always On;
  1. 在build setting中增添设计的场景并编译
  2. 运行(调整button位置)。

加入VRTK的遊戲製作:模擬水果忍者

在Menu场景的基础上修改:
[外链图片转存失败(img-P0uRZJc3-1562839057550)(https://i.imgur.com/GoauE0Q.png)]
[VRTK_Scripts]不变,需要修改[VRTK_SDKManager],把游戏结构加入到其中,即可:
[外链图片转存失败(img-4QCy25hZ-1562839057551)(https://i.imgur.com/8WOgAOr.png)]

修改Canvas

  1. 删除Button元件
  2. 去掉VRTK_UIPointer元件和menu.cs(用不上了)
  3. 设置Canvas模式為Screen Space (Camera)
  4. 在Canvas下添加三个Text,分别管理Score,Time和GAME OVER文字显示
  5. 将Canvas拖至[VRTK_SDKManager]/SDKSetups/SteamVR/[CameraRig]/Camera(head)下
    [外链图片转存失败(img-KcVxnWmD-1562839057552)(https://i.imgur.com/R5u7lbG.png)]

设置Controller

  1. 制作左右手的武器(从asset找或用cube替代皆可)
  2. 分别添加带Trigger的Box Collider和不带Gravity的Rigibody
  3. 将武器分别拖至[VRTK_SDKManager]/SDKSetups/SteamVR/[CameraRig]/Controller(Left)和[VRTK_SDKManager]/SDKSetups/SteamVR/[CameraRig]/Controller(Right)下
    [外链图片转存失败(img-Ai3ElVeS-1562839057552)(https://i.imgur.com/e2SZN7P.png)]

制作游戏

  1. 创建Empty Object命名为GM,并赋其GM.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GM : MonoBehaviour
{
     
    #region 倒计时
    [Header("倒数时间的文字")]
    public Text CountdownTimeText;
    [Header("属性面板中可调整整体游戏的时间")]
    public int Timer;
    //程式中倒数计时
    int ScriptTimer;
    #endregion

    #region 计分
    [Header("计分文字")]
    public Text ScoreText;
    //计算总分数
    int TotalScore;
    #endregion

    #region 产出物件
    [Header("所有要产出的物件")]
    public GameObject[] CreateObject;
    [Header("物件产出的位置")]
    public GameObject[] CreatePos;
    #endregion
    [Header("游戏结束文字")]
    public GameObject GameOverObj;

    //判断是否游戏结束
    bool isGameOver;

    // Start is called before the first frame update
    void Start()
    {
     
        //程式中的倒计时=属性面板中调整的时间
        ScriptTimer=Timer;
        InvokeRepeating("CountdownTime",1f,1f);
        InvokeRepeating("CreateObj",Random.Range(0.5f,1.5f),Random.Range(0.5f,1.5f));
    }

    // Update is called once per frame
    void Update()
    {
     
        //判断是否游戏结束
        if(isGameOver)
        {
     
            GameOverObj.SetActive(true);
        }
        else
        {
     
            GameOverObj.SetActive(false);
        }
    }

    void CountdownTime()
    {
     
        ScriptTimer--;
        ScriptTimer=Mathf.Clamp(ScriptTimer,0,Timer);
        CountdownTimeText.text="Time: "+ScriptTimer+"s";
        if(ScriptTimer<=0)
        {
     
            isGameOver=true;
        }
    }

    public void totalScore(int AddScore)
    {
     
        TotalScore+=AddScore;
        ScoreText.text="Score: "+TotalScore;
    }

    void CreateObj()
    {
     
        if(!isGameOver)
        {
     
            Instantiate(CreateObject[Random.Range(0,CreateObject.Length)],CreatePos[Random.Range(0,CreateObject.Length)].transform.position,transform.rotation);
        }
    }
}
  1. 将Canvas中的Text拖至GM设定中的对应槽中,随便设置一个游戏时间(30s为例)
    [外链图片转存失败(img-RIkxxV47-1562839057553)(https://i.imgur.com/4sUReRp.png)]
  2. 制作产出位置:新建Empty Object命名为Game拖至Position,在该空物件下再新建空物件若干,分别调节各个空物件位置(空物件所处位置即为产出位置)。
    [外链图片转存失败(img-csoAF19k-1562839057554)(https://i.imgur.com/tV810Bo.png)]

Note:可用Select Icon与其它游戏物件作区分

  1. 将GamePosition拖至[VRTK_SDKManager]/SDKSetups/SteamVR下
  2. 指定GM中的产出位置数目,将GamePosition中的空物件拖至生成的对应槽中。
    [外链图片转存失败(img-QsP9wQhX-1562839057555)(https://i.imgur.com/q8YUCYx.png)]
  3. 制作产出物件之水果:从3D农产品中拖出几种水果,设置Tag=Food;添加带Trigger的Sphere Collider和带Gravity的Rigibody;同时赋其Object.cs;给水果的子物件分别添加不带Trigger的Sphere Collider;完成后将物件制成Prefab保存后从场景中删除。

Note: 水果的各个Collider尤其要注意范围设置适当。

  1. 制作产出物件之炸弹:从3D Weapon中拖出几种炸弹,设置Tag=NotFood;添加带Trigger的Sphere Collider和带Gravity的Rigibody;同时赋其Object.cs;完成后将物件制成Prefab保存后从场景中删除。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Objects : MonoBehaviour
{
     
    //往某个方向飞的力量值
    float StartForce;

    Rigidbody rigidbodyObject;// * 动态生成物件最好声明为私有
    
    // Start is called before the first frame update
    void Start()
    {
     
        //如果物件没有被削到,过5s消失
        Destroy(gameObject,5f);
        //随机生成一定范围内的力量值
        StartForce=Random.Range(7.5f,8.5f);
        //抓取水果/炸弹物件身上的rigibody
        rigidbodyObject = GetComponent<Rigidbody>();
        //给带有Rigibody的物件一个方向力
        //Vector3.up;//世界坐标y轴
        //transform.up;//物件坐标y轴
        rigidbodyObject.AddForce(transform.up * StartForce,ForceMode.Impulse);
    }
}
  1. 指定GM中的产出物件数目,将制作的产出物件预制物拖至生成的对应槽中。
    [外链图片转存失败(img-VnjOlGzS-1562839057556)(https://i.imgur.com/MGM6vl3.png)]

  2. 完善特效预制:从Prefab中拖出效果预知,分别赋予DestroyObject.cs并指定效果持续时间(假设为2s)后保存修改。

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

public class DestroyObject : MonoBehaviour
{
     
    public float DeletTime;
    
    // Start is called before the first frame update
    void Start()
    {
     
        Destroy(gameObject,DeletTime);
    }
}

  1. 添加武器劈砍产出物件的效果:给之前制作的左右手武器赋予HitObject.cs,指定效果槽数为2,Element0放置刀影效果,Element1放置爆炸效果。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HitObject : MonoBehaviour
{
     
    //透过脚本抓取食物子物件
    Transform[] FoodChildren;
    //水果与爆炸物的特效 0:水果特效;1:爆炸特效
    public GameObject[] EffectPrefab;

    // Start is called before the first frame update
    //void Start(){}

    private void OnTriggerEnter(Collider hit)
    {
     
        //如果刀子碰到食物
        if(hit.GetComponent<Collider>().tag=="Food")
        {
     
            //当刀子砍到水果,防止水果物件重新被触发,将父物件Collider关闭
            hit.GetComponent<Collider>().enabled=false;
            //将父物件的Rigidbody属性删除
            Destroy(hit.transform.GetComponent<Rigidbody>());
            //找寻下一阶层的子物件
            FoodChildren=hit.GetComponentsInChildren<Transform>();
            //给子阶层物件Rigidbody
            FoodChildren[2].gameObject.AddComponent<Rigidbody>();
            FoodChildren[1].gameObject.AddComponent<Rigidbody>();
            //开启子阶层物件Collider
            FoodChildren[2].gameObject.GetComponent<Collider>().enabled=true;
            FoodChildren[1].gameObject.GetComponent<Collider>().enabled=true;
            //传递分数资讯给GM并显示加250分
            GameObject.Find("GM").GetComponent<GM>().totalScore(250);
            //动态产生水果特效
            Instantiate(EffectPrefab[0],hit.transform.position,hit.transform.rotation);
        }

        //如果刀子碰到爆炸物
        if(hit.GetComponent<Collider>().tag=="NotFood")
        {
     
            //传递分数资讯给GM并显示扣150分
            GameObject.Find("GM").GetComponent<GM>().totalScore(-150);
            //动态产生爆炸物特效
            Instantiate(EffectPrefab[1],hit.transform.position,hit.transform.rotation);
            //删除爆炸物件
            Destroy(hit.gameObject);
        }
    }
}

测试

  1. 将任一武器拖至Scene下后运行游戏(Simulator模式下)通过拖动武器检验游戏进行中的水果炸弹生成上抛,武器劈砍,水果切开,炸弹爆炸等效果。
  2. 暂停游戏后开启SteamVR关闭Simulator后继续进行游戏,观察游戏界面分数与时间的显示,以及游戏结束后“Game Over”字体的显示。

参考设置图片

[外链图片转存失败(img-4aKYeDjF-1562839057557)(https://i.imgur.com/oiIzAsR.png)]
[外链图片转存失败(img-QwGYDen7-1562839057558)(https://i.imgur.com/wpUvov1.png)]
[外链图片转存失败(img-cBL7qMHH-1562839057560)(https://i.imgur.com/OU6IzpL.png)]
[外链图片转存失败(img-DZNaXLgh-1562839057560)(https://i.imgur.com/U691syB.png)]
[外链图片转存失败(img-Rd2N3USw-1562839057562)(https://i.imgur.com/R9qHCAH.png)]
[外链图片转存失败(img-Hhtkv2na-1562839057563)(https://i.imgur.com/uytl1RS.png)]
[外链图片转存失败(img-7EdOs8mE-1562839057564)(https://i.imgur.com/zvgtqiF.png)]
[外链图片转存失败(img-4G1nNMXv-1562839057564)(https://i.imgur.com/PZRokx0.png)]
[外链图片转存失败(img-N4qkXNyc-1562839057565)(https://i.imgur.com/q3KXImH.png)]
[外链图片转存失败(img-J7xy38Fs-1562839057566)(https://i.imgur.com/26m8Pib.png)]
[外链图片转存失败(img-B3KReZLR-1562839057568)(https://i.imgur.com/qADuEVE.png)]
[外链图片转存失败(img-2BU4tWe3-1562839057568)(https://i.imgur.com/JunbrGl.png)]
[外链图片转存失败(img-drHL8Eu6-1562839057569)(https://i.imgur.com/0xxjk3x.png)]
[外链图片转存失败(img-JeQP7oMn-1562839057570)(https://i.imgur.com/i4U8U2n.png)]
[外链图片转存失败(img-LtHrid4Q-1562839057570)(https://i.imgur.com/HYxGEN8.png)]

你可能感兴趣的:(Unity,C#,Unity,VR,C#)