VR大空间资料 04 —— VRAF使用体验和源码分析
自用文档,UE4.27 UE5.4项目 HTC Vive VR和Pico4UE
可SteamVR串流 HTC ViveVR、OculusQuest2、Pico4UE设备
可OpenXR串流Pico4UE设备
可打包apk运行于OculusQuest2、Pico4UE设备
官网地址
fab地址 https://www.fab.com/zh-cn/listings/c29a61c5-c337-4b07-9ff7-6ed9788619e3
项目地址 https://www.unrealengine.com/marketplace/zh-CN/product/advanced-vr-framework
商城店家1 https://www.unrealengine.com/marketplace/zh-CN/profile/SSZ+Canada+Inc
商城店家2 https://www.unrealengine.com/marketplace/zh-CN/profile/Human+Codeable
官方文档 https://humancodeable.com/documentation-main/
Yutube频道 https://www.youtube.com/channel/UC-21apdXIhGS_hYVsRll4UQ
官方引导页 https://linktr.ee/humancodeable (官方打包文件无法下载)
Map_Example_Comp地图,官方默认ProjectSetting
SteamVR分辨率 150% 2460x2740:20帧 此时开启DLSS:30帧
SteamVR分辨率 100% 2016x2240:30帧 此时开启DLSS:45帧
SM5→SM6→SM6+VirturalShadowMap→+Lumen
VR帧率30→26→20→0(运行立刻OOM),
PC帧率96→76→70→45帧(1秒后内存OOM)
TSR→TAA→FXAA→MXAA
VR帧率30→30(远处字体闪烁)→30(远处近处字体闪烁)→30(远处近处都有锯齿感)
PC帧率96→120→104→106
Map_Example_Comp地图开启 Virtural Shadow Map(依赖SM6) 可以修复UE5.4左右眼阴影不同步的Bug
SteamVR分辨率 100% 2016x2240:20帧 此时开启DLSS:30帧
https://vreue4.com/recent-posts
advanced session plugin
A Virtual Reality Tool Kit
DLC
AFU - Smartwatch - VR (依赖于4.1版本 和 Oculus)
https://www.youtube.com/watch?v=cGvVouBurPM
Version
4.20-4.23: AVRFramework 2.1 AVRFramework
4.24-4.25: AVRFramework 3.1 AVRFramework
4.26: AdvancedFramework 4.1 AFCore
5.0: AdvancedFramework 5.0 AFCore
官方文档
官方文档 https://humancodeable.com/documentation-main/
file:///F:/_VR/AVRFramework%E2%80%94%E2%80%94PDF/Documentation_AFCore_4.1.pdf
不需要Nav自动寻路组件
VRHand模型手掌长度20cm 170cm高度
Epic默认VRHand手掌27cm 190cm高度
坑
还可以, 个人推荐vrep框架,免费开源,且随UE版本跟新
vraf有局域网功能,能打包安卓和平板,基本vr抓取触碰事件有组件填动画Transform参数就行,一些宏函数可以抄来用
没有全身body,可以结合mimic ik body,
商城只更新到5.2版本,最近一次更新是从epic上传到fab。
5.3因为UI控件的父类中不能使用Handle异步线程,需要自己改一改
针对大空间需要调整方法TeleportCharacter传参不旋转,和胶囊体不同步头显坐标
vraf官方youtube网址禁止评论,dlc包放自己官网卖。
后处理花屏
如果Windows平台设置中的RHI改为 dx12,再把Custom Depth-Stencil Pass的【EnabledWithStencil】改为【Enabled】就会导致手柄射线的边框错误成后处理乱码
RHI改为 dx11时,配合 Custom Depth-Stencil Pass的【Enabled】边框颜色不显示。
模型切换
默认OpenXR插件
UE的MotionControllerComp,会根据VR运行环境,配置不同的模型
OpenXR---HTCVive 或者 Quest 2模型
OpenXR + PicoOpenXR Oculus Quest1模型
PicoXR + PDC Quest 2模型(因为默认BP_MotionController 使用这个模型,模板源码左右手模型颠倒,需要自行修正)
BP_MotionController初始化时,把自身这个自定义手柄模型的Acotr 吸附到 MotionControllerComp
如果不执行
基本为True,隐藏自定义ControllerMesh模型。
如果不隐藏,则自定义模型白色quest2和MotionControllerComp的黑色Oculus1模型重叠显示
UE5.3可以处理隐藏官方默认的MotionControllerComp的设备模型,替换为自定义模型。
HTC和Oculus的手柄模型怎么判断切换模型显示,按键通过CompController的Preset文件配置
VRAF官方案例的模型在建模阶段就考虑了和VR游玩是的角度配置
Vive是平的 正X朝向
Oculus是有倾斜角度
Pico 则是把Occulus模型绕y轴转30°
左手Invert Scale-Z轴-1,右手Invert Rotation-y=50 左手负50
Lumen
如果要 lumen 开启,还需要开启Generate Mesh Distance Fidled 或者 Support HardwareRayTracing
如果开启硬件光追,还需要开启skincache
Lumen + 硬件光追
前向渲染 打包Android用

