VR之 扔一个纸飞机吧


前言

在oculus quest2里的first step应用里体验了多种有趣的交互,如乒乓球,遥控飞艇,拉线火箭,纸飞机等。其中尤以纸飞机给我的手感最为特殊(VR纸飞机哪里来的手感啊喂,还不是有需求就做喽)。这里就大概记录一下其间的开发历程。


一、准备工作

1.引入插件

steamvr plugin2.x的版本对输入都做了单独的隔离,可以很方便地适应多种输入设备和多重输入模式的开发。同时,其中提供的基础脚本组件也十分完备且可理解(Hand,Throwable,Laser等等),在它基础上可以很快进行开发且拓展性良好。但是相对的steamvr plugin不兼容VRTK(VR tool kit,不再更行了,时代的眼泪),对习惯了VRTK的人来讲不得不说是一种遗憾。

在这里插入图片描述
具体操作:再asset store里搜索steamvr plugin,下载导入,接受所有的弹框,完毕后,找到window -> steamvr input,点击。
在这里插入图片描述
保持默认,点击save and generate。
在这里插入图片描述
基本的开发环境就布置好了,打开Interaction_Example场景,里面依旧存放了一些制作好的交互物体了。

2.准备模型

拖入一个cube作为放置纸飞机的台子。

随便找一个纸飞机的模型,导入到unity,拖到场景中,并赋予合适材质,如图:

在这里插入图片描述
对飞机模型进行简要处理:目的是保持z轴正方向和飞机的朝向一致,做法就是建一个根节点,并将飞机模型拖入作为子节点,并适当调整方向。
在这里插入图片描述
OK,到这里准备工作基本完成。下面看看我们到底要干什么,该怎么干。

二、需求拆解

那么扔纸飞机到底是一个怎样的过程呢?你要用自己的虚拟手(左手右手其中一只)“捏”起飞机,然后扔出,飞机通过手的带动获得一个初速度(包含大小和方向),以与手脱离的点为初始点做一个平抛的运动。整体过程基本上就是这样,是不是感觉回到了高中基本物理。
在这里插入图片描述

当然,学过大学物理,尤其是流体力学的小伙伴都明白,纸飞机依照构造不同,抛出角度和速度的不同,其轨迹千差万别,千奇百怪(例如,纸飞机在空中的突然急转,原因是因为飞机的结构以及空气流体的影响)。我尝试了去构建纸飞机在空气流体中运动的理论模型,把压强、对流等都考虑了进去,做了很多这方面的研究后发现(其实是模型越搞越混乱,笔者只是大学上过一点力学的小白),其实这陷入到了一个误区:在游戏引擎里去追求真实的力学仿真。花这么大力气去“模拟”现实,不如把精力用在改善用户体验上(“超脱”现实),遂放弃。

用户体验又从两方面可以入手――视觉反馈和力反馈,对应头显和手柄。视觉上,抛物线要尽量柔美,不能太过剧烈,同时需要一些视觉辅助(如拖尾和一些其他特效等)。力反馈的话,主要是体现在手柄的震动上,即要让使用者在抓握到纸飞机时体会到与什么都没抓握到时不一样的感觉。

总结下来,需求部分如下:

  1. 纸飞机抓握,扔出
  2. 纸飞机扔出时轨迹是抛物线
  3. 纸飞机飞行特效
  4. 抓握纸飞机的手柄反馈

三、实现部分

1.纸飞机抓握,扔出

在开发之前,我们先要了解一下Player这个预制体(prefab)。可以理解为它包含了我们的vr camera和两只手并处理其输入响应,负责将这三者映射到虚拟环境中(当然还有一些其他的组件,例如负责输入模式调控的InputModule,debug组件等)。简单来说,将这个场景中有了这个prefab之后,你戴上头显启动应用,就可以进入到虚拟环境中通过头显看到虚拟环境中的物体以及自己的虚拟双手了。
在这里插入图片描述
在这里插入图片描述

回到我们之前构建好的场景(Interactions_Example)中,此时物体上没有挂任何脚本,所以暂时没有任何交互效果。

