Unity HTC Vive实现物体受手柄触发,方法并显示在用户朝向方向

在VR虚拟场景搭建的项目中,需要实现,三维物体部分放大的效果,展示不同组成部分的介绍功能,同时便于观察。

我设想通过手柄触碰目标物体,扣动扳机触发事件,目标物体放大,其他部分隐藏,关闭渲染。在放大的物体上扣动扳机,物体返回原样。同时,在物体放大时,无论用户在什么位置,物体显示在用户的实现朝向的方向,即用户面前。

编写脚本,继承VRTK_InteractableObject类(steamVR插件VRTK中,用来控制手柄和物体交互的脚本)。

设置参数,例如移动后的高度,距离camera Rig位置面前的距离,移动后的scale大小,移动的时间等等。

枚举物体的状态,我设置了三种 idle moving placed

	//物体状态
	private enum State { IDLE, MOVING, PLACED }

移动的过程中moving,初始状态idle,移动后放大等效果为placed。

定义变量,开始时为IDLE状态,记录物体开始时的初始位置originPosition。Pivot差值。头显的数据等等。

	void Start(){
		state = State.IDLE;
		originPosition = transform.position;
		pivot = new Vector3 (0, Pivot, 0);
		scaleDelta = new Vector3 (Scale, Scale, Scale) / SpeedFrame;
		headset = VRTK_DeviceFinder.HeadsetTransform ();
	}

VRTK_DeviceFinder类:用于在场景中孕照左右手柄,头显,返回硬件编号,位置信息等。

除HeadsetTransform等,其他重要的API:

获取左右手柄的游戏物体

VRTK_DeviceFinder.GetControllerRightHand(); 
VRTK_DeviceFinder.GetControllerLiftHand();

获得左右手柄对应的硬件编号

VRTK_DeviceFinder.GetControllerIndex(rightHand)


继承VRTK_InteractableObject类,重构StartUsing方法,按下Trigger是进行状态转换

public override void StartUsing(GameObject currentUsingObject) {
		base.StartUsing(currentUsingObject);
		switch (state) {
		case State.IDLE: {
				StartCoroutine(moveToPlayArea());
			}
			break;
		case State.MOVING:
			break;
		case State.PLACED: {
				StartCoroutine(moveBack());
			}
			break;
		}
	}

将物体移动到用户的面前:

private IEnumerator moveToPlayArea() {
		foreach (ModelInteractableObject o in models) {
			if (o.state == State.PLACED) {
				StartCoroutine (o.moveBack ());
			} else if (o.state == State.MOVING) {
				yield return new WaitWhile(() => o.state == State.MOVING);
			}
		}
		state = State.MOVING;
		Vector3 position = headset.position;
		position.y = Height;
		position += new Vector3(headset.forward.x, 0, headset.forward.z) * Distance;
		Vector3 dirDelta = (position - (transform.position + pivot)) / SpeedFrame;
		for(int i = 0; i < SpeedFrame; i++) {
			transform.Translate (dirDelta , Space.World);
			transform.localScale -= scaleDelta;
			yield return new WaitForEndOfFrame();
		}
		
		state = State.PLACED;
	}

物体三种状态的转换,根据头显的位置,确定物体移动的位置。高度,距离变量控制。speedFrame控制时间。通过Translate方法移动。

将物体移动回原来的位置:

private IEnumerator moveBack() {
		state = State.MOVING;
		Vector3 dirDelta = (originPosition - transform.position) / SpeedFrame;
		for(int i = 0; i < SpeedFrame; i++) {
			transform.Translate (dirDelta , Space.World);
			transform.localScale += scaleDelta;
			yield return new WaitForEndOfFrame();
		}
		state = State.IDLE;
	}

与上面方法同理。


源码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRTK;
using VRTK.Highlighters;

public class ModelInteractableObject : VRTK_InteractableObject {

	[Header("Model Interactable Object")]

