以抓取物体为例
1.获取手柄引用
用GetComponent获取手柄引用作为跟踪物体
2.手柄与物体的碰撞检测
手柄需要添加colider组件并设置为trigger,碰撞物体需要添加rigidbody和colider组件
3.获取按钮事件
用device判断手柄按键是否按下
4.抓取过程:物体作为手柄的transform子物体,失去rigidbody相关属性
5.松开过程:物体的parent为空,重新获取自身rigidbody相关属性
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InteractWithBox : MonoBehaviour
{
private SteamVR_TrackedObject trackedObject;
private SteamVR_Controller.Device device;
private GameObject interactBox;
// Start is called before the first frame update
void Start()
{
//获取手柄引用
trackedObject = GetComponent();
device = SteamVR_Controller.Input((int)trackedObject.index);
}
// Update is called once per frame
void Update()
{
if(device != null)
{
return;
}
if(device.GetAxis().x!=0||device.GetAxis().y!=0)
{
Debug.Log(device.GetAxis().x + " | " + device.GetAxis().y);
}
//判断Trigger按下
if(device.GetPressDown(SteamVR_Controller.ButtonMask.Trigger))
{
if(interactBox != null)
{
interactBox.transform.parent = transform;
Rigidbody rig = interactBox.GetComponent();
rig.useGravity = false;//取消重力
rig.isKinematic = true;//开启动力学,不受自身影响,只跟随手柄运动
}
}
//判断Trigger松开
if(device.GetPressUp(SteamVR_Controller.ButtonMask.Trigger))
{
interactBox.transform.parent = null;
Rigidbody rig = interactBox.GetComponent();
rig.useGravity = true;
rig.isKinematic = false;
}
}
//碰撞体进入处理函数
private void OnTriggerEnter(Collider other)
{
interactBox = other.transform.gameObject;
}
//碰撞体脱离处理函数
private void OnTriggerExit(Collider other)
{
interactBox = null;
}
//碰撞体碰撞过程中手柄震动
private void OnTriggerStay(Collider other)
{
device.TriggerHapticPulse(700);
}
}
以凝视一个Button为例子
1.放置场景,添加Canvas时需要将Render Mode设置为World Space,在Canvas下添加Botton,并添加box colider组件
2.添加准星GUI,将Texture Type(贴图类型)设置为Sprite转化为image显示
3.将准星设置为Camera(eye)的子物体,Camera(eye)—Canvas—image(Reticle)—copy image(Background),把准星的image拖到Source Image中
4.通过准星的环形填充来充当倒计时的功能,在Image组件的ImageType下可以选择Filled填充方式,Fill Amount从0到1的变化就是环形填充的过程,将背景的透明度降低
5.通过代码脚本实现检测视线凝视,并添加准星贴图,解决近大远小问题
6.用shader中的UIOverlay添加到一个Material材质中拖到准星和背景的image上解决准星贴图在游戏物体上闪烁问题
7.再写一个脚本用于响应凝视的物体
8.用不同的tag区分UI和物体,实现不同的响应,把脚本放到需要凝视响应的物体上
9.为物体添加高亮显示,先给物体一个Material,再凝视过程中替换Material
VRGazeItem.cs,在脚本中添加mat变量
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class VRGazeItem : MonoBehaviour
{
public Material highlightMat;//高亮材质
public Material normalMat;//正常材质
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
//视线移入处理函数
public void OnGazeIn()
{
if (gameObject.tag == "GazeUI")
{
//传递一个视线移入的消息,类似鼠标移入
ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerEnterHandler);
}
else if (gameObject.tag =="GazeObj")
{
gameObject.GetComponent().material = highlightMat;
}
}
//视线移出处理函数
public void OnGazeOut()
{
if (gameObject.tag == "GazeUI")
{
//传递一个视线移出的消息,类似鼠标移出
ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerExitHandler);
}
else if (gameObject.tag == "GazeObj")
{
gameObject.gameObject.GetComponent().material = normalMat;
}
}
//视线停留处理函数
public void OnGazeFire(RaycastHit hit)
{
if (gameObject.tag == "GazeUI")
{
//填写凝视时实现的效果
}
else if (gameObject.tag == "GazeObj")
{
//在击中点施加一个力
gameObject.GetComponent().AddForceAtPosition(hit.point.normalized * 100, hit.point);
}
}
}
GazeController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GazeController : MonoBehaviour
{
public Canvas reticleCanvas;
public Image reticleImage;
private GameObject target;
private Vector3 originPos;//初始位置
private Vector3 originScale;//初始缩放
private float countDownTime = 3;//倒计时时间
private float nowTime = 0;//当前时间
// Start is called before the first frame update
void Start()
{
//记录初始位置和初始缩放
reticleImage.fillAmount = 0;//默认
originPos = reticleCanvas.transform.localPosition;
originScale = reticleCanvas.transform.localScale;
}
// Update is called once per frame
void Update()
{
Ray ray = new Ray(transform.position, transform.forward);//视线
RaycastHit hit;//记录视线击中点
if(Physics.Raycast(ray, out hit))//检测视线击中点
{
//将准星置于击中点的位置
reticleCanvas.transform.position = hit.point;//将准星位置放置在击中点
reticleCanvas.transform.localScale = originScale * hit.distance;//解决准星近大远小的问题
reticleCanvas.transform.forward = hit.normal;//解决准星与摄像机平面垂直的问题,让准星的forward方向与击中点的法线方向保持一致
if(hit.transform.gameObject != target)
{
//先移出原来有凝视效果的物体
if(target != null)
{
VRGazeItem oldItem = target.GetComponent();
if(oldItem)
{
oldItem.OnGazeOut();
}
}
//视线初次进入的处理
target = hit.transform.gameObject;
VRGazeItem newItem = target.GetComponent();//记录有凝视效果的物体
if(newItem)
{
newItem.OnGazeIn();
}
}
else//视线在此处停留
{
nowTime += Time.deltaTime;//累加得到当前时间
if((countDownTime - nowTime) > 0)
{
reticleImage.fillAmount = nowTime / countDownTime;//未达到激活条件进行倒计时,环形填充动画
}
else
{
//达到激活条件
VRGazeItem gazeFireItem = target.GetComponent();
if(gazeFireItem)
{
gazeFireItem.OnGazeFire(hit);
}
nowTime = 0;
}
}
}
else
{
//恢复准星位置
reticleCanvas.transform.localPosition = originPos;
reticleCanvas.transform.localScale = originScale;
reticleCanvas.transform.forward = Camera.main.transform.forward;
reticleImage.fillAmount = 0;
}
}
}