跳转到内容
CRUSHFORMV1.1.0
在 Fab 上获取

入门自定义车辆

自定义车辆

本页适用于不是 Chaos Vehicles Pawn 的车辆。对于 Chaos Vehicles Pawn,Crushform Chaos Vehicle Adapter 会从移动组件中读取车轮,此时请参见快速入门。

  • Actor 的根组件就是模拟的车身。凹陷和漆面都以它为基准进行测量,而凹陷需要一个形变配置文件。
  • 按照快速入门设置材质。
  • 车轮是 Actor 的组件:网格体,或者是随车轮转动的场景组件。
  • 如果使用 C++,请按照安装中的说明,将你调用的 Crushform 模块添加到你的 Build.cs 中。

适配器会在你列出的每个车轮下方对地面做射线检测。它负责创建凹陷和污垢、玻璃、轮胎、声音、道具破坏和撞击反馈,并回答它们关于车辆的问题。

  1. Add › Crushform Vehicle Adapter。将它添加到 Actor 上。
  2. 设置 UCFVehicleAdapterComponent::Wheels:每个车轮对应一个 FCFAdapterWheel。在 Details 中展开 Wheels,为每个车轮添加一个元素。在每个元素中,从 Wheel 列表中选择车轮(该列表显示 Actor 的场景组件),并将 Radius 设为轮胎半径,单位为厘米。半径为 0 时,取车轮网格体边界高度的一半。对于已经在转动的车轮,请设置其半径。
适配器的 Details 面板,Wheels 中有四个元素,每个元素都有一个 Wheel 下拉列表和 0 cm 的 Radius。
Wheels:每个车轮一个元素,每个元素中都从列表中选定其车轮。
  1. 设置 GroundChannel,即车轮追踪地面所用的通道(默认为 Visibility),以及 GroundProbeMargin,默认为 2 cm。射线从轮毂处开始,因此离地面这一距离的轮胎仍然视为接触地面。也可以在游戏中调用 UCFVehicleAdapterComponent::SetWheels,传入 FCFAdapterWheelState 值。它在 BeginPlay 之前调用也有效。
  2. 设置 Authority 和 RamAllowanceMps。Server 是默认值:服务器模拟车辆并判定其命中。选择 OwningClient 时,驾驶车辆的端模拟它并上报其命中,而服务器只在上报的速度变化量不超过车辆近期峰值速度加上 RamAllowanceMps(默认 15 m/s)时才接受该上报,并且对于更轻、会让步的车身,这一上限会相应放宽。然后设置配置文件和功能开关。留空的 DeformProfile 或 TireProfile 会取项目默认值。每个功能开关默认都是开启的;关闭某个开关会关闭它所对应的组件。
  3. 如果车辆需要对损伤作出反应,请在蓝图中绑定 OnDamageChanged。它会在车身损伤改变时触发,并且只有在 Dirt and Dents 开启时才会触发。

适配器看不到你的操控,因此需要告诉它:

  • 调用 UCFVehicleAdapterComponent::SetBraking,传入 0 到 1 之间的值。车辆制动时,滚动 FX 会扬起更多的污垢。
  • 当听者位于车厢内时,调用 UCFVehicleAdapterComponent::SetCabinMuffled 并传入 true。它会使滚动声音和发动机声音变得沉闷。
  • 发动机声音会读取五个事件:GetEngineRunState(Off、Cranking 或 Running)、GetEngineRPM、GetEngineIdleRPM、GetEngineMaxRPM 和 GetEngineLoad。适配器的默认回答是关闭和零。可以在适配器的蓝图子类中覆盖它们,或在 C++ 中覆盖。

如果你的车轮由内置射线检测之外的物理系统模拟,请覆盖适配器的四个车轮事件:UCFVehicleAdapterComponent::BuildWheelContacts、CollectWheelMeshes、BuildTireWheels 和 BuildLoadRatios。Chaos 适配器对 Chaos Vehicles 也是这样做的。