	[Tooltip("移动面前的距离")]
	public float Distance = 3f;
	[Tooltip("移动面前的高度")]
	public float Height = 1f;
	[Tooltip("移动面前的Scale")]
	public float Scale = 0.5f;
	[Tooltip("移动到目标位置的时间,以帧为单位")]
	public float SpeedFrame = 30.0f;
	[Tooltip("Pivot差值")]
	public float Pivot = 0;
	[Tooltip("FloorInfoPanel用于显示楼层信息")]
	public ModelInfoPanel FloorInfoPanel;
	[Tooltip("FloorInfoPanel中显示的信息")]
	public string Info;

	private enum State { IDLE, MOVING, PLACED }

	private State state;
	private Vector3 originPosition;
	private Vector3 pivot;
	private Vector3 scaleDelta;
	private Transform headset;

	private GameObject textPanel;

	private static ModelInteractableObject[] _models;
	private static ModelInteractableObject[] models {
		get {
			if (_models == null) {
				_models = FindObjectsOfType ();
			}
			return _models;
		}
	}

	void Start(){
		state = State.IDLE;
		originPosition = transform.position;
		pivot = new Vector3 (0, Pivot, 0);
		scaleDelta = new Vector3 (Scale, Scale, Scale) / SpeedFrame;
		headset = VRTK_DeviceFinder.HeadsetTransform ();
	}

	//按下Trigger时进行状态转换
	public override void StartUsing(GameObject currentUsingObject) {
		base.StartUsing(currentUsingObject);
		switch (state) {
		case State.IDLE: {
				StartCoroutine(moveToPlayArea());
			}
			break;
		case State.MOVING:
			break;
		case State.PLACED: {
				StartCoroutine(moveBack());
			}
			break;
		}
	}

	//触摸时打开高亮
	public override void OnInteractableObjectTouched (InteractableObjectEventArgs e) {
		if (state == State.PLACED || state == State.MOVING) {
			ToggleHighlight (false);
		}
		base.OnInteractableObjectTouched (e);
	}

	//开始触摸,显示手柄的高亮和Tooltips
	public override void StartTouching (GameObject currentTouchingObject) {
		base.StartTouching (currentTouchingObject);
		VRTK_ControllerActions action = currentTouchingObject.GetComponent ();
		action.ToggleHighlightTrigger (true, Color.yellow);
		action.SetControllerOpacity (0.5f);
	}

	//停止触摸,关闭手柄高亮和Tooltips
	public override void StopTouching (GameObject previousTouchingObject) {
		base.StopTouching (previousTouchingObject);
		///FloorInfoPanel.gameObject.SetActive (false);

		VRTK_ControllerActions action = previousTouchingObject.GetComponent ();
		action.ToggleHighlightTrigger (false);
		action.SetControllerOpacity (1f);
	}

	private IEnumerator moveToPlayArea() {
		foreach (ModelInteractableObject o in models) {
			if (o.state == State.PLACED) {
				StartCoroutine (o.moveBack ());
			} else if (o.state == State.MOVING) {
				yield return new WaitWhile(() => o.state == State.MOVING);
			}
		}
		state = State.MOVING;
		Vector3 position = headset.position;
		position.y = Height;
		position += new Vector3(headset.forward.x, 0, headset.forward.z) * Distance;
		Vector3 dirDelta = (position - (transform.position + pivot)) / SpeedFrame;
		for(int i = 0; i < SpeedFrame; i++) {
			transform.Translate (dirDelta , Space.World);
			transform.localScale -= scaleDelta;
			yield return new WaitForEndOfFrame();
		}
		state = State.PLACED;
	}

	private IEnumerator moveBack() {
		state = State.MOVING;
		Vector3 dirDelta = (originPosition - transform.position) / SpeedFrame;
		for(int i = 0; i < SpeedFrame; i++) {
			transform.Translate (dirDelta , Space.World);
			transform.localScale += scaleDelta;
			yield return new WaitForEndOfFrame();
		}
		state = State.IDLE;
	}
}





你可能感兴趣的:(HTCVive,Unity3D,C#)