为PAP(我们纸飞机的根节点)添加碰撞体组件(我选用了BoxCollider)和Interactable脚本(steamvr plugin里的),并适当调节碰撞体:
在这里插入图片描述
此时,启动应用进入虚拟环境,发现手在进入模型碰撞体后,已经可以高亮模型了。
在这里插入图片描述
但是这还不够,试着去抓握的话,物体并没有与手的有效互动,此时需要添加另一个脚本组件Throwable(也是steamvr plugin里的)。添加了这一脚本后,会自动为物体添加刚体Rigidbody组件。此时,再次进入虚拟环境,并尝试抓握纸飞机:
在这里插入图片描述
纸飞机可以被顺利抓起并且丢出,但是此时也发现,在抓握纸飞机的瞬间,手部模型消失不见。我们详细看一下Interactable脚本组件:
在这里插入图片描述
其中bool量 HideHandOnAttach表示在被抓握时隐藏手部模型,取消勾选。
在这里插入图片描述
嗯~好像还是不太对,手的姿势奇奇怪怪的,那么就k一个手势上去好了。添加SteamVR_Skeleton_Poser脚本。

在这里插入图片描述
点击Create创建对应姿势文件,并找一个合适的文件夹存入。而相应的,编辑器内,模型根节点下,会多出两个手部模型的克隆文件,如图:
在这里插入图片描述
所谓k姿势,就是去摆弄下面两个手部模型,“k”到合适的姿势,在触发交互操作时,手部会自动使用对应的姿势。记得k好姿势后,点击“Save Pose”。
在这里插入图片描述
好像像那么点样子了,我就不去仔细k它的样子了,这个手势可以随时回来微调,但是一定要记得保存,我们进入虚拟环境看一下:
在这里插入图片描述效果还可以,那么这部分基本就完成了,我们借助了steamvr plugin提供的Interactable和Throwable组件实现了交互,并通过它提供的组件去k了手势。

当然,这两个脚本内涵丰富,光是其提供的功能就可以让我们实现很多不同的效果,你可以自行去调试切换,如切换Throwable里AttachmentFlags里面的内容,和Interactable中AttachEaseIn的开关等。

2.抛物线

因为使用了Rigidbody,即Unity的物理模块,所以扔出去的物体很自然是一个弧线(抛物线)。但是仍然有一些问题待解决:现在扔出去纸飞机就像扔出去一个砖块一样,没有办法保持飞机尖儿冲向运动方向;重力感觉太强,没有体现出空气浮力的感觉。

针对这两点,我们写脚本解决:添加自定义脚本,名称为PAPController,如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PAPController : MonoBehaviour
{        
    [Range(0,9.81f)]
    public float buoyancy = 8f;

    private Hand _hand;
    private Interactable _interactable;
    private Transform paperAirplane;
    private Rigidbody _rigidbody;
    private ConstantForce _constantForce;
        // Start is called before the first frame update
    private void Awake()
    {
        paperAirplane = GetComponent<Transform>();
        _rigidbody = GetComponent<Rigidbody>();
        _constantForce = GetComponent<ConstantForce>();
        _interactable = GetComponent<Interactable>();
    }
     private void Start()
    { 
        //添加浮力
        _constantForce.force = new Vector3(0, buoyancy, 0); 
    }
        // Update is called once per frame
    void FixedUpdate()
    {
        //每帧将方向矫正为速度方向
        paperAirplane.forward = _rigidbody.velocity.normalized;
    }
}

添加ConstantForce组件
在这里插入图片描述
现在可以试试丢出去的手感啦。

3.特效

这里的特效我们主要做拖尾的特效,借助的是unity自带的TrailRenderer这个组件
在这里插入图片描述
可以分别做几个不同的拖尾(基于颜色,持续时间,甚至于shader的不同),
在这里插入图片描述
效果:
在这里插入图片描述

4.手柄力反馈

力反馈在Hand脚本里,有一个专门的封装好的函数可以使用,即TriggerHapticPulse。
具体简单的调用为:

    private void Update()
    {
        if (!_interactable.attachedToHand)
        {
            return;
        }
        if (_hand != _interactable.attachedToHand)
        {
            _hand = _interactable.attachedToHand;
        }
        _hand.TriggerHapticPulse(0);
    }