对于每个车轮,BuildWheelContacts 每帧为其填写一个 FCFWheelContact:

  • Point:轮胎与地面的接触点,世界空间,单位 cm。只有当 bGrounded 为 true 时才会读取它。
  • Radius:轮胎半径,单位 cm。
  • bGrounded:车轮悬空时为 false。悬空的车轮不会留下污垢。
  • PhysicalMaterial:车轮下方的地面。为空时取默认速率。
  • SurfaceSpeed:轮胎表面的速度,单位 m/s,轮胎向前滚动车辆时为正。
  • GroundSpeed:轮毂相对地面的速度,单位 m/s。小于 0 时,使用车辆的速度。
  • WheelIndex:车轮的编号,从 0 开始计数。在车轮的整个生命周期中保持不变。

CollectWheelMeshes 返回车轮网格体。它们会沾上污垢,但从不发生形变。BuildTireWheels 返回每个轮胎的网格体、物理半径和 HostIndex,以及 bAcceptsPush;何时设置它,见 宿主接口 指南。BuildLoadRatios 按 HostIndex 返回接触力除以名义载荷的结果;缺失或为负的条目会使该车轮的载荷系数保持为 1。

如果你的车辆有自己的组件设置,并且不想使用适配器,请使用宿主接口。你需要自己添加 Crushform 组件,并在 Actor 或它的某个组件中回答它们提出的问题。接口位于 Crushform 模块中,但 ICFTireHost 和 ICFTireViewer 位于 CrushformTire 中,ICFEngineHost 位于 CrushformEngine 中。

组件模块需要
UCFDeformComponentCrushform一个形变配置文件:没有它,组件不会做任何事。Actor 的根组件就是模拟的车身。
UCFGlassUnitsComponentCrushform玻璃板,即 Actor 的 UCFGlassComponent 子组件。Actor 的 GetCrushformGlass 返回这个组件。它需要形变组件。
UCFTireComponentCrushformTireActor 上的 ICFTireHost。没有它,轮胎保持圆形。一个轮胎配置文件,或项目默认值。
UCFRollingAudioComponentCrushformWheelActor 上的 ICFWheelHost。每种路面的声音来自项目的路面表。
UCFRollingFXComponentCrushformWheelActor 上的 ICFWheelHost。每种路面的特效来自项目的路面表。
UCFPropContactComponentCrushform将 Actor 的根基元用作车身。ICFBreakerHost 是可选的。
UCFImpactFeedbackComponentCrushformImpact它所上报命中的基元,用 SetObservedPrimitive 设置。它的 EventTag 播放声音;为空则不播放声音。
UCFEngineAudioComponentCrushformEngineActor 上的 ICFEngineHost。一个声音配置文件,或项目默认值。

在车辆的车身上,适配器使用 UCFNetImpactFeedbackComponent,而不是 UCFImpactFeedbackComponent。它只在模拟车身的那个端上上报车身的命中,其他端播放转发来的副本。

在 Add 菜单中,撞击反馈组件 CFImpact Feedback、CFNet Impact Feedback 以及铰接部件的那个组件,都列在 Custom 下,而不是 Crushform 下。

六个宿主接口:

接口提问者回答内容
ICFDeformHostUCFDeformComponent、网络撞击反馈哪个端模拟车身,忽略的 Actor,上报是否合理,哪些网格体会发生形变,车轮网格体,玻璃板
ICFWheelHost污垢层、UCFRollingAudioComponent、UCFRollingFXComponent本帧的车轮,即 FCFWheelContact 值
ICFBreakerHostUCFPropContactComponent本机是否模拟车身,接触的刚体,上报路径,被破坏单元所凹陷的形变组件
ICFTireHostUCFTireComponent车轮,写回的偏移量和推挤量,载荷比,车辆组合
ICFTireViewer轮胎预算,位于 Pawn 上乘坐该 Pawn 的车辆,徒步时为 null
ICFEngineHostUCFEngineAudioComponent发动机的状态:运行状态、转速、怠速与最高转速、负荷

