VR开发实战HTC Vive项目之说走就走的旅行

一、场景

VR开发实战HTC Vive项目之说走就走的旅行_第1张图片

二、虚拟空间内移动传送

1、实现位置点选取
VR开发实战HTC Vive项目之说走就走的旅行_第2张图片
VR开发实战HTC Vive项目之说走就走的旅行_第3张图片

Tourism_LaserPointer

using UnityEngine;
using System.Collections;


public class Tourism_LaserPointer : MonoBehaviour
{
    public bool active = true;
    public Color color;
    public float thickness = 0.002f;
    public GameObject holder;
    public GameObject pointer;
    bool isActive = false;
    public bool addRigidBody = false;
    public Transform reference;
    public event PointerEventHandler PointerIn;
    public event PointerEventHandler PointerOut;

    public Vector3 HitPoint;
    Material currentCubeMaterial;

    Transform previousContact = null;
    Parabola parabola;

    // Use this for initialization
    void Start ()
    {
        holder = new GameObject();
        holder.transform.parent = this.transform;
        holder.transform.localPosition = Vector3.zero;

        pointer = GameObject.CreatePrimitive(PrimitiveType.Cube);
        pointer.transform.parent = holder.transform;
        pointer.transform.localScale = new Vector3(thickness, thickness, 100f);
        pointer.transform.localPosition = new Vector3(0f, 0f, 50f);
        BoxCollider collider = pointer.GetComponent();
        if (addRigidBody)
        {
            if (collider)
            {
                collider.isTrigger = true;
            }
            Rigidbody rigidBody = pointer.AddComponent();
            rigidBody.isKinematic = true;
        }
        else
        {
            if(collider)
            {
                Object.Destroy(collider);
            }
        }
        currentCubeMaterial = new Material(Shader.Find("Unlit/Color"));
        currentCubeMaterial.SetColor("_Color", color);
        pointer.GetComponent().material = currentCubeMaterial;

        parabola = GetComponent();
    }

    public virtual void OnPointerIn(PointerEventArgs e)
    {
        if (PointerIn != null)
            PointerIn(this, e);
    }

    public virtual void OnPointerOut(PointerEventArgs e)
    {
        if (PointerOut != null)
            PointerOut(this, e);
    }


    // Update is called once per frame
    void Update ()
    {
        if (!isActive)
        {
            isActive = true;
            this.transform.GetChild(0).gameObject.SetActive(true);
        }

        float dist = 100f;

        SteamVR_TrackedController controller = GetComponent();

        Ray raycast = new Ray(transform.position, transform.forward);
        RaycastHit hit;
        bool bHit = Physics.Raycast(raycast, out hit);

        if(previousContact && previousContact != hit.transform)
        {
            PointerEventArgs args = new PointerEventArgs();
            if (controller != null)
            {
                args.controllerIndex = controller.controllerIndex;
            }
            args.distance = 0f;
            args.flags = 0;
            args.target = previousContact;
            OnPointerOut(args);
            previousContact = null;
        }
        if(bHit && previousContact != hit.transform)
        {
            PointerEventArgs argsIn = new PointerEventArgs();
            if (controller != null)
            {
                argsIn.controllerIndex = controller.controllerIndex;
            }
            argsIn.distance = hit.distance;
            argsIn.flags = 0;
            argsIn.target = hit.transform;
            OnPointerIn(argsIn);
            previousContact = hit.transform;
        }
        if(!bHit)
        {
            previousContact = null;
            currentCubeMaterial.SetColor("_Color", Color.black);
            parabola.EndPosion = Vector3.zero;
        }
        else
        {
            currentCubeMaterial.SetColor("_Color", Color.red);
            parabola.EndPosion = hit.point;
        }
        if (bHit && hit.distance < 100f)
        {
            dist = hit.distance;
        }
        if (bHit)
        {
            HitPoint = hit.point;
        }

        if (controller != null && controller.triggerPressed)
        {
            pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist);
        }
        else
        {
            pointer.transform.localScale = new Vector3(thickness, thickness, dist);
        }
        pointer.transform.localPosition = new Vector3(0f, 0f, dist/2f);
    }
}

SteamVR_TrackedController

using UnityEngine;
using Valve.VR;

public struct ClickedEventArgs
{
    public uint controllerIndex;
    public uint flags;
    public float padX, padY;
}

public delegate void ClickedEventHandler(object sender, ClickedEventArgs e);

public class SteamVR_TrackedController : MonoBehaviour
{
    public uint controllerIndex;
    public VRControllerState_t controllerState;
    public bool triggerPressed = false;
    public bool steamPressed = false;
    public bool menuPressed = false;
    public bool padPressed = false;
    public bool padTouched = false;
    public bool gripped = false;

