HTC VIVE 基础开发1

此文章用于自己学习过程中的记录,以便日后翻阅


开发HTC VIVE 首先需要下载Steam 平台,然后在Steam的商店里搜索Steam VR下载安装就可以了。


创建新的Unity项目

创建一个新的Unity工程




下载Steam VR插件

打开unity的 Asset Store 




在Unity中的商店里搜索Steam vr插件


下载完成后点击Import按钮




将下载好的Steam VR插件全部导入到新的Unity工程中



导入中会有绿色的读条,如果导入完成后弹出API Update Required,这是在提示你API更新,可以不用管他,点击No Thanks即可。(原因应该是Steam VR 插件的版本不是最新的)


等待SteamVr_Settings弹出后点击Accept All。到这里导入就完成了



实现对手柄的控制

打开SteamVR_LaserPointer,在这里实现对激光笔的控制功能,首先把SteamVR_LaserPointer

Ctrl+d 复制一份,然后再该脚本的基础上进行修改


新建一个文件夹命名为Script,把SteamVR_LaserPointer脚本重命名为LaserPointer,然后拖入新建的Script文件夹中


进入LaserPointer脚本,然后修改类名为LaserPointer



删除掉选中的蓝色部分,然后保存



制作激光笔选中的目标点




在void Update 找到代码判断的语句进行扩充,并执行Hit判断,把Hit到的点存到刚刚创建的HitPoint变量中


在SteamVr文件夹中找到Prefabs文件夹然后把CameraRig放到场景中去


在场景中用Cube搭建一个简单的房间,然后把CameraRig的位置进行调整


展开CameraRig,选择Controller(left)对左手的手柄进行控制操作


给Controller(left)添加修改的Laser Point 赋予给CameraRig和SteamVR_TrackedController

然后去掉TrackSteamVR_Tracked Object



到了这一步,如果你的HTC VIVE设备调试正确,那就能在Unity的场景中看到他们了。






创建传送的脚本

创建Teleport

复制脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Teleport : MonoBehaviour {
    //手柄的引用
    public GameObject Left;
    //储存一些变量
    LaserPointer LP_Left;
    SteamVR_TrackedController ST_Left;
    Transform CurrentTransform;

    
	// Use this for initialization
	void Start () {
        //初始化变量  注册监听  获取激光笔和控制器
        LP_Left = Left.GetComponent<LaserPointer>();
        ST_Left = Left.GetComponent<SteamVR_TrackedController>();
        LP_Left.PointerIn += LeftPointIn;//手柄指向事件
        LP_Left.PointerOut += LeftPointOut;//手柄取消事件
        ST_Left.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)
        {
            TeleportPosition(LP_Left.HitPoint);
        }
    }

    //移动的方法
    private void TeleportPosition(Vector3 targetPosition)
    {
        this.gameObject.transform.position = new Vector3(targetPosition.x - Left.transform.localPosition.x, targetPosition.y, targetPosition.z - Left.transform.localPosition.z);
    }
    // Update is called once per frame
    void Update () {
		
	}
}


赋予Teleport脚本给CameraRig组件,然后把Controller(left)赋值到Left



到这一步就完成了传送功能




实现多场景加载

创建一个新的Check脚本用来检测


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

public class Check : MonoBehaviour {

    //检测 条件完成 达到时 隐藏
    public GameObject _Object;
    // 加载场景名字
    public string SceneName;
    //异步加载控制器
    AsyncOperation _AsyncOperation;

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

    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void FixedUpdate () {

        //通过_AsyncOperation.isDone 来检测场景是否加载完成  如果加载完成 就隐藏特殊物体 来展现新的场景 同时避免再度触发碰撞进行场景加载
        if (_AsyncOperation!=null && _AsyncOperation.isDone)
        {
            _AsyncOperation = null;
            _Object.SetActive(false);
        }
	}
}

把Check 赋予给一面墙(创建一个CUBE拉成一面墙的样子,既点击墙面之后跳转的场景),然后对这个墙壁添加BoxCollider组件,BoxCollider组件大小为包裹住墙壁。然后勾选IsTrigger(勾上代表触发器)





在CameraRig上的Camera(eye)上添加BoxCollider 组件,同意勾选上IsTrigger 选项


然后添加Rigidbody组件取消勾选UseGravity



创建场景管理

创建一个新的脚本 Manager 用来管理你的场景

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

public class Manager : MonoBehaviour {

    public static Manager Instance;

    Check CurrentCheck;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Debug.LogError("不能重复创建Manager");
        }
    }

    public void StartNewScene(Check _Check)
    {
        //检测当前是否有场景加载 如果没有将调用对象设置为currentCheck

        if (CurrentCheck == null)
        {
            CurrentCheck = _Check;
        }else if (CurrentCheck != _Check)//如果有  就调用CurrentCheck.Reset方法重置 并且更新CurrentCheck调用
        {
            CurrentCheck.Reset();
            CurrentCheck = _Check;
        }
    }
 
}