参数可以具体调节,同样的参数视硬件不同反馈强度也会有一定的不同。

最后再做一点点修饰(加了状态切换)(从我写的下一个脚本开始我就开始写注释,我发四)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR;
using Valve.VR.InteractionSystem;

public class PAPController : MonoBehaviour
{
    [Range(0,9.81f)]
    public float buoyancy = 8f;
    private enum PAPStat { InAir,InHand,ShouldbeStatic};
    private PAPStat stat = PAPStat.ShouldbeStatic;
    private Hand _hand;
    private Interactable _interactable;
    private Transform paperAirplane;
    private Rigidbody _rigidbody;
    private ConstantForce _constantForce;
    
    private void Awake()
    {
        paperAirplane = GetComponent<Transform>();
        _rigidbody = GetComponent<Rigidbody>();
        _constantForce = GetComponent<ConstantForce>();
        _interactable = GetComponent<Interactable>();
    }
    private void Start()
    { 
        _constantForce.force = new Vector3(0, buoyancy, 0); 
    }
    void FixedUpdate()
    {
        if (stat == PAPStat.InAir )
        {
            paperAirplane.forward = _rigidbody.velocity.normalized;
        }
    }
    private void Update()
    {
        if (!_interactable.attachedToHand)
        {
            return;
        }
        if (_hand != _interactable.attachedToHand)
        {
            _hand = _interactable.attachedToHand;
        }
        _hand.TriggerHapticPulse(0);
    }
    
    public void ChangeStatToInAir()
    {
        if (stat != PAPStat.InAir)
        {
            stat = PAPStat.InAir;
        }
    }
    
    public void OnCollisionEnter(Collision collision)
    {
        if (collision.transform.tag != "Player")
        {
            stat = PAPStat.ShouldbeStatic;
        }
    }
}

总结

本文展示了笔者从灵感思路,到编程实现的全过程,功能和代码结构当然还是有待完善和优化的地方,但是这种面对问题和解决问题的方式我认为还是有可取之处的。

对于我自身来说,写文章的目的既是希望能够对自己做的东西有个清晰的认识,同时也希望能够将这些微不足道的经验进行总结分享,帮助到更多跟我处于同一阶段的人。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的Unity VR项目实例,用于在虚拟现实设备上展示一个球体: 1. 创建一个新的Unity项目,并将其命名为“VR Ball”。 2. 在场景中创建一个空对象,并将其命名为“Ball”。 3. 在“Ball”对象上添加一个Sphere Mesh组件,并将其缩放为合适的大小。 4. 在“Ball”对象上添加一个Rigidbody组件,并勾选“Use Gravity”选项。 5. 在“Ball”对象上添加一个Box Collider组件,并将其大小调整为合适的大小。 6. 创建一个新的3D对象,命名为“CameraRig”。 7. 将“CameraRig”对象的位置设置为(0, 1.6, 0),并将其旋转为(0, 0, 0)。 8. 在“CameraRig”对象上添加一个Camera组件,并将其设置为VR摄像机。 9. 在“CameraRig”对象上添加一个OVRCameraRig组件,并将其设置为VR摄像机。 10. 在“CameraRig”对象上添加一个OVRManager组件,并将其设置为VR管理器。 11. 在“CameraRig”对象下创建一个空对象,命名为“LeftHandAnchor”。 12. 在“CameraRig”对象下创建一个空对象,命名为“RightHandAnchor”。 13. 在“LeftHandAnchor”和“RightHandAnchor”对象上添加一个OVRGrabber组件,并将其设置为左手或右手。 14. 在“Ball”对象上添加一个OVRGrabbable组件,并将其设置为可抓取。 15. 在场景中添加一个光源,并将其设置为适当的强度和颜色。 16. 在场景中添加一些背景音乐或音效,以增加沉浸感。 17. 在编译并运行项目之前,确保已正确配置VR设备和Unity VR设置。 18. 运行项目并在虚拟现实设备上测试球体的移动和抓取功能。 这个简单的Unity VR项目实例可以帮助您了解如何在虚拟现实环境中创建和操纵3D对象。您可以在此基础上扩展和改进项目,以实现更复杂的虚拟现实应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Claude的羽毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值