使用Unity3D在VR中捕获对象的一种改进方法

VR中执行的最常见的动作之一就是取走,移动,旋转和扔掉对象,因此在VR开发中进行编程的通常动作之一就是处理此类交互的一致方式。

我已经与Unity合作多年,开发视频游戏和VR应用程序,并且尝试了多种抓取对象的方法,在解释最近发现的更好的新方法之前,我将以利弊方式对其进行解释。

育儿+运动刚体

如果您以前使用Unity3D进行开发,首先想到的就是这个想法。 抓住物体时,使其成为玩家手的子代并将其设置为运动学的(不受物理影响)。

grabbedGameObject.transform.parent = hand.transform; 
grabbedGameObject.getComponent().isKinematic = true;

效果很好,物体可以顺畅地跟随手的位置和旋转,但是它具有不便的运动刚体,只能检测到刚体碰撞器的碰撞,如下表所示。

表格来自:https://docs.unity3d.com/Manual/CollidersOverview.html

刚体对性能不利,应将其用于模拟物理,并且应牢记仅将其用于此目的。 因此,我们应该将“刚体”用于玩家可以抓取和投掷的物体,而不是用于墙壁和其他静态物体。

当然,您可以将对象设置为非运动学的,但是每次碰撞时,它将使它在玩家的手上移动。 对于休闲体验而言这可能很有趣,但对于真正的严肃游戏体验而言却毫无意义。 想象一下在VR中使用一把剑,剑的每次击中都错开,最后剑指向玩家的手。

但是,我们真的需要与“静态碰撞器”和其他运动刚体碰撞吗?

好吧,这取决于,也许您不需要它,如果是这种情况,您可以采用这种方法,但是我想用一些细节来改善我的体验/游戏,例如当玩家用石块击中石墙上的一些火花粒子时具有足够力量的金属武器。 如果您想改善自己的游戏/ VR体验,请继续阅读!

固定关节

另一种方法是使用固定接头。

从Unity手册中:

固定关节将物体的运动限制为取决于另一个物体。 这有点类似于育儿,但是是通过物理而非变换层次结构实现的。 使用它们的最佳方案是当您有想要轻松地彼此分离的对象,或无需父母就可以连接两个对象的运动时。”

这样,您可以向可抓取对象中添加“固定关节”组件,并在抓取对象时将“固定关节”组件的connectedBody公共变量设置为手。

 grabbedGameObject.getComponent().connectedBody = hand.getComponent(); 

这里的问题是双手需要一个刚体组件。 更糟糕的是,固定关节物理学始终与对象模拟物理学“战斗”,这会引起一些奇怪的行为。

约束条件

另一种方法是使用约束。

从Unity手册中:

“约束组件链接游戏对象的位置,旋转或比例。 Unity场景中的基本对象,可以表示角色,道具,风景,摄像机,路标等。 GameObject的功能由与其相连的组件定义。 更多信息
参见术语表中的另一个GameObject。 受限的GameObject会像链接到的GameObject一样移动,旋转或缩放。”

我发现几个月前,我认为这可能是解决我的对象捕获问题的正确方法。 具体来说,我尝试了“父母约束”。 这就是Unity手册对此所说的:

“父约束移动和旋转GameObject
就像它是“层次结构”窗口中另一个GameObject的子级一样。 但是,它提供了某些优势,而当您使一个GameObject成为另一个的父对象时,这些优势是无法实现的:

  • 父约束不会影响比例。
  • 父约束可以链接到多个GameObject。
  • GameObject不必是父约束链接到的GameObjects的子代。
  • 您可以通过指定权重及其每个源GameObjects的权重来改变“约束”的效果。

例如,要将剑放置在角色的手中,请将“父约束”组件添加到剑GameObject。 在“父约束”的“源”列表中,链接到角色的手。 这样,剑的运动被限制在手的位置和旋转上。”

有了这个例子,可能有什么问题?

显然,我尝试了成功。 它或多或少像固定关节一样工作,但是没有物理棘手的东西。 我试图编写自己的代码来复制玩家的手的位置和旋转到被抓取的物体之前,但效果不佳,我认为约束可以这样工作,但它们可能在所需的正确时间执行,因为它们是引擎的一部分。

问题。 约束比固定关节好得多,但是似乎它们也可以与刚体物理进行战斗,并且逐帧调试我意识到,有时碰撞后,对象碰撞器会跳到奇怪的位置。 那触发了玩家周围的虚假碰撞,这很烦人。

第三方插件

在解释我最近的发现之前,我想指出,有很多插件可以改善开发人员的生活。 在Unity Asset Store中,您可以找到它们。 我不知道他们是否解决了这个问题,但是如果您想快速工作并且不介意与他人的代码体系结构捆绑在一起(我介意,并且我更喜欢在可以避免的情况下不使用插件,因为那取决于您,但这是另一个故事),您可以尝试。

TrackedPoseDriver和BasePoseProvider

改进的抓取对象的新方法。

最近,我尝试使用TrackedPoseDriver组件,该组件用于制作游戏Camera和Hand GameObjects跟随Headset和Controller,以在Unity中抓取对象。

当玩家抓住并反对时,会发生什么:

 grabbedGameObject.transform.parent = null; 
grabbedGameObject.transform.position = Vector3.zero;
grabbedGameObject.transform.rotation = Quaternion.identity;
 TrackedPoseDriver tpd = grabbedGameObject.AddComponent(); 
tpd.UseRelativeTransform = true;
tpd.poseProviderComponent = hand.PoseProvider;

该代码做什么?

  • 首先,该对象是脱亲的(以防万一,我将其他对象作为父对象),并将其设置在位置0,0,0且不旋转。 投掷时,物体将重新定位到手的位置,并且也会随着手的旋转而旋转。
  • 然后,代码将“跟踪姿势驱动程序”组件添加到该对象,该对象在抛出对象时必须销毁。 我尝试禁用它,但是它会一直跟踪位置和旋转,因此我需要每次添加和销毁它。
  • 将“姿势提供者”组件设置为“跟踪的姿势驱动程序”使它跟随姿势提供者的位置和旋转。
  • 姿势提供者是双手需要的新组件。 它基本上是一个继承自BasePoseProvider并实现其“ TryGetPoseFromProvider”方法的组件,该方法从“跟踪姿势驱动程序”中用作跟随父项。

Pose Provider的代码简化(我使用偏移量以不同方式抓取对象,但此处使用手旋转和定位):

 using UnityEngine; 
using UnityEngine.Experimental.XR.Interaction;
 namespace SimpleVR 
{
public class PoseProvider : BasePoseProvider
{
public override bool TryGetPoseFromProvider(out Pose output)
{
output = new Pose(transform.position, transform.rotation);
return gameObject.activeInHierarchy;
}
}
}

重要的是要说BasePoseProvider是实验性的并且可以更改(肯定会从UnityEngine.Experimental.XR.Interaction迁移)。

使用这种新方法,所有操作似乎都可以完美运行,并且可以与墙壁和其他静态碰撞器碰撞,并且无需进行奇怪的物理模拟。

结论

我不知道这是否是目前使用Unity3D在VR中抓取对象的最佳方法,但这是我能够弄清的最佳方法。 我将继续研究并尝试新事物,以提高自己的技能和游戏水平。

希望您发现这篇文章对您有帮助,如果您有任何疑问,请随时在此处询问或发送电子邮件至daniel.c.estrella@gmail.com

感谢您的时间!