    public event ClickedEventHandler MenuButtonClicked;
    public event ClickedEventHandler MenuButtonUnclicked;
    public event ClickedEventHandler TriggerClicked;
    public event ClickedEventHandler TriggerUnclicked;
    public event ClickedEventHandler SteamClicked;
    public event ClickedEventHandler PadClicked;
    public event ClickedEventHandler PadUnclicked;
    public event ClickedEventHandler PadTouched;
    public event ClickedEventHandler PadUntouched;
    public event ClickedEventHandler Gripped;
    public event ClickedEventHandler Ungripped;

    // Use this for initialization
    void Start()
    {
        if (this.GetComponent() == null)
        {
            gameObject.AddComponent();
        }

        if (controllerIndex != 0)
        {
            this.GetComponent().index = (SteamVR_TrackedObject.EIndex)controllerIndex;
            if (this.GetComponent() != null)
            {
                this.GetComponent().index = (SteamVR_TrackedObject.EIndex)controllerIndex;
            }
        }
        else
        {
            controllerIndex = (uint) this.GetComponent().index;
        }
    }

    public void SetDeviceIndex(int index)
    {
            this.controllerIndex = (uint) index;
    }

    public virtual void OnTriggerClicked(ClickedEventArgs e)
    {
        if (TriggerClicked != null)
            TriggerClicked(this, e);
    }

    public virtual void OnTriggerUnclicked(ClickedEventArgs e)
    {
        if (TriggerUnclicked != null)
            TriggerUnclicked(this, e);
    }

    public virtual void OnMenuClicked(ClickedEventArgs e)
    {
        if (MenuButtonClicked != null)
            MenuButtonClicked(this, e);
    }

    public virtual void OnMenuUnclicked(ClickedEventArgs e)
    {
        if (MenuButtonUnclicked != null)
            MenuButtonUnclicked(this, e);
    }

    public virtual void OnSteamClicked(ClickedEventArgs e)
    {
        if (SteamClicked != null)
            SteamClicked(this, e);
    }

    public virtual void OnPadClicked(ClickedEventArgs e)
    {
        if (PadClicked != null)
            PadClicked(this, e);
    }

    public virtual void OnPadUnclicked(ClickedEventArgs e)
    {
        if (PadUnclicked != null)
            PadUnclicked(this, e);
    }

    public virtual void OnPadTouched(ClickedEventArgs e)
    {
        if (PadTouched != null)
            PadTouched(this, e);
    }

    public virtual void OnPadUntouched(ClickedEventArgs e)
    {
        if (PadUntouched != null)
            PadUntouched(this, e);
    }

    public virtual void OnGripped(ClickedEventArgs e)
    {
        if (Gripped != null)
            Gripped(this, e);
    }

    public virtual void OnUngripped(ClickedEventArgs e)
    {
        if (Ungripped != null)
            Ungripped(this, e);
    }

    // Update is called once per frame
    void Update()
    {
        var system = OpenVR.System;
        if (system != null && system.GetControllerState(controllerIndex, ref controllerState))
        {
            ulong trigger = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_SteamVR_Trigger));
            if (trigger > 0L && !triggerPressed)
            {
                triggerPressed = true;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnTriggerClicked(e);

            }
            else if (trigger == 0L && triggerPressed)
            {
                triggerPressed = false;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnTriggerUnclicked(e);
            }

            ulong grip = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_Grip));
            if (grip > 0L && !gripped)
            {
                gripped = true;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnGripped(e);

            }
            else if (grip == 0L && gripped)
            {
                gripped = false;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnUngripped(e);
            }

            ulong pad = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_SteamVR_Touchpad));
            if (pad > 0L && !padPressed)
            {
                padPressed = true;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnPadClicked(e);
            }
            else if (pad == 0L && padPressed)
            {
                padPressed = false;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnPadUnclicked(e);
            }

            ulong menu = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_ApplicationMenu));
            if (menu > 0L && !menuPressed)
            {
                menuPressed = true;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnMenuClicked(e);
            }
            else if (menu == 0L && menuPressed)
            {
                menuPressed = false;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnMenuUnclicked(e);
            }

            pad = controllerState.ulButtonTouched & (1UL << ((int)EVRButtonId.k_EButton_SteamVR_Touchpad));
            if (pad > 0L && !padTouched)
            {
                padTouched = true;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnPadTouched(e);

            }
            else if (pad == 0L && padTouched)
            {
                padTouched = false;
                ClickedEventArgs e;
                e.controllerIndex = controllerIndex;
                e.flags = (uint)controllerState.ulButtonPressed;
                e.padX = controllerState.rAxis0.x;
                e.padY = controllerState.rAxis0.y;
                OnPadUntouched(e);
            }
        }
    }
}

2、实现游客传送
VR开发实战HTC Vive项目之说走就走的旅行_第4张图片
using UnityEngine;
using System.Collections;

