车辆主要功能实现
策划好车辆想要实现的具体功能后,可以去任意的模型网站(这里推荐Sketchfab 网站链接:https://sketchfab.com/feed)搜寻心仪的适合的车辆模型资源。具体的下载方法可以参见您的第一个Idea VR项目。下面就针对案例中整个车辆具有的功能模块从四个方面依次进行说明。
车辆关键节点调试
下载好的模型结构方面不一定非常标准,一般都需要进行不同程度的调整。
车辆驾驶功能需要以车身刚体(VehicleBody)作为根节点,它是基于物理引擎中常见的射线投射的车辆系统。你需要为车的主体添加一个碰撞形状(CollisionShape),并为车轮添加车轮(VehicleWheel)节点。导入模型后需将车身和车轮结构分布放置于车身刚体(VehicleBody)和车轮(VehicleWheel)节点下层。
节点之间层级关系如图所示:
具体步骤如下:
导入车辆模型,检查方向盘模型结构
首先新建一个空场景,导入下载好的车辆模型。IdeaXR既支持将素材直接拖入编辑器窗口导入,也支持将模型从文件面板导入。不了解的可以参见您的第一个Idea VR项目
导入后将模型摆放如图
在场景面板中选择导入的车辆模型的节点,鼠标右键,在弹出的面板中选择使用本地。这样就会暴露出车辆模型的全部子节点。
确认车轮、车门、方向盘等结构是否与主体分离,轴心是否符合车辆驾驶的动作要求。尤其是方向盘的结构需要注意观察,看其转动中心是否在几何中心并可以绕着转向轴/转向柱转动。 找到车辆中方向盘的节点,选取后车辆模型中会有对应显示,双击方向盘节点进行快速定位
定位到方向盘之后确认方向盘的旋转效果
VehicleBody和VehicleWheel节点的建立
选中车辆模型节点对其车身添加车身刚体(VehicleBody)子节点,点击场景面板中的 + 号,在搜索栏进行搜索选取确认点击新建。
为了给新建的车身刚体合适的碰撞形状,找到能覆盖车身大部分构件的Meshinstance类型子节点,选中,再点击主窗口右上角的网格
可以选择第3、4、5行中的任意一个。具体区别可以将鼠标放置在该选项中进行查看,或查看网格工具生成碰撞形状,再将该碰撞形状移位到刚刚建立的车身刚体节点下。到此,车身的碰撞就生成了。
接下来配置车轮,可以在车身刚体节点下建立对应四个车轮的VehicleWheel子节点,同样是点击“+”号,可以搜索wheel关键字快速寻找,也可以熟悉一下各个节点使用鼠标滚轮慢慢寻找。建立节点层级关系如下,可以根据车轮位置对新建的车轮节点重命名便于以后寻找对应车轮。
调整节点位置
一定要将车身刚度选中后显示的Z轴箭头调至与车头朝向一致,刚刚建立的车轮(VehicleWheel)节点朝向(蓝色箭头)也要相同,并将车轮(VehicleWheel)放置到各个车轮对应的位置,设置好属性面板中的车轮半径贴合车轮物理模型节点。之后分别将对应的车轮模型节点拖拽到车轮(VehicleWheel)节点下面。调整完成后的层级如下
车辆驱动控制
之前我们已经完成了关键节点的建立和位置摆放,要想车辆动起来离不开驱动控制,可以给任意一个包含车辆所有零件和构件的节点添加脚本文件,我们在这里将脚本文件添加给了车身刚体(VehicleBody),编写控制车辆运动的基本方法。
先给一些基本参数赋予初值,用export
方法将其显示在属性面板上便于快速更改
export var enable_drive = false #控制车辆能否启动
export var MAX_ENGINE_FORCE = 150.0 #引擎力最大值
export var MAX_BRAKE_FORCE = 15.0 #刹车力最大值
export var MAX_STEER_ANGLE = 0.4 #转向角度最大值
export var steer_speed = 1.0 #转向速度
其他内置初始值
var steer_target = 0.0
var steer_angle = 0.0
var steer_val = 0.0
var is_ready = false
直线驾驶操控功能模块
func _physics_process(delta):
var throttle_val = 0.0
var brake_val = 0.0
if enable_drive:
if Input.is_action_pressed("move_forward"):
throttle_val = 1.0 #对应键盘的W键控制车辆前进
if Input.is_action_pressed("move_backward"):
brake_val = 1.0 #对应键盘的S键控制车辆后退
if Input.is_action_pressed("move_left"):
steer_val = 1.0 #对应键盘的A键控制车辆左转
elif Input.is_action_pressed("move_right"):
steer_val = -1.0 #对应键盘的D键控制车辆右转
else:
steer_val = 0.0
else :
steer_val = 0.0
车辆制动功能
var val = throttle_val - brake_val
if Input.is_action_pressed("ui_select"): #使用空格键进行制动
val = 0
brake = 0.5 * MAX_BRAKE_FORCE
else:
brake = 0.5
车辆马力设定
engine_force = val * MAX_ENGINE_FORCE
转弯驾驶效果优化操控功能模块
steer_target = steer_val * MAX_STEER_ANGLE
if (steer_target < steer_angle):
steer_angle -= min(steer_speed * delta,steer_angle-steer_target)
elif (steer_target > steer_angle):
steer_angle += min(steer_speed * delta, steer_target-steer_angle)
steering = steer_angle #车辆转向角
到这里基础控制功能就都已经实现完成了,但是先不要着急,对于之前建立的车轮(VehicleWheel)节点还需要我们进行一步关键操作,要设定好前轮作为导向轮,至于驱动轮实现牵引就随你喜好了,想让车辆是四驱就可以全部选中使用牵引。具体位置选项在属性面板上直接勾选就行,是不是很方便呢?
目前你的小汽车就已经具备“嘀嘀嘀”开起来的能力了,是不是很有成就感?不过还没有结束,为了不让车辆从我们的视野中消失,需要在车身刚体(VehicleBody)节点下面添加一个相机节点,相机可以任意摆放成你喜欢舒适的视角。这时候随便搭个场景你的“小汽车”就可以随意驰骋了,但为了让我们实现更好的驾驶体验继续跟着以下步骤完善它吧。
车辆驱动效果优化
除去车辆想要开动起来所必须的驾驶操作方法外,为了更好的驾驶体验,更贴近物理世界真实的车辆驾驶效果。我们还可以给车辆节点添加一些我们想要的效果,在这个案例中我们就给车辆模型添加了音效、尾气、方向盘转动等效果。 下面是一些具体的实现过程
音效和尾气粒子效果
创建并重命名之后的节点层级如下
音效
首先创建两个3D音频播放器的节点,将相应MP3格式音频文件拖拽到属性面板的音频流中。具体图示如下
打开之前在车身刚体(VehicleBody)上创建的脚本,可以根据制动键是否相应对刹车音频(stop)的播放进行控制,根据车辆的驾驶速度对行驶音频(driving)进行控制。代码编写可以添加在之前制动功能的代码块里,具体如下
var val = throttle_val - brake_val
if Input.is_action_pressed("ui_select"):
val = 0
$stop.play()
brake = 0.5 * MAX_BRAKE_FORCE
else:
brake = 0.5
$stop.stop()
if linear_velocity.length() > 3.0 :
$driving.play()
else :
$driving.stop()
尾气
想要实现车辆驾驶时具有一个排放尾气的效果,我们可以去资源中的粒子库调用喷雾效果,直接将喷雾粒子拖拽到相应车辆的排气筒位置,位置摆放合适之后就可以将喷雾节点放置到节点层级图所示的位置。
为了方便控制,我们将两个喷雾节点挂靠在一起,让某一个喷雾节点成为另一个的子节点。并对喷雾节点的一些属性进行编辑,调试成想要的效果,大小要与车辆模型的比例相匹配。节点调节完毕后,在脚本中完成对尾气效果显现的控制。
代码如下:
func _ready():
is_ready = true
set_camera(use_car_camera)
$"喷雾".hide() #添加在最开始执行的代码块里确保在最开始没启动的时候是没有尾气效果的
func _physics_process(delta):
var throttle_val = 0.0
var brake_val = 0.0
if enable_drive:
if Input.is_action_pressed("move_forward"):
throttle_val = 1.0
$"喷雾".show() #放在驾驶操控的功能模块中,加油门的时候触发排放尾气的效果
else :
$"喷雾".hide() #停止油门进给的时候也适当停止尾气效果
方向盘、转速仪表盘转动效果
想要实现方向盘跟随车轮转动一起实现转向的效果,之前方向盘模型结构检查的步骤必不可少,借助于之前的工作我们可以非常方便的完成方向盘的转动效果。而标识转速的仪表盘指针的转动可以通过跟车辆模型的移动速度挂靠起来成比例关系转换成角度进行相应转动效果。
以上效果只需要两行代码就可以实现了。
#方向盘转动效果控制
$Steering_Wheel.rotation.z = -steer_angle
#仪表盘转速指针的转动控制
$"Sketchfab_model/RootNode/Speedo Needle/Speedo_Needle_point".rotation.y=linear_velocity.length() / 5 - 0.5
相机控制——机位切换、视角切换
为了方便操控车辆模拟不同状态的观察视角,我们在这个案例中给车辆模型添加了多个相机节点。
相机与车辆模型间的节点层级关系如下:
建立好以上相机节点之后将相机依次摆放调试到合适的位置,案例中将1、2、3号相机摆放如最开始的案例演示所示,可以作为相机摆放位置的参考。
相机切换的脚本实现,可以通过键盘上方的数字1、2、3进行不同相机视角的切换
export(String, "驾驶视角", "追踪视角", "侧方视角") var use_car_camera setget set_camera
func _ready():
is_ready = true
set_camera(use_car_camera)
#具体的切换相机的功能实现
func set_camera(s):
use_car_camera = s
if not is_ready:
return
if use_car_camera == "驾驶视角":
$相机.global_transform #获取1号相机节点作为驾驶视角的相机
$相机.current = true
elif use_car_camera == "追踪视角":
$相机2.global_transform #获取2号相机节点作为追踪视角的相机
$相机2.current = true
elif use_car_camera == "侧方视角":
$相机3.global_transform #获取3号相机节点作为侧方视角的相机
$相机3.current = true
#设置数字按键切换
func _unhandled_key_input(event):
if event.scancode == KEY_1 and event.pressed:
set_camera("驾驶视角")
if event.scancode == KEY_2 and event.pressed:
set_camera("追踪视角")
if event.scancode == KEY_3 and event.pressed:
set_camera("侧方视角")
if event.scancode == KEY_4 and event.pressed:
enable_drive = not enable_drive #强制启动车辆按键切换