其他
NewtonVR丨基于真实物理学的Vive VR游戏交互革新(上)
导读:因为本文篇幅比较长,我们将分成上中下(三篇)在GAD微信公众号里连续三天推送
大家好,我叫Nick,是Tomorrow Today实验室的一名虚拟现实开发工作者。我们正在致力于使用Unity3D游戏开发引擎开发一款尚未公布的VR游戏。同各位虚拟现实开发领域的同仁一样,我在开发过程中一直在迎接各种新奇独特的挑战,并反复尝试直到找到最好的解决方案。每当我进行了一次自认为很有趣或者很有用的尝试之后,我就非常希望能够在社区里和大家一起分享。
在今后的一段日子里,我将会持续更新这个博客,事实上,在本周的晚些时候我们还会将我们Tomorrow Today实验室的交互系统上传到GiHub,在Creative Commons许可证的保护下,大家将可以免费使用该系统,希望能够以此表达我们对社区的一份感谢。
在旧的交互系统之中,如果玩家试图通过Vive控制器抓取或者以其他形式和某个物体进行交互,这个物体就会从属在控制器上,并且是纯运动学状态(没有力的作用),从属之后,物体的位置和旋转就会时刻保持和控制器相匹配。当玩家放置下该物体之后,它就会变成原来的状态。这样的设置带来了大量的问题。
https://v.qq.com/txp/iframe/player.html?vid=y0303drhyx4&width=500&height=375&auto=0
旧系统-通过从属关系设置位置和旋转
视频说明:地板上存在一个网球拍,玩家通过控制器抓取了网球拍,网球拍就和控制器重合了,并且固定在一起,网球拍的位置和旋转始终和控制器保持一致,同时网球拍变成了可以穿透墙壁的物体,最后放下网球拍后,它会自动掉落在地板上。
由于抓取的物体是纯运动学状态,因此重力、碰撞、铰链等带来的外部力都不会对这个物体产生任何作用。不过这个物体对其他刚体仍然可以施加力的作用,但是如果用它撞击墙面或者其他不具备非运动学状态的刚体部分的物体,他就会直接穿透过去。当物体在穿透墙面的时候,如果松开它,它就会卡在那里。显然,牛顿力学的规则不适用于这个世界。物体之间的力的作用不是大小相等,方向相反的。物体在交互时的质量和速度都变得没有了意义。
如果我们不能够给出完美持续的力的反馈(就如同星际迷航中的Holodeck系统),我们就很难使玩家相信游戏中的物体有真实的重量,甚至会让玩家质疑物体是否真的存在。解决这个技术瓶颈最简单的方法就是对玩家朋友说:“好吧,我们没法让你无法穿透墙面或者把东西放在墙里面,但是我们可以假装你们不会这么做”。但是,我认为建立一个存在真实的物理机制,并且能使其有效运行的虚拟现实游戏世界才是真正的解决之道。
一致性对于一个功能性完善并且可信的虚拟现实世界至关重要。我们希望玩家在带上头盔之后就能够迅速的,不自觉的理解虚拟现实世界的运行法则。虽然游戏世界中的物理法则不一定要和现实世界里的物理机制完全一样,但是我们希望玩家可以批判性地看待这些虚拟法则,并且能够使用或者颠覆它们来解决一些问题,因此我们要确保这些法则在整个游戏中都要保持一致性。
https://v.qq.com/txp/iframe/player.html?vid=u0303xkxvjb&width=500&height=375&auto=0
旧系统-缺乏一致性的世界内的交互
视频说明:有一堆乒乓球放在一个半球形网袋之中,网球拍放在网袋的支架上,控制器抓取了网球拍,调整了球拍的位置和角度之后,玩家将网球拍绕于球袋下方,轻轻碰触袋底,袋底和乒乓球都向上微微抬起,玩家又将球拍放低,不接触球袋,然后迅速抬起,网球拍穿透了球袋和乒乓球,但是有几个球受到了影响,被拍出来了,然后玩家又迅速从上往下挥拍,再次穿过了球袋,这一次也有几个球受到了影响,被拍出来了,然后玩家又从下往上再次挥拍,球拍再次穿过了球袋,这一次也有几个球受到了影响,一个球被拍出来了。
代码:protected void OnAttach(VRHand hand){ … this.GetComponent<Rigidbody>().isKinematic = true; this.transform.parent = hand.transform; this.transform.forward = hand.transform.forward; this.transform.position = hand.transform.position; …}
基于物理原理的交互系统虽然很好,但是设计过程中有一些真正的难点需要考虑:
1)由于交互中的物体是不会穿过碰撞对象的,我们必须要处理这样一种状况:物体被其他物体挡住了,但是用户的手还在持续移动。玩家的手会突然和物体松开,这个时候施加在物体外部的力是什么情况?是否我们应该提供一条虚拟信息提醒玩家他们此刻手中持有物品,就好像Surgeon Simulator《外科医生》这款游戏中,手上会显示持有的物体?或者我们应该迅速让这个物体离开玩家的手,就好像Job Simulator《模拟工作》这款游戏一样?
2)如果物体在交互时是连接在一个铰链(开关,门把手,旋钮)上时该怎么处理?如果玩家移动控制器的路径对于物体而言是无法跟随的又该怎么处理?玩家可以在远距离仍然和物体保持交互,同时我们会提供一条虚拟信息或者玩家会中断交互作用,即使这样做会使得物体从玩家手中滑落
未固定的可交互对象:我们的游戏中存在大量可交互对象,它们可以被抓取,也可以被自由移动。我们的解决方案是在每一次更新后都创建新的位置和旋转角度,并且使用插值来平滑运动轨迹。
代码:protected void UpdatePosition(VRHand hand){ … Vector3 toHandPos = Vector3.MoveTowards(this.transform.position, HeldHand.transform.position, 1); Quaternion toHandRot = Quaternion.RotateTowards(this.transform.rotation, HeldHand.transform.rotation, 1);
RigidBody.MovePosition(Vector3.Lerp(RigidBody.transform.position, toHandPos, Time.deltaTime * AttachedPositionMagic)); RigidBody.MoveRotation(Quaternion.Slerp(RigidBody.transform.rotation, toHandRot, Time.deltaTime * AttachedRotationMagic)); …}
固定的可交互对象:某些可交互对象会通过铰链(门,舱盖,窗户等)被固定在一个位置由于这些物体的运动路径都是唯一的,假如编程改变物体的位置,它就有可能会让物体移动到不合理的位置。但是相反的是,既然已经知道了运动的路径,也知道转动的幅度和起始位置,找到最终的位置是相对容易的。仅仅通过测量手到起始点的相对位置,我们就可以很容易算出物体在其轨迹上运动了的百分比,通过插值就可以得出物体的位置了。
https://v.qq.com/txp/iframe/player.html?vid=p0303ucew2d&width=500&height=375&auto=0
通过插值交互固定的物体
视频说明:有一个壁龛,壁龛之中有一部电话,壁龛有一个铰链连接的盖子,可以上下方向转动,从下往上转动可以关闭壁龛,从上往下转动可以打开壁龛,玩家通过控制器先打开盖子,然后在盖子快旋转180度的时候,松开盖子,盖子自动转动到180度位置,之后玩家将盖子提高到接近水平方向位置,给它一个向上的推力后松手,盖子先向上运动一定角度,然后迅速在重力作用下向下转动到垂直向下位置,然后玩家又向上转动了盖子到接近关闭的状态,再转动到水平位置,最后向上转动到关闭状态。
代码:…float currentAngle = Vector3.Angle(startDir, lockedHandDir);float pull = currentAngle / DegreesOfFreedom;sphericalMapping.value = pull;if (repositionGameObject){ if (inBounds) { RotationPoint.rotation = Quaternion.Slerp(start.rotation, end.rotation, pull); }}…
在如上所示的动画中,当玩家和壁龛的盖子进行交互时,壁盖会变成纯运动学状态,壁盖的位置是通过以上的代码片段设置的。在结束交互的时候,壁盖再次变成非纯运动学状态,物理法则重新发生效用。因此,玩家可以砰的一声关上壁盖,也可以随时放下它,让重力作用将它拽下去。
警告:我们目前为止还没有使用过可调节的连接处,或者可以在多余一个轴上旋转的连接处,我并不清楚是否可以对它们使用球形插值,如果可以,又该如何操作。
作者:Nick Abel \ Keith Bradner
翻译:陆于璇
经验分享丨项目实践项目孵化丨渠道发行做有梦想的游戏人-GAME AND DREAM-