public class Teleport : MonoBehaviour
{
    //手动引用左手柄物体
    public GameObject GoLeft;
    //设置全局变量存储数据
    Tourism_LaserPointer slLeft;
    SteamVR_TrackedController stLeft;
    ClickedEventHandler ce;
    Transform currentTransform;
    PointerEventArgs tp;
    void Start()
    {
        //在Start方法中初始化变量,以注册监听方法
        slLeft = GoLeft.GetComponent();
        stLeft = GoLeft.GetComponent();
        //注册监听事件LeftPointIn(手柄有物体指向事件)LeftPointOut(取消指向事件)TriggerClicked(扳机扣下事件)
        slLeft.PointerIn += LeftPointIn;
        slLeft.PointerOut += LeftPointOut;
        stLeft.TriggerClicked += TriggerClicked;
    }
    void LeftPointIn(object sender, PointerEventArgs e)
    {
        //当有物体指向时设置全局变量标识
        currentTransform = e.target;
    }

    void LeftPointOut(object sender, PointerEventArgs e)
    {
        //取消指向事件时将标识置为空
        currentTransform = null;
    }

    void TriggerClicked(object sender, ClickedEventArgs e)
    {
        //如果有指向物体则调用传送传送至目标位置
        if (currentTransform != null)
        {
            TeleportByPosition(slLeft.HitPoint);
        }
    }

    private void TeleportByPosition(Vector3 targetPosition)
    {
        Debug.Log("targetPosition:" + targetPosition.x + "_" + targetPosition.y + "_" + targetPosition.z);
        //根据之前所得公式计算目标位置移动实际空间
        this.gameObject.transform.position = new Vector3(targetPosition.x - GoLeft.transform.localPosition.x, targetPosition.y, targetPosition.z - GoLeft.transform.localPosition.z);
    }
}

三、多场景编辑

VR开发实战HTC Vive项目之说走就走的旅行_第5张图片
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;

public class TouristCheck : MonoBehaviour
{
    //应用物体当检测条件达到时影藏该物体
    public GameObject DoorObject;
    //加载场景的名称
    public string SceneName;
    //场景异步加载操作控制器
    AsyncOperation asyncOperation;

    void OnTriggerEnter(Collider other)
    {
        //当检测到碰撞时查看碰撞物体是不是主相机,如果是进行场景加载并对asyncOperation赋值进行标记
        if (other.tag == "MainCamera" && asyncOperation == null)
        {
            asyncOperation = SceneManager.LoadSceneAsync(SceneName, LoadSceneMode.Additive);
        }
    }

    void FixedUpdate()
    {
        //通过asyncOperation.isDone来检测场景是否加载完成如果加载完成则将墙影藏并将该物体影藏来展现新场景,同时避免再度触发碰撞进行场景加载
        if (asyncOperation != null && asyncOperation.isDone)
        {
            SceneManager.SetActiveScene(SceneManager.GetSceneByName(SceneName));
            Manager.Instance.StartNewScene(this);
            asyncOperation = null;
            DoorObject.SetActive(false);
        }
    }

    public void Reset()
    {
        //首先将隐藏的墙显示出来
        DoorObject.SetActive(true);
        //然后根据场景名称卸载场景
        SceneManager.UnloadScene(SceneName);
    }
}

VR开发实战HTC Vive项目之说走就走的旅行_第6张图片

VR开发实战HTC Vive项目之说走就走的旅行_第7张图片
VR开发实战HTC Vive项目之说走就走的旅行_第8张图片

四、场景管理类

VR开发实战HTC Vive项目之说走就走的旅行_第9张图片
单例模式
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;

public class Manager : MonoBehaviour {
    public static Manager Instance;
    public Light CurrentLight;
    TouristCheck currentCheck;
    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Debug.LogError("不能重复创建Manager类");
        }
    }

    public void StartNewScene(TouristCheck touristCheck)
    {
        //检测当前是否已经有场景加载,如果没有将调用对象设置为currentCheck
        if (currentCheck == null)
        {
            currentCheck = touristCheck;
        }
        //如果有则调用TouristCheck.Reset方法重置并更新currentCheck
        else if (currentCheck != touristCheck)
        {
            currentCheck.Reset();
            currentCheck = touristCheck;
        }
        CurrentLight = GameObject.Find("Directional Light").GetComponent();
    }
}

五、实现不同时段效果

TimeController

using UnityEngine;
using System.Collections;

public class TimeController : MonoBehaviour {
    SteamVR_TrackedController TrackedController;
    float padX, padY;
    bool isGripp;

    // Use this for initialization
    void Start () {
        //获取控制器脚本
        TrackedController = GetComponent();
        //添加握和松开的监听
        TrackedController.Gripped += Gripped;
        TrackedController.Ungripped += Ungripped;
    }