回到Check脚本,添加两行新的代码


然后在Check脚本中添加Reset方法


回到Unity里创建一个GameObject,然后把它的位置Reset,重命名为Manager,然后把Manager脚本挂上去


现在不会两个场景一起显示了,只会显示其中一个

到这里就完成了场景卸载的功能




制作不可传送区域

需要对传送进行限制,例如不能传送到界外,或者不能传送到屋顶或者水里了

例如把水面设置为不可传送区域,在搜索栏中搜索命名为water的所有模型(前提是你的模型的名字为water,如果是别的就搜索你模型的名字)



然后把Layer层的Default全部设置为Ignore Raycast


到这里就无法选择水面进行移动了。但是还没有明显的禁止移动提示。

接下来需要把禁止移动的提示制作的更明显

打开LaserPoint脚本进行编辑,需要先添加一个Material 来控制射线的颜色


因为上面定义了新的Material ,所以把下面的删掉


在指向判断这里添加新的更改,把可以只想设置为绿色,否则设置为红色



到这里就完成了点击不可传送地区射线变红的功能


制作抛物线---把激光笔的直线更改为抛物线


创建一个新的脚本parabola

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

public class Parabola : MonoBehaviour {

    //发射器位置
    public Transform ShootTransform;
    //起点
    public Vector3 StartPosition;
    //终点
    public Vector3 EndPosition;
    //重力加速度
    public float GravitationalAcceleration = 10;
    //绘制节点数量
    public int LineNodeNum = 10;
    //绘制抛物线
    public LineRenderer Line;

    Vector3[] Position;


	// Use this for initialization
	void Start () {
        //初始化线段绘制节点
        Position = 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 () {
        //更新发射点的位置(曲线)
        ShootTransform.position = this.transform.position;
        ShootTransform.rotation = Quaternion.Euler(this.transform.rotation.eulerAngles.x - 30, this.transform.rotation.eulerAngles.y, 0);
        //当结束点为0但没有结束点的时候 将线段恢复为直线
        if (EndPosition == Vector3.zero)
        {
            ResetLine();
            return;
        }
        StartPosition = ShootTransform.position;
        //计算出水平和垂直上的位移
        float Sx = Vector3.Distance(GetPlaneVector(EndPosition), GetPlaneVector(StartPosition));
        float Sy = StartPosition.y - EndPosition.y;
        //计算出垂直方向和水平方向上的初始速度比值
        float tanA = -ShootTransform.forward.y / Vector3.Distance(Vector3.zero, GetPlaneVector(ShootTransform.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;
        Position[8] = StartPosition;
        for (int i = 1; i < LineNodeNum; i++)
        {
            float xz = GetX(Vx, FirstLineNodeTime * (i + 1));
            float y = GetY(FirstLineNodeTime * (i + 1), Vy);
            Position[i] = Vector3.Normalize(GetPlaneVector(ShootTransform.forward)) * xz + Vector3.down * y + ShootTransform.position;


        }
        Line.SetPositions(Position);

    }
    /// <summary>
    /// 计算水平方向的位移
    /// </summary>
    /// <param name="Speed">水平方向初速度</param>
    /// <param name="time">时间</param>
    /// <returns></returns>
    private float GetX(float Speed,float time)
    {
        float X = Speed * time;
        return X;
    }

    /// <summary>
    /// 计算垂直方向的位移
    /// </summary>
    /// <param name="time">时间</param>
    /// <param name="SpeedDownFloat">垂直方向的初速度</param>
    /// <returns></returns>
    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++)
        {
            Position[i] = transform.forward * i + transform.position;
            Line.SetPositions(Position);
        }
    }

}













回到LaserPointer脚本里去抛物线的处理












创建一个GameObject命名为Shooter,再创建一个命名为ShootLine



对ShootLine添加Line Renderer组件,然后创建一个材质球赋予它(颜色随意)



找到Controller (left)添加刚刚写好的抛物线脚本,然后把刚刚的Shooter、ShootLine指定过来


可以增加LineNodeNum的节点数让抛物线更平滑

到这里就实现了抛物线


这时由于激光笔自带的射线和制作的抛物线同时存在,需要把内置的直线取消掉,打开LaserPointer脚本注销掉所有pointer相关的代码







全部注销后激光笔内置的直线就消失了,只剩下抛物线。

启用单例模式制作抛物线颜色

在Parabola脚本中添加一个新的单例模式 用来改变抛物线的颜色




回到LaserPointer脚本,在指向判断处添加控制颜色的代码


回到Unity运行测试,如果可以移动的抛物线则为绿色,否则为红色直线-------文章结束

猜你喜欢

转载自blog.csdn.net/weixin_39096278/article/details/80943706
今日推荐