组件按以下顺序查找其宿主:先是在 BeginPlay 之前用 SetCrushformHost 设置的显式宿主;然后是在 UCFHostRegistry 中为该 Actor 注册的宿主;再然后是 Actor 本身(如果它实现了该接口);否则就没有宿主。每个事件都以 ECFHostAnswer 回答:Default 表示组件按没有宿主时的情况自行决定,而 Yes 或 No 会覆盖这一决定。

  1. 打开 Actor 的蓝图,进入 Class Settings。在 Implemented Interfaces 下点击 Add,选择接口,例如 CFDeform Host,然后是 CFWheel Host。
Implemented Interfaces 列表,上方是 Add 按钮,下面列出两个接口:CFDeform Host 和 CFWheel Host。
Class Settings › Implemented Interfaces:已为 Actor 添加的 Crushform 宿主接口。
  1. 接口的函数会出现在 My Blueprint 的 Interfaces › Crushform 下,名称为去掉空格后的 C++ 名称,例如 GatherCrushformWheelMeshes。双击其中一个即可实现它。ECFHostAnswer 成员如果你没有改动,则返回 Default。列表成员如果你没有改动,则不收集任何内容。
My Blueprint 的 Interfaces 部分,Crushform 分组下列出 ShouldCrushformIgnore、ShouldCrushformDeform、IsCrushformReportPlausible、IsCrushformImpactSourceHere、GetCrushformGlass、GatherCrushformWheelMeshes、GatherCrushformIgnoredActors 和 GatherCrushformWheelContacts。
My Blueprint 在 Interfaces › Crushform 下列出接口函数。
  1. GatherCrushformWheelMeshes 有一个输出数组,因此它是一个函数。创建一个包含四个车轮网格体的数组,并将其连接到 Return 节点的输出。
四个车轮网格体组成的一个数组,连接到 Return 节点。

在 Actor 上实现这些接口,并为你回答的每个事件覆盖其 _Implementation。下面的类为一辆四轮车回答车轮网格体和车轮接触。

YourVehicle.h
#pragma once
#include "CoreMinimal.h"
#include "CFDeformHost.h"
#include "CFWheelHost.h"
#include "GameFramework/Actor.h"
#include "YourVehicle.generated.h"
class UMeshComponent;
class UStaticMeshComponent;
UCLASS()
class AYourVehicle : public AActor, public ICFDeformHost, public ICFWheelHost
{
GENERATED_BODY()
public:
virtual void GatherCrushformWheelMeshes_Implementation(TArray<UMeshComponent*>& OutMeshes) const override;
virtual void GatherCrushformWheelContacts_Implementation(TArray<FCFWheelContact>& OutContacts) const override;
/** The four wheels, in WheelIndex order. Fill it in the constructor with the wheel components you create, or in Blueprint once they exist. */
UPROPERTY(VisibleAnywhere)
TArray<TObjectPtr<UStaticMeshComponent>> Wheels;
/** Tyre radius, cm. */
UPROPERTY(EditAnywhere)
float WheelRadius = 35.f;
/** The speed of each wheel's surface, m/s, from your vehicle model. */
TArray<float> WheelSurfaceSpeeds;
};

GatherCrushformWheelContacts 的函数体从每个车轮的轮毂向下追踪,并为每个车轮填写一个 FCFWheelContact。悬空的车轮保持 bGrounded 为 false,其 Point 和 PhysicalMaterial 保持未设置。