    //当握时修改全局变量开关
    void Gripped(object sender, ClickedEventArgs e)
    {
        isGripp = true;
    }

    void Ungripped(object sender, ClickedEventArgs e)
    {
        isGripp = false;
    }


    // Update is called once per frame
    void Update () {
        if (isGripp)
        {
            //获取当前触摸位置,如果不为零则根据触摸的x轴来调整平行光朝向
            if (TrackedController.controllerState.rAxis0.x != 0 && TrackedController.controllerState.rAxis0.y != 0)
            {
                float angle = 90 + TrackedController.controllerState.rAxis0.x * 120;
                Manager.Instance.CurrentLight.transform.rotation = Quaternion.AngleAxis(angle, Vector3.right);
            }
        }
    }
}

六、创建传送面和不可传送面

1、区分传送面
VR开发实战HTC Vive项目之说走就走的旅行_第10张图片
VR开发实战HTC Vive项目之说走就走的旅行_第11张图片
2、修改传送点选取方式
VR开发实战HTC Vive项目之说走就走的旅行_第12张图片
VR开发实战HTC Vive项目之说走就走的旅行_第13张图片

实现抛物线效果

using UnityEngine;
using System.Collections;

public class Parabola : MonoBehaviour {
    //传入参数Speed方向shootTransfrom发射器位置StartPosition起始位置EndPosion结束位置GravitationalAcceleration重力加速度lineNodeNum绘制节点数量line绘制抛物线
    public Vector3 Speed;
    public Transform shootTransfrom;

    public Vector3 StartPosition;
    public Vector3 EndPosion;
    public float GravitationalAcceleration = 10;
    public int lineNodeNum = 10;
    public LineRenderer line;
    Vector3[] positions;

    // Use this for initialization
    void Start () {
        //初始化线段绘制节点
        positions = new Vector3[lineNodeNum];
        line.SetVertexCount(lineNodeNum);
    }

    Vector3 GetPlaneVector(Vector3 v3)
    {
        return new Vector3(v3.x, 0, v3.z);
    }
    
    // Update is called once per frame
    void FixedUpdate () {
        //更新发射点位置
        shootTransfrom.position = this.transform.position;
        shootTransfrom.rotation = Quaternion.Euler(this.transform.rotation.eulerAngles.x - 30, this.transform.rotation.eulerAngles.y,0);

        //当结束点为0及没有结束点时将线段回复为直线
        if (EndPosion == Vector3.zero)
        {
            ResetLine();
            return;
        }
        StartPosition = shootTransfrom.position;

        //提前计算出在水平和竖直上的位移
        float Sx = Vector3.Distance(GetPlaneVector(EndPosion),GetPlaneVector(StartPosition));
        float Sy = StartPosition.y - EndPosion.y;
        //计算出竖直方向和水平方向上的初速度比值
        float tanA = -shootTransfrom.forward.y / Vector3.Distance(Vector3.zero, GetPlaneVector(shootTransfrom.forward));
        //根据推导出来的结果计算出运动时间
        float t = Mathf.Sqrt((2 * Sy - 2 * Sx * tanA) / GravitationalAcceleration);

        //判断计算是否有异常
        if (float.IsNaN(t))
        {
            ResetLine();
            return;
        }

        //推导出水平和竖直初速度
        float Vx = Sx / t;
        float Vy = Vx * tanA;

        //最后带出方程绘制出线段。
        float firstLineNodeTime = t / lineNodeNum;
        positions[0] = StartPosition;
        for (int i = 1; i < lineNodeNum; i++)
        {
            float xz = GetX(Vx, firstLineNodeTime * (i + 1));
            float y = GetY(firstLineNodeTime * (i + 1), Vy);
            positions[i] = Vector3.Normalize(GetPlaneVector(shootTransfrom.forward)) * xz + Vector3.down * y + shootTransfrom.position;
        }

        line.SetPositions(positions);
    }

    /// 
    /// 计算水平方向位移
    /// 
    /// 水平方向初速度
    /// 时间
    /// 
    private float GetX(float speed, float time)
    {
        float X = speed * time;
        return X;
    }

    /// 
    /// 计算竖直方向位移
    /// 
    /// 时间
    /// 竖直方向初速度
    /// 
    private float GetY(float time,  float speedDownFloat)
    {
        float Y = (float)(speedDownFloat * time + 0.5 * GravitationalAcceleration * time * time);
        return Y;
    }

    /// 
    /// 将线段重置为一条直线
    /// 
    void ResetLine()
    {
        for (int i = 0; i < lineNodeNum; i++)
        {
            positions[i] = transform.forward * i + transform.position;
        }

        line.SetPositions(positions);
    }
}

七、展示效果

你可能感兴趣的:(VR开发实战HTC Vive项目之说走就走的旅行)