基本原理
- 光源渲染:首先,对场景中的每个光源进行渲染,生成光照贴图或光照数据。
- 物体渲染:接着,对场景中的每个物体应用这些光照数据,生成最终的图像。
优缺点
- 直观易懂:Forward Shading的原理简单直观,容易实现和理解。
- 灵活性强:适用于各种类型的光源和物体,特别是对于静态或变化不频繁的光源效果较好。
- 性能问题:对于大量光源和复杂场景,Forward Shading会导致大量的重复计算和渲染,影响性能。
- 内存占用:需要存储大量的光照贴图,占用较多的内存和存储空间。
应用场景
- 静态或变化不频繁的光源:由于不需要频繁更新光照数据,静态光源可以有效地利用Forward Shading。
- 复杂场景:对于包含大量静态或变化不频繁物体的场景,Forward Shading可以提供较好的渲染效果。
Separate Translucency
PDA设置
项目设置中有PDA,CompControl也有PDA手柄配置
打包Oculus
勾选Meta Quest设备,否则会以观看平板的模式运行游戏
AI移动
需要把Nav Volume通过Ctrl M 改放在 Persistent地图,才能在Persistent运行时,成功执行AI寻路,否则会aborted
如果Nav只放在大厅地图,Persistent运行时失效。
如果Nav放在Mesh地图时,Manager运行也时失效
【错误记录】
Teleport瞬移是将Camera移动到目标位置,此时Pawn位置有相对偏移
沙漠地图不知道为啥瞬移过去时,会仿佛重置头显设备。 向量Camera - 向量Pawn + 向量Pawn
因为默认使用瞬移,导致Camera位置和Pawn位置重叠
1.BP_PlayerControllerMain Spawn PlayingPawn,此时Camera和Pawn位置是正确的。
2.BP_PlayerControllerMain Spawn BP_Transition时传参PlayingPawn,BP_Transition小黑盒Begin时执行角色Teleport到空中小黑盒,此时的 PlayingPawn时Camera和Pawn位置是正确的。
3.瞬移到小黑盒中心结束后,调用“过渡结束”事件分发,绑定在BP_PlayerControllerMain。
4.从小黑盒瞬移到场景 默认地图时可以匹配,Landscape位置不对。
因为框架源码设置了根据头显坐标重置了胶囊体位置
BP_Pawn_Base中
子类BP Pawn VR重写
Bug 胶囊体朝向
框架默认启动时,以头显朝向为正北 正X方向,因为根据目的地旋转值Set Controller Rotation。
修改BP_Transition和BP_PlayerPosition的UseRotation为false
CSDN文档
https://blog.csdn.net/weixin_55901138/article/details/138162643
Select & Grab Component 选中和抓起组件
https://blog.csdn.net/weixin_55901138/article/details/138541672
Latch Component 吸附组件
https://blog.csdn.net/weixin_55901138/article/details/138626752
Gaze Component 关注进度条组件
https://blog.csdn.net/weixin_55901138/article/details/138720887
Anchor Component 锚点吸附组件
MotionController
MotionControllerComponent的display_device_model在UE5.4时因为过时被彻底移除,UE5.3还能使用,但是有警告,没有影响,手动修改模型的显示和隐藏即可
https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/MotionControllerComponent?application_version=5.2
display_device_model (bool): [Read-Write] Display Device Model: Used to automatically render a model associated with the set hand.
display_device_model (bool): [Read-Write] deprecated: bDisplayDeviceModel is deprecated.
Please use the XRDeviceVisualizationComponent for rendering instead.
BP_PawnVR 在OnPossess时,获取BP_PlayerController_Main,并调用父类BP_PawnBased的方法ClientInitializePawn执行客户端角色初始化,
BP_Pawn继续在ClientInitializePawn方法中执行客户端角色初始化,继续调用Server_PrepareVRPawn执行服务器方法调用SwitchControllers创建左右手控制器
创建自定义Actor -- BP_MotionController -- BP_MotionController_Controller_Left 手柄射线模式
或者 -- BP_MotionController_Hands_Left 手掌拟真模式
BP_PawnVR初始化创建自定义控制器BP_MotionController时,传参官方组件MotionControllerComp变量(display_device_model变量过时)
BP_MotionController_Controller_Right 挂载ChildActorComp节点 BP_MComp_Laser 实现射线抓取功能、UI射线模拟鼠标点击
BP_MotionController_Hands_Right 挂载ChildActorComp节点 BP_MComp_Hand_Grab 实现按键拟真抓取功能
BP_MotionController_Hands_Right 挂载ChildActorComp节点 BP_MComp_Hand_Laser 实现UI射线模拟鼠标点击 Select功能名触发
BP_MComp_Teleport 瞬移功能
BP_MComp_Movement 移动功能
display_device_model相关
如果是是手柄模型,则影响显示隐藏;手掌模型不影响
手柄震动 Haptics
"SetHaptics"
Comp组件
自定义ActorComponent组件,物体可以被互动的功能模块化
功能分类
Info
Comp_PlayerInfo_Basic 被用于BP_PlayerState_Main、Widget_Multiplayer_Host_Simple、Widget_NameTag
Interaction
Comp_Gaze
ActorComponent组件
交互地图中,简单凝视案例 BP_GazeView 和其子类凝视文本动画 BP_GazeView_DemoText 都依赖了Comp_Gaze组件
BP_GazeView凝视出现文本动画 调用 CompGaze的GazeView回调,根据枚举执行容器内的组件们 显示/隐藏、缩放、位移
Comp_Gaze_Comp
Progressbar:
Gaze组件被目光/光标聚焦时,会出现一个逐渐增长的进度条用于提示用户已经开始聚焦,当进度条满格后就会触发交互事件。Progressbar这一栏参数用于调整Gaze交互的触发时间与进度条的样式,UI有两种预设两种预设样式,环状进度条与横条,如果用户要自行设计进度条,需要继承Widget_GazeIndicator类:
Timeout Gaze:不继续凝视0.2s后 停止凝视
Progress Duration: 设定从聚焦开始到触发交互需要目光停留的时间
开始调用输出Start,连续调用输出Update,直到停止调用后,等待延迟Timeout ,最后再输出End
Settings:
Component Definition:
ComponentTagToSearchFor:
添加Gaze组件交互事件联动的组件的Tag,通常会与Switch组件交互,Switch组件再与其他功能组件进行交互。
ActorsToTrigger:
添加场景中产生交互的Actor对象引用,Actor对象的组件必须包含上一栏中的Tag。若是在自身Actor中进行通信,则只需要勾选TriggerAlsoSelf即可。
Toggle:
表明交互事件触发时会交替设置交互组件的Set/Unset状态。
Component Tag to Gaze at:
用于设置用户聚焦在哪个Mesh上,默认为None时表明用户的目光聚焦到整个Actor(可能包含多个mesh)都能触发Gaze事件,若希望在聚焦到某部分Mesh时才触发聚焦事件,则需要在Mesh上添加Tag并将Tag填入该参数栏
Comp_TouchSnap
自定义拾取物品后触碰显示进度条,进度条完成时执行吸附逻辑
Comp_TouchSimulate
自定义触摸物体时显示进度条,进度条完成可自定义调用执行语言字幕播放、下一步逻辑等事件。
组件挂载在可触摸的物体上,PC端通过按T键位进行持续屏幕中心射线检测,扫描到物体时显示圆圈进度条,
组件中创建进度条BP_GazeIndicator的子类BP_ProgressCircle,其中挂载了Wiget组件。BP_GazeIndicator设置了进度条回调等事件分发回调
VR端则手心射线检测触摸(暂未实现)
Comp_VoiceCaption
自定义音频语音字幕组件,挂载在角色身上,定时器修改UI窗口坐标
Comp_Grab
要被拾取的物体,添加CompGrab这个ActorComponent组件,
组件中携带者InteractorInfo交互信息键值对,用于调用每个交互Actor的接口抓取和释放
屏幕和移动端版本,因为没有Controller控制器手柄,所以BP_Pawn_Base_Screen实现了接口Interface_GrabbingActor
VR版本,手柄控制器挂载了BP_MComp_Laser这个Actor作为ChildActorComp,其实现了接口Interface_GrabbingActor
VR版本,手掌控制器挂载了BP_MComp_Hand_Grab这个Actor作为ChildActorComp,其实现了接口Interface_GrabbingActor
参数说明
GrabType:
按照官方的文档,抓取类型有普通模式与使用物理柄的模式。在桌面端测试中两个模式没有看出太大的差别,
模板中的Pawn类中集成了使用Physics Handle抓取的逻辑。默认使用normal模式即可,Physic Handle可能是类似隔空取物的模式
Release Type:
是否开启物理模拟。当你选择了Free Placement,即松手后物体停留在松手时的位置,即使用户在运行前手动将StaticMesh或SkeletalMesh的物理模拟打开,模型仍然不会开启物理模拟。相应的,当设定到Physics模式时,物体就自动开启了物理模拟
Should Keep Upright:
定义了当物体被拿起时是否自动将物体旋转到自身Z轴与世界Z轴平行。
在VR项目中,如果想让物体模拟被手拿起来的效果,请把这一项参数关闭
Snap:
参数只有在使用VR模式且使用BP_MComp_Laser抓取时才会使用到。
开启Snap后,当用户使用Laser抓取物体时,物体会自动吸附到手柄处,吸附后的物体旋转需要玩家自行在Relative Controller Position中进行定义
Conditions
参数定义的是Grab组件的一些限制条件。
Canbe Picked UP: 表明是否开启Grab功能
Should Auto Pickup: 这一条件只适用于VR项目,它表明Actor在VR手柄接近后是否被自动拾取
Max Distance to Socket: 规定了物体能被拾取的最大距离
Grab Attached Actor Instead: 这个笔者还未测试过,猜测打开后即使松开抓握键物体也不会掉落
Grab Tag: 当Actor中不止一个StaticMesh时,可以对能够被抓取的Mesh添加自定义Tag并将Tag填写入该栏中,这样只有含有对应Tag的Static Mesh能够被抓取起来。
Allow MultiGrab & Allow Pickup if Already Picked Up: 这两个选项需要同时打开才能实现
Keys这一栏参数笔者认为是非常重要的,有使用过VRA模板的用户会发现当用户用手柄射线抓握了物品后,手柄的X轴和Y轴绑定的转向/移动事件会被重载为将被抓握的物体旋转/前后移动。然而当我们实际项目中,很多需求是需要拿起物品进行移动的,解决这一问题的关键就在于合理设定Keys这一栏参数。
Grab组件能够重写手柄的事件,预设的手柄事件如下:
AllKeys:
参数为true时,当用户拾取Actor后,Grab组件就会重写上图手柄的事件。
此时VR模式下的移动事件就会被Blocked,因此当用户有拿起物体移动的需求时,AllKeys参数需要为False.
OccuiedKeys Array:
有时当用户拿起物体后,物体含有特殊功能需要通过手柄按键触发时,可以在Array中添加需要重写的手柄输入事件。
例如当用户拿起枪,手柄按下Trigger时就应该能够发射子弹,此时就可以在Array中添加Trigger,然后重写Grab组件的Grab Trigger Axis事件即可。
伪代码
Allkeys为False时,VR手掌抓着物体时才能正常使用控制器原本配置的功能
例如:瞬移
本蓝图物体被抓起后 重定向Trigger功能
【注意】官方默认版本为 Trigger映射Select功能,所以Grip映射Grab,简单配置Trigger占用,即可后续模拟扳机开枪功能
现改为Trigger隐射Grab功能,修改BP_MComp_Hand的FunctionInput源码,目的Trigger松开按键时不丢开物品
如果CompGrab添加了Face01的占用
则在抓起物品时,无法使用控制器原本配置的Teleport瞬移功能
如果抓取模型后,想吸附到目标位置时,不需要虚影功能则断开TrytoSpawnSnappingHelper连接,
此时吸附功能也失效,需要用老版本的球形碰撞体Overlap检测的Attach吸附逻辑。
【解决】简单处理,继续连接执行TrytoSpawnSnappingHelper,修改源码:
添加CompGrab.Search变量配置,创建BP_HelperSnapping时输入配置,关闭Search则关闭每帧搜索设置虚影模型位置,
CompGrab 自定义新增条件变量SerachToShowHoloGhost。
抓取后,靠近目标位置,此时松手后,物体吸附目标
改为:抓取后,靠近目标位置,不松手等待2秒,出现进度条,进度条结束自动松手+物体自动吸附目标
CompGrab.SnapActor 调用 CompConnector.AttachConnectorToActor 吸附到锚点
松手时,判断是否有吸附虚影提示,有则物体位移到虚影Holo处,位移动画期间禁止拾取,绑定占用吸附目标锚点(避免多个把手同时送手吸附),
物品位移到虚影位置时 绑定调用吸附完成回调,执行松开物品和Attach物品到目标。
Comp_Latch
Comp_Latch_Drag
Comp_Latch_Physics
Comp_Latch_Simple
Comp_Overlap
Comp_Overlap_Comp
Comp_Overlap_Drag
Comp_Select
Comp_Select_Comp BP_Table_SelectExample
Comp_Select_SelectionWindow
Comp_Teleport
Misc
Comp_Delete
Comp_LineMeshComponent
Comp_Mount
Comp_Orbit
Comp_SpawnActor
Comp_SpawnLocation
Comp_Spectator
Comp_Video
Multiplayer
Comp_Replication
Comp_Replication_Actor
Comp_Replication_Component
Comp_RPC
Pawn
Comp_Controls
Comp_Touch 触摸组件提供给BP_Pawn_Mobile调用
Snapping
CompAnchor 挂载在要吸附的基底模型位置,其中创建了BP_Helper_Anchor 用于给吸附帮助的球形检测
CompGrab 添加在可拾取的物体,其中创建了BP_Helper_Snapping
CompConnector 挂载在可拾取的物体,被BP_Helper_Snapping每帧调用方法 进行球形检测目标是否有BP_Helper_Anchor
Comp_Anchor
父类StaticMeshComponent,设置吸附模型的锚点
吸附时的逻辑:添加CompAnchor的ActorGotAttached事件分发的绑定
通过在Comp_Connector_Anchor中,调用CompAnchor的AttachActorToAnchor,再Call这个ActorGotAttached事件分发
需要添加在物体将要吸附在的Actor上,相当于一个吸附的基底
参数说明
Anchor Deactivated:
该bool参数用于开启/关闭Connector组件,当bool为true时带有Anchor组件的物体将无法与该connector进行交互,参数默认是false。
Connector IDs to Allow:
需要将吸附在Connector位置的Anchor Comp的ID填入该参数栏中,这样两个组件才能互相通信实现吸附交互。
Connector should attach:
该bool参数用于设定当开启了物理模拟的Anchor物体吸附在Connector上时是否继续持续吸附在Connector上。
如果为false,当connector倾斜时或物体收到物理体影响时,吸附的物体会因为重力掉下。
确保角色在释放后保持相互连接。如果未启用ConnectorShouldAttach,则当物理功能被激活时,连接的角色可能会在折断后掉到地上。
Sphere Radius:
该参数决定了能够检测到Anchor组件的球形范围大小
Transform设置:
Anchor组件与Connector组件吸附的方向与位置是根据这两个组件的Location和Rotation决定的,并且吸附后的物体Scale时由Connector的Scale决定的。
官网推荐在Anchor组件上添加一个Arrow组件辅助判断方向,这两个组件的吸附的方式是两个组件的X轴相对地吸附
Comp_Attach
父类ActorComponent,
被用于收音机本体 和 BP_PawnBase
可以用于吸附完成事件回调,Attach时获取吸附的Actor对象
【疑问】在收音机的这个组件删除了,不影响抓取吸附,可能因为CompAnchorHandle也有attach回调事件
Comp_Connector
Comp_Connector_Anchor
组件需要放置在被抓起的Actor上,Anchor组件与Grab组件之间不需要额外设置,
并且只有当物体被CompGrab抓取后才会触发Anchor组件与Connector组件之间的识别
参数说明
ConnectorID:
需要用户自行添加一个自定义的ID,用于与对应的connector进行吸附的识别。
Search Radius&Distance:
Anchor组件检测Connector组件的范围半径与距离,结合起来类似以Anchor组件为原点的一个胶囊体范围进行检测。
Comp_Anchor的Location与Rotation调整:
Anchor组件的位置与旋转与Connector的组件的位置与旋转决定了物体吸附在基座上时的位置与旋转。根据Connector组件介绍中的图片进行设置即可
Comp_Connector_Surface
State
Comp_Drag
Comp_Highlight
Comp_Name
Comp_Open
Comp_Percent
Comp_Switch
Comp_Trigger
Comp_Velocity
UI
Comp_HUD
父类ActorComponent
挂载在PC角色
Comp_PawnUI
父类ActorComponent
挂载在VR角色
新增变量 控制是否开启随着延迟跟随头部
自定义事件分发WidgetCommandClient,(Use to implement replicable widget functionalities.)
这个事件官方demo无绑定,只被自身的ForwardWidgetCommand调用,注意与Comp_Widget的自定义分发事件同名
ForwardWidgetCommand这个事件被BP_PawnUI_Element调用
SpawnElement核心伪代码逻辑
使用方法:获取角色身上挂载的CompPawnUI组件 通过Toggle调用SpawnElement方法创建UI ,显示和销毁切换
也可以直接调用SpawnElement创建UI
帮助节点是否吸附到镜头
Comp_Widget
父类WidgetComponent
挂载在想要显示提示窗口的物体上
CompWidget的事件绑定添加WidgetCommandClient事件分发,根据命令CloseWidget执行关闭窗口
CompWidget自定义方法 SetWidgetClass根据传递的Class创建WidgetBase,并把自身CompWidget传递进去
按钮面板的子类UI都继承于WidgetBase,WidgetBase自定义事件WidgetCommand,调用了CompWiget的自定义事件WidgetCommand
执行命令关闭窗口CloseWidget 或者 切换关卡SwitchLevel
在CompWiget的自定义事件WidgetCommand中,调用了Call WidgetCommand Client分发事件,其绑定在挂载使用了CompWidget的BP蓝图中
挂载CompWidget的蓝图绑定WidgetCommandServer,Actor开启了Replicates, 执行CloseWidget和SwichLevel。
BP_Trigger_Button_Toggle
交互按钮切换器携带组件
Comp_Drag 组件拖拽
Comp_Overlap_Drag 组件重叠拖拽
Comp_Select 组件焦点
Comp_Switch 组件切换
添加控制的Actor和标签
齿轮的组件Comp_Switch Tag值为Cog_SwitchComp(默认是Cog_ActiveComp)
Comp_Open【State】
Tick 进过编辑器Apply之后,先检测到长度,然后移除,最后停止激活
Comp_Drag 【State】
一般与Comp_Latch一起使用,制作拉杆
DragType 决定了运动的形式和方向
Linear Type:主要是在对应的轴上平移。
Angle Type:主要用与旋转类似方向盘,闸门的交互物体,可以在Yaw与Roll方向上的旋转。【为什么没有Pitch】
Spline Type:能够使用spline自定义拖动的路线,灵活地设计交互物体的拖动路径。
Rotation Type:与Angle Type有所区别,只能在VR模式下进行拖动,一般用于制作旋钮的交互。
ValueRange 范围,AngleType为Rotation时用力方向相反?
StartValue 初始值,搭配Applay使用
Snap Type规定了当用户拖动完物体,物体的释放后运动类型。
FreeMovement:该参数表明当用户放手后,物体会固定在放手时的状态不变。
SnapToSegment:该参数表明当用户放手后,物体会恢复到距离当前值最近的设定值。
Reset: 该参数表明当用户放手后,物体会恢复到被拖动前的状态。
SnappingSpeed: 该参数设定了用户释放物体后,物体Snap到某一值时运动的速度
Follow Speed: 该参数设定了交互物体跟随用户手柄运动的速度
InvertPercent:
该参数要根据用户使用的具体情况来调整,根据物体运动的方向不同,物体走过规定路径/角度的百分比值会变化,例如事例中旋转闸门,我们希望顺时针旋转时,percent的变化为0-1;在测试中顺时针旋转阀门实际上Percent的变化是1-0,此时我们就需要将InvertPercent参数勾选为true。
SectionSet 数组用于让用户自定义规定当前交互物体可以Snap到的部分有多少块,主要是结合Snap Type参数时使用的
Comp_Latch_Drag【Interaction】
Comp_Drag与Comp_Latch_Drag,当两个组件都设定好了基本参数后,需要让这个两个组件建立起通信
在Comp_Drag组件中,需要设定组件的Component Tags,并且将要控制的Mesh的Tag填入至Component Tag to Control参数栏中。
在Comp_Latch_Drag组件中需要将刚刚设定的Comp_Drag组件的component tag填入至Drag Tag参数栏中,并且将要手部模型要吸附到的Mesh的Tag也填入至Component Tag to Attach To该参数栏中即可。
例如CompDrag自身Tag为Lever_DragComp,需要控制的Mesh的Tag为Mesh_Lever
CompLatchDrag需要配置的DragTag值Lever_DragComp,需要控制的Mesh的Tag为Mesh_Lever
Can Be Latched On To:这个参数用于开关Latch Comp,可以根据用户设定在适当的时候开启与关闭Latch功能。
Max Attach Distance:用户手柄能够触发吸附的最大距离。
Auto Attach:
该参数与上一个参数结合使用,若Auto Attach为True时,用户的手柄进入吸附范围后会自动吸附至交互物体上,若为False则需要用户手动按下抓握键才能够吸附在交互物体上。
Component Tag to Attach To:
这个参数需要填写手部模型需要吸附的模型的Tag名称,配置添加Tag至StaticMesh并在SM上添加手部模型吸附的Socket。
Allow Simulataneous Latching:
这决定了交互物体上是否允许多个用户/控制器进行吸附。
Minimum Detach Distance:用户脱离的最小距离,当用户手柄移到范围外时手部模型将自动脱离交互物体。
手部模型调整
(X=-10.000000,Y=6.000000,Z=-3.000000) 右手HMDOffset默认
(X=-10.000000,Y=-6.000000,Z=-3.000000)
(X=-10.000000,Y=3.000000,Z=-10.000000) y=30° pico右手
Latch组件是通过获取交互物体模型上的Socket位置与Socket Name来进行手部模型的贴合与样式的选择。因此在我们要使用Latch组件时需要对模型进行处理,在对应位置添加有命名规范的插槽
根据官方的视频中的说明可知, 插槽的命名规范是:
第一段必须为L_或R_,代表了左手或右手。后面接00-14的数字,这数字代表模板中手部对应的15种不同的手势姿势。
例如下图中的旋钮模型,官方设定的插槽名称为L_02与R_02,这代表了Latch到旋钮时的手势应该是02号手势。
AttachPoint插槽也是必须要有的插槽,它必须要尽可能的靠近手部模型放置在物体上的位置
VRA模板中自带的十五种不同的手势,用户可以按需自行配置SocketName进行手势的匹配
ChildAcotrComponent组件
BP_MComp持有MotionControllerComp,用与其子类射线、抓握使用
按键逻辑实现在自定义BP_Motion_Controller_Controller的子类左右手控制器中的挂载节点ChildActorComp,例如BP_MComp_Move
框架自定义Actor:BP_MComp,其子类有BP_MComp_Hand_Grab、BP_MComp_Teleport、BP_MComp_Movement,
每个BP_MComp的子类Actor实现用户操作的逻辑模块化,最后作为ChildActorComponent挂载在BP_MotionController节点。【是否可用SceneComponent作为父类?需要查看源码】
框架自定义Actor:BP_MotionController,其子类有BP_MotionController_Controller、BP_MotionController_Controller_Left、BP_MotionController_Hands_Left
BP_PawnVR初始化创建控制器时,传参官方组件MotionControllerComp变量(display_device_model变量UE5.4过时弃用)
BP_MComp 自定义Actor
BP_MComp_Teleport
BP_MComp_Movement
BP_MComp_Hand_Grab
BP_MComp_Hand_Finger
BP_MComp_Ping
BP_MComp_Hand_Laser
BP_MComp_Laser
只有右手手柄有BP_MComp_Laser 做射线交互,而且右手手柄没有BP_MComp_Hand_Grab,
所以通过射线组件 实现功能Select、Grab、LaserModes、Ping
手柄抓取:Trigger常按后拾取,(Release触发)松开Trigger后则丢弃;Grip侧键点按一次松开,(Press触发)抓取物品吸附到手心,再点按一次Grip侧键(Press触发)丢弃物品
手柄射线如果Trigger常按抓取UI面板的滚动按钮,需要按一下侧键才能松开【Bug】
【修复】更改DA_ControllerVR_VivePreset的 HolddingGrip 为True,
模型抓取可以常按Trigger(Select)拾取,松开按键丢下物品,但是UI滑动按钮通过Trigger(Select)常按后依旧需要点按Grip(Grab)键松开,
模型抓取和UI滑动按钮抓取,都可以通过常按Grip(Grab)拾取,松开按键丢下物品和按钮。
官方代码:射线模式常按拾取物品时,同个手柄调用瞬移,拾取射线变为瞬移射线,自动松手
BP_MComp_Hand_Grab
左右手掌都有BP_MComp_Hand_Grab,
所以通过手掌抓取组件 实现功能Grab
手掌抓取:框架原版只有Grip侧键点按吸附,再点按丢弃,修改DA_Preset配置选项,把Trigger和Grip键位都改为Grab功能。
想要抓取物品时瞬移,物品的CompGrab中的Allkeys为False,取消所有按键占用
【Bug】瞬移射线被拾取物体阻挡,导致抓取时无法瞬移
手掌球形检测通道是Visibility
瞬移射线检测通道是Camera,【修复】所以要抓取的物体Camera通道改为Ignore
【Bug】如果改为常按抓取,右手常按抓取时,右手瞬移键刚按下就把抓取功能松开,左手移动正常。如果左手抓取,右手瞬移正常,左手移动正常。
【修复】BP_MCompTeleport中修改占用AllKeys为False
按键触发瞬移时,默认Allkeys为true,则所有按键都松开了,包括常按抓取键
(BP_MComp_Movement无影响 因为没有移动时替换操作的逻辑)
拖拽拉杆抓取时则出现按下Face01出现瞬移引导射线
远程遥控器抓取时没有影响,因为CompGrab重定向上下左右按键映射
如果AllKeys为False时
添加Trigger 则表示该键要占用 执行松开逻辑
添加Grab 则表示功能要占用 执行松开逻辑
【Bug】当前按键自定义DA配置Trigger和Grip都是Grab时,
如果已经常按后拾取,手里有物体
Grip按下时,Trigger再按下不执行,Trigget松开就调用松手,无法实现Grip拿着枪,Trigger开枪射击
【修复】源码新增判断CompGrab是否占用了Trigger,
如果Trigger占用了,就不执行Trigger和Grip的Grab松开逻辑,改为在CompGrab中Grip调用松手接口
【Bug】如果模型无添加Socket,官方版本为模型吸附到手心时产生一小段位移
【解决】改为在哪按键就在哪抓起来吸附到VR手掌
以VR手掌中心点的位置设置球形圆柱搜索距离
官方 20 改为 1 目的是无需虚空念力抓取
参数说明
伪代码
Actor EventTick 根据是否抓取物品,每帧通过按键数值修改手指头弯曲程度,例:远程遥控器 remote
Actor EventBegin初始化时,
事件SetSearchTickForPickup中用无限循环模拟定时器进行球形检测是否可以拾取物品,定时评率根据手里是否抓着物品动态修改0.4、0.1,
用抓取值判断手掌是否张开,如果张开则Search for Pickup 球形检测手心物品是否可以拾取,CompGrab执行物品高亮,
(如果有自动拾取,则继续调用接口抓取Grabbing Actor Grab Actor实现吸附功能)。
接口实现Grabbing
BP_MComp_Hand_Grab 实现了接口 Interface_GrabbingActor 的 Grabbing Actor Grab Actor方法,(还有射线和PC桌面实现过这个接口方法)
这个接口方法在被扫描时自动抓取时被调用过,
这个接口方法在BP_PawnVR按键事件映射中,通过调用BP_MotionController的KeyInput方法,然后遍历BP_MComp并执行其Function Input方法中被调用,
接口实现时调用了 Check for Grab or Latch 方法,判断传参Actor是否有CompGrab组件,根据SocketName获取手指动作,有则特殊姿势抓取,无则直接抓 GrabActor。
GrabActor → OnRep_GrabDefinition → GompGrab.GrabPressed → GompGrab.TrytoSpawnSnappingHelper → CompAnchor/CompConnector锚点吸附逻辑
GrabActor方法中,调用OnRep_GrabDefinition,设置抓取的手指动作、吸附在手掌控制器的点位、是否替换了按键功能(例如抓着枪射击发射子弹),
如果GrabComp的所有者蓝图有开启同步则服务器调用OnRep_GrabDefinition
【疑问】收音机把手没有开启Replicates,但是有Comp_Replication_Actor
GrabDefinition的Set w/Notify 同步方法OnRep_GrabDefinition,
调用了Detach for Grab,判断手里是否有东西,是否可以多抓取,调用CompGrab.ReleaseAllGrabbingActors执行松开所有抓取物
实现了抓起物体时是否有PhysicsHandle物理约束,
设置FirstHandle、SecondHandle位置,调用PullActor拉起抓着的物体 移动到手掌的正确位置,
OnRep_GrabDefinition最后调用GompGrab.GrabPressed方法,其中执行了调用抓起物体后的事件分发(绑定在具体的拾取物体蓝图中),物理约束设置,
GompGrab.GrabPressed最后调用GompGrab.TrytoSpawnSnappingHelper,创建吸附帮助,提示用的高亮模型、虚影模型GhostMesh
PullActor方法,目的是使用timeline动画实现 拉起抓着的物体 移动到手掌的正确位置。
在OnRep_GrabDefinition调用,即为GrabDefinition的Set w/Notify中调用。
BP_MComp_Hand_Grab 实现了接口 Interface_GrabbingActor 的 Grabbing Actor Release Actor 方法,(还有射线和PC桌面实现过这个接口方法)
这个接口方法在BP_PawnVR按键事件映射中,通过调用BP_MotionController的KeyInput方法,然后遍历BP_MComp并执行其Function Input方法中被调用,
这个接口方法在CompGrab.EventBegin被调用过,初始化绑定销毁时调用该接口
这个接口方法在CompGrab.ReleaseAllGrabbingActors被调用过,
GrabDefinition调用了Detach for Grab,其中调用CompGrab.ReleaseAllGrabbingActors目的是松手释放所有抓取的物体
Detach for Release.Detach from Grabbing Actor 和 Detach for Connector.Detach from Grabbing Actor 被用于齿轮系统案例,齿轮的连接和吸附
AFVR按键映射源码分析
按键逻辑实现在自定义BP_Motion_Controller_Controller的子类,
左右手控制器中的挂载子Actor节点ChildActorComp,
例如BP_MComp_Movement
移动功能按键数据传递
CompControls获取DA_ControllerVR_VivePreset的左右手详细按键
Key:EnumSide
Value:Struct_KeyToFunctionMap
目的是设备按键 映射为自定义功能变量
例如: 圆盘↑键 Face01 映射为 Teleport
ProjectSetting--InputAction设置FaceButton01Right值Vive(R)Trackpad Up
按键触发时映射为左右手的Face01(取自自定义按键枚举Enum_Controller_Buttons)
VRPawn调用CompControls组件方法再映射为功能Teleport(取自DA_ControllerVR_VivePreset)
其中ExecuteControlFunction如果被子类VRPawn执行则优先执行重置、暂停、PawnUI功能名
BP_PawnVR 调用左右手控制器BP_Controller的方法Key Input 传参功能名,目的是MComp执行功能
BP_MotionController 的 KeyInput 按键按下的操作添加到集合(松开移除),遍历挂载子Actor节点BP_MComp,组件执行FunctionInput和KeyPressed(子类重写)
例如Teleport功能实现在BP_MComp的子类Actor:BP_MComp_Teleport,作为ChildActorComp挂载在
自定义手柄控制器BP_MotionController_Controller_Right的节点上,这样左右手可以模块化不同按键功能
也可以最后再在playerPawn中新增按键重定向
BP_VRPawn设置按键事件监听
DA_ControllerVR_VivePreset中按键配置
CompControls中按键映射
默认按钮
FaceButton01 左手上键呼出菜单,右手上键
Y键 飞行
Tab键位,聊天
Ping F键
VOIP Loop L键 网络电话?
Sprint 冲刺 左Shift
跳跃 Jump
控制鼠标 左Alt
Select 鼠标左键拾取
鼠标右键旋转
暂停 P
蹲下Crouch C键位
Input 按键设置
默认常按
MoveRight 和 ThumbstickLeft_XY
Pico Input 按键配置
PDA Vive 对比 Oculus
VRPawn的CompController中配置PDA
DA_ControllerVR_VivePreset 对比 DA_ControllerVR_OculusPreset
瞬移改成按钮松开,瞬移结果面向为手的朝向方向
HTC Vive VR 项目自定义二期按键
抓取功能按键数据传递
ProjectSetting -- InputAction设置按键 Trigger和 Grip
BP_PawnVR 中 设置按键触发时映射为左右手的 Trigger 和 Grip (取自自定义按键枚举Enum_Controller_Buttons)
BP_PawnVR 调用CompControls组件方法再映射为自定义功能名Grab(取自DA_ControllerVR_VivePreset)
其中ExecuteControlFunction如果被子类VRPawn执行则优先执行重置、暂停、PawnUI功能名
BP_PawnVR 调用左右手控制器BP_Controller的方法Key Input 传参功能名,目的是MComp执行功能
BP_MotionController 的 KeyInput 按键按下的操作添加到集合(松开移除),遍历挂载子Actor节点BP_MComp,组件执行FunctionInput和KeyPressed(子类重写)
可以抓起的Actor,添加CompGrab组件(Actor Comp),吸附到基底锚点的点位Comp_Connector_Anchor(SceneComponent)
基底吸附的Actor,吸附锚点点位Comp_Anchor_Antenna(StaticMeshComponent) 添加CompAttach组件,吸附数量(Actor Comp)
BP_MotionController_Hands_Right 挂载ChildActorComp节点 BP_MComp_Hand_Grab(Actor) 实现按键拟真抓取功能
伪代码
BP_VRPawn绑定按键监听并映射为功能名,让 BP_MotionController 的 KeyInput 方法执行按键功能名。
在 BP_MotionController 初始化时,遍历自身挂载的BP_MComp的ActorChildComp,然后再在自身 KeyInput 中循环遍历执行每个BP_MComp.FunctionInput和KeyPressed
左右手手掌控制器挂载 BP_MComp_Hand_Grab,在组件内FunctionInput 执行了Grab方法名,调用了接口Grabbing Actor Grab Actor,
接口实现时调用了Check for Grab Or Latch,方法中调用了GrabActor方法,方法中调用了GrabDefinition的Set w/Notify 同步方法OnRep_GrabDefinition 和 手指动作设置 和 抓取时按键。
同步方法内实现了抓起物体时是否有PhysicsHandle物理约束,
设置FirstHandle、SecondHandle位置,调用PullActor拉起抓着的物体 移动到手掌的正确位置,
最后调用CompGrab.GrabPressed方法,其中执行了调用抓起物体后的事件分发(绑定在具体的拾取物体蓝图中),物理约束设置
最后调用GompGrab.TrytoSpawnSnappingHelper,创建吸附帮助,提示用的高亮模型、虚影模型Ghost
BP_MapInfo
地图数据和Pawn配置
Map_Example_Components 是子地图自己
Menu地图加载了 Menu和Lobby的PDA
说明
每个地图放置BP_MapInfo蓝图
这含有3个PDALevel的primaryDataAsset主要数据资产
LevelKey、 MenuKey 、 LobbyKey
通过调用BP_GameInstanceMain对象的Get Current Level Class方法来判断关卡是否持有BP_MapInfo,
关卡中设置其LevelKey的数据资产:所在持久化的地图数据 + Map To Load的地图数据
BP_GameInstance_Main 主要实现SwitchLevel切换关卡管控
BP_GameMode_Main 配置选项
BP_PlayerController_Main 只让当前客户端判断Pawn类型并配置GameInstance所持有的InfoLevel对象为LevelKey,并让服务器生成VRPawn角色并更改持有,按键设置
和调用BP_PlayerState_Main设置PlayerIndex
BP_Helper_TransferPawn 初始化让服务器调用Controller生成Pawn的方法,获取BP_MapInfo的LevelKey传递给Controller
Map_Example_Persition根据Map_Info的PDA数据,打开Map_Intro地图 ,
Intro地图还有【BP_IntroScreen】即将加载Map_Menu
网络同步
GameInstance客户端和服务器端各一个,无网络复制功能
GameMode只有服务器端拥有1个
Controller服务器同玩家个数,客户端只有一个
Pawn和PlayState服务器和客户端都有,同玩家个数
渲染设置
自用打包exe
Render-Shader Permutation Reduction - Support Sky Atmosphere Fog √
Default Setting --ExtendDefault Luminance Range in auto exposure setting √
Substrate Materials √
Texture Streaming √