YourVehicle.cpp
#include "YourVehicle.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/World.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
void AYourVehicle::GatherCrushformWheelMeshes_Implementation(TArray<UMeshComponent*>& OutMeshes) const
{
for (UStaticMeshComponent* Wheel : Wheels)
{
OutMeshes.Add(Wheel);
}
}
void AYourVehicle::GatherCrushformWheelContacts_Implementation(TArray<FCFWheelContact>& OutContacts) const
{
UWorld* World = GetWorld();
if (!World)
{
return;
}
// The physical material comes back only when the query asks for it.
FCollisionQueryParams Params(SCENE_QUERY_STAT(YourVehicleWheel), false, this);
Params.bReturnPhysicalMaterial = true;
for (int32 Index = 0; Index < Wheels.Num(); ++Index)
{
const UStaticMeshComponent* Wheel = Wheels[Index];
if (!IsValid(Wheel))
{
continue;
}
// Trace down from the hub, past the tyre radius.
const FVector Hub = Wheel->GetComponentLocation();
const FVector TraceEnd = Hub - FVector(0.f, 0.f, WheelRadius + 20.f);
FHitResult Hit;
FCFWheelContact Contact;
Contact.WheelIndex = Index;
Contact.Radius = WheelRadius;
Contact.SurfaceSpeed = WheelSurfaceSpeeds.IsValidIndex(Index) ? WheelSurfaceSpeeds[Index] : 0.f;
Contact.bGrounded = World->LineTraceSingleByChannel(Hit, Hub, TraceEnd, ECC_Visibility, Params);
if (Contact.bGrounded)
{
Contact.Point = Hit.ImpactPoint;
Contact.PhysicalMaterial = Hit.PhysMaterial.Get();
}
OutContacts.Add(Contact);
}
}

需要 Actor 车轮的代码应通过 Execute_ 向其宿主请求,而不是通过接口指针按名称调用事件:

#include "CFHostRegistry.h"
#include "CFWheelHost.h"
TArray<FCFWheelContact> Contacts;
if (UObject* Host = UCFHostRegistry::FindHost(this, *UCFWheelHost::StaticClass()))
{
ICFWheelHost::Execute_GatherCrushformWheelContacts(Host, Contacts);
}
  • 同一个 Actor 的同一接口若再设置第二个宿主,它会替换第一个,并给出警告。
  • 蓝图宿主在编辑器预览中不会作出回答。预览不运行它的脚本,因此 Details 中的晶格布局和区域编辑器看不到它的回答。请在 Play 中检查结果。
  • 不要通过接口指针按名称调用接口事件。请使用 Execute_ 函数,或组件的访问器。

这些是适配器的属性。适配器自身的设置位于适配器组件的 Details 中。

属性默认值作用
UCFVehicleAdapterComponent::Wheels空适配器追踪的车轮:每个车轮一个组件和一个 Radius。
GroundChannelVisibility车轮追踪地面所用的通道。
GroundProbeMargin2 cm轮胎离地多高仍视为接触地面。
AuthorityServer由哪个端模拟车辆并判定其命中。
RamAllowanceMps15 m/s使用 OwningClient 时:上报的命中速度可以比近期峰值速度高出多少。
DeformProfile项目默认值车身的凹陷和污垢。
TireProfile项目默认值轮胎。
EngineSoundProfile项目默认值发动机的声音。
bDirtAndDents、bGlass、bTires、bRollingSound、bRollingFX、bEngineSound、bImpactFeedback、bBreaksProps全部开启每个开关都会关闭它所对应的组件。
ImpactImpulsePerKgForFullIntensity600 cm/s以满强度播放的命中所对应的速度变化量。
ImpactMinImpulsePerKg50 cm/s低于此速度变化量的命中不播放任何内容。
PropContactMinSpeedCmS50 cm/s低于此速度,车辆不破坏任何道具。
  1. 打开带有你车辆的关卡,然后按 Play。
  2. 用波浪号键打开控制台,运行 Crushform.Stats。它会输出正在使用的图集槽位,以及场景中每个可受损 Actor 的命中计数器。
  3. 运行 Crushform.Debug 1。它会绘制车身晶格中受损的节点。Crushform.Debug 2 会绘制全部节点。
  4. 运行 Crushform.DebugImpact 10 <name>,以 10 m/s 命中名称包含 <name> 的 Actor,距离最远 100 m。如果不带名称,则命中准星下的 Actor。它从不命中你控制的 Pawn,因此请按快速入门第 5 步驾车撞墙来检查你自己的车辆。这是作弊指令:除 Shipping 之外的所有构建(包括编辑器)中都有它。
  5. 驶过由 PM_CF_Mud 构成的地面。车轮会按照项目路面表中为泥浆设定的方式对其产生反应。