本文基于Player,而非CameraRig
参考文章:https://blog.csdn.net/chh19941125/article/details/90027555
Player:这个预制件是整个系统的核心,大多数其他组件都依赖于player
InteractableExample:这个类是一个和手柄进行简单交互的类。会接收手柄发来的信息并作出反应。由于很多example类中很多方法没用上,挑选了我们所需要方法放在了Core文件中。
UI&Hints:这个显示了交互系统如何处理提示。并且如何使用它和Unity UI控件像按钮一样进行交互。
交互系统的简单介绍
交互系统的核心是Player,Hand和Interactable类。Player预制件为场景设置play对象和SteamVR摄像机。这个交互系统通过发送信息给场景中所有可以和手柄进行交互的对象,这个对象再对接收的信息作出反应。并可以设置对象附着到手柄上。为了接收手的信息只要添加Interactable组件到对象上,这个对象就可以根据手的信息作出相应的悬停监测。Player预制件也创建了一个用于模仿鼠标事件可以简单地用Unity UI控件控制的输入模式。交互系统也包括一个传统的第一人称摄像机,用键盘和鼠标控制。鼠标就类似人的一只手一样。当开发者没有VR头显时,这个模式就非常有用了。
Player类的功能介绍
- Player类是一个单例,意味着在一个场景中只有一个Player对象。
- Player主要功能是持续追踪手柄和头显。
- 它可以在整个项目中全局访问,并且交互系统的许多方面都假定Player对象始终存在于场景中。
- 不管是VR模式还是2D fallback模式,它都保持追踪。
- 通过Player类使用访问器允许其他组件以类似的方式运行,而不知道是否正在使用VR头显或鼠标/键盘。
- 2D fallback模式虽然有用,但仍然有限制。此模式主要用于测试非常简单的交互,仅限于一个手和触发按钮。它对于一个团队在开发中,并不能保证每个人一直都有一个VR头显和控制器设备。
- Player的一些有用的属性:
- hmdTransform:一只返回当前摄像机的transform。可能是VR头显和2D fallback camera。
- feetPositionGuess:根据头显的位置猜测玩家脚的位置。但不能准确知道脚的位置,根据玩家的站立方式,这可能非常不准确。
- bodyDirectionGuess:类似于feetPositionGuess,根据玩家的站立方式,这可能非常不准确。
注意:在编辑场景中,player类设置使用icons显示feet和hands。但由于unity工作方式,需要将Core/icons移动到根目录下Gizmos中,才能起作用。 - 关闭2D fallback mode的两种方式:
1、在build之前,将player的属性面板中Allow Toggle To 2D取消勾选
2、在PlayerSetting.Player中Scripting Define Symbols增加HIDE_DEBUG_UI。这将会在build时取消2D debug view,但允许在编辑器中使用。
Hand类的功能介绍
- Hand类为交互系统完成了大部分繁重工作。
- Hand检测正在悬停的对象(Interactables)并根据当前的悬停状态向它们发送消息。
- hand在同一时间只能悬停在一个对象,在相同时间只能有一只hand能悬停在一个对象。
- 对象可以附着在手柄上,也可以从手柄上分离。只有一个对象可以是手柄的焦点对象,但是同时可以将多个对象附加到手柄上。
- 一旦一个对象从手柄上分离,那么之前附着在手柄上的对象(仍然附着在手柄上)会成为手柄的焦点对象。
- 当手柄上没有任何东西时,它将始终显示手柄控制器。
- 当对象一旦附着在手柄上时,可以给附着在手柄上的对象可以设置一个附着标志物UI来确定手柄和对象的行为。
- 根据情况,可以锁定手柄以悬停在其他物体或任何物体上。
- 这些是hand发送给交互物体的信息:
OnHandHoverBegin: 当手柄首次开始悬停在对象上时发送
HandHoverUpdate: 悬停在对象上时,每一帧都发送信息
OnHandHoverEnd: 当手柄停止悬停在对象上时发送
OnAttachedToHand: 当对象附加到手柄上时发送
HandAttachedUpdate: 附加到手柄上时,每一帧都发送信息
OnDetachedFromHand: 当对象从手柄中分离时发送
OnHandFocusLost: 当附加对象失去焦点时发送,因为其他东西已附加到手柄上
OnHandFocusAcquired: 附加对象获得焦点时发送,因为前一个焦点对象已从手柄中分离 - 这些是手柄发送给其子对象的消息:
OnHandInitialized: 通过将自己与SteamVR跟踪控制器的设备ID相关联来首次初始化手柄时发送
OnParentHandHoverBegin: 当手柄开始悬停在某物上时发送
OnParentHandHoverEnd: 当手柄停止悬停在某物上时发送
OnParentHandInputFocusAcquired: 当游戏窗口获得输入焦点时发送
OnParentHandInputFocusLost: 当游戏窗口失去输入焦点时发送 - 这些成员处理附加和分离:
AttachObject: 将传入的附加标签设置在附着对象上
DetachObject: Detaches the object from the hand and optionally restores it to its original parent currentAttachedObject: This returns the in-focus attached object on the hand,if any - Hand还有一些有用的属性和函数可用于自定义其行为:
OtherHand: 这是玩家控制的另一只手柄。这对于需要用两个手柄交互的对象(例如长弓)非常有用。
HoverSphereTransform and Radius: 这可用于自定义手柄的悬停范围。
HoverLayerMask: 这可以改变,以便手柄只悬停在某些层中的对象上。
HoverUpdateInterval: 根据游戏的要求,可以设置进行悬停检测的时间间隔。
HoverLock/Unlock: 这用于使手柄仅悬停在某个对象上。当hover locked时,传入为null将使得手柄不会悬停在任何东西上。此技术用于在传送arc处于活动状态时使手柄不悬停在对象上。
GetStandardInteractionButton/Up/Down: 这些用于检查手柄上的扳机的状态。 这对于也可以与2D fallback一起使用的简单对象非常有用。 在2D fallback情况下,左键单击用作标准交互按钮,对象的行为相同。
GuessCurrentHandType: 这使用一些SteamVR函数来确定哪个手柄最左边哪个是最右边的。
GetAttachmentTransform: 物体可以使用手上的“attachment transforms”来弄清楚如何附着到手柄上的transform。
层次结构
Directional Light
平行光(方向光) ,最省资源,可以改变角度,可以看作太阳。
其他Light
Point Light:点光源(由一个点向四周发射光源) 可以改变位置 联想:灯泡
Spot Light:聚光灯(聚光/锥光) 最耗费资源 可以改变位置和角度 联想:手电
Area Light:区域光(创造灯光贴图烘焙时使用) 无法应用于实时光照
Study
StudyCore
private Interactable interactable; //交互
public static bool flag = false;
//UI交互部分
private TextMesh textMesh;//本体所带文本框
public string beginText = null; //开始文本
public string processText = null; //过程中文本
//Sound声音部分
public GameObject publicAudioSource;
private AudioSource audioSource; //音乐库
public AudioClip beginSound; //开始音乐
public AudioClip alreadySettedSuccessfully; //训练成功结束音乐
private Hand.AttachmentFlags attachmentFlags = Hand.defaultAttachmentFlags &
(~Hand.AttachmentFlags.SnapOnAttach) &
(~Hand.AttachmentFlags.DetachOthers) &
(~Hand.AttachmentFlags.VelocityMovement); //抓取实现效果
Awake
初始化
void Awake()
{
interactable = this.GetComponent<Interactable>();
//Sound初始化
audioSource = publicAudioSource.GetComponent<AudioSource>();
Soundplayer(audioSource, beginSound);
//UI初始化
textMesh = GetComponentInChildren<TextMesh>();
//textMesh.fontSize = 240;
textMesh.color = new Color32(248, 241, 22, 255);
textMesh.text = beginText;
}
Interactable组件
所有交互最基础的组件(所有要交互的UI、物体必添加)。添加后相当于告诉系统,该物体可以与手柄进行交互。
ControllerHoverHighlight组件
LeftHand和RightHand表示左右手,其下的ControllerHoverHighlight组件,包含高亮效果的Material,更改其Material便可改变手柄高亮效果。
FireHapticsOnHightlight
勾选后,当手柄接触物体时,手柄高亮、触发震动。
还可以通过下列代码来控制手柄震动时长。
...GetComponent<LeftHand>().controller.TriggerHapticPulse(500);
OnHandHoverBegin
当手柄接触到物体时(事件触发,一帧);
/// <summary>
/// 触碰但未抓取
/// </summary>
/// <param name="hand"></param>
private void OnHandHoverBegin(Hand hand)
{
if (textMesh != null)
{
textMesh.color = new Color32(23, 219, 57, 255);
textMesh.text = beginText;
}
}
OnHandHoverEnd
悬停结束时调用(类似OnTriggerExit,一帧)
OnAttachedToHand
附加到手柄上时调用
/// <summary>
/// 正在抓着物品
/// </summary>
/// <param name="hand"></param>
private void OnAttachedToHand(Hand hand)
{
StudyFlag.studyFlag = true;
Rigidbody rigidbodyOfAttachedObject = hand.currentAttachedObject.GetComponentInChildren<Rigidbody>();
Transform transformOfAttachedObject = hand.currentAttachedObject.GetComponentInChildren<Transform>();
rigidbodyOfAttachedObject.drag = 10000;
rigidbodyOfAttachedObject.angularDrag = 10000;
if (textMesh != null)
{
textMesh.color = new Color32(23, 219, 57, 255);
textMesh.text = processText;
}
else
{
Soundplayer(audioSource, alreadySettedSuccessfully);
Flag.operationCount++;
}
}
OnDetachedFromHand
从手柄上分离时调用(一帧)
/// <summary>
/// 放下物品
/// </summary>
/// <param name="hand"></param>
private void OnDetachedFromHand(Hand hand)
{
Rigidbody rigidbodyOfAttachedObject = hand.currentAttachedObject.GetComponentInChildren<Rigidbody>();
Transform transformOfAttachedObject = hand.currentAttachedObject.GetComponentInChildren<Transform>();
rigidbodyOfAttachedObject.drag = 1;
rigidbodyOfAttachedObject.angularDrag = 1;
}
HandHoverUpdate
附加到手柄上时,每一帧都发送信息
private void HandHoverUpdate(Hand hand)
{
GrabTypes startingGrabType = hand.GetGrabStarting();
bool isGrabEnding = hand.IsGrabEnding(this.gameObject);
if (interactable.attachedToHand == null && startingGrabType != GrabTypes.None)
{
hand.HoverLock(interactable);
hand.AttachObject(gameObject, startingGrabType, attachmentFlags);
}
else if (isGrabEnding)
{
hand.DetachObject(gameObject);
hand.HoverUnlock(interactable);
}
}