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

世界撞击反馈

撞击反馈

轿车撞上墙、栅栏、树或窗户时,会发出声音并迸出火花。可破坏的碎片落地时,会发出一声闷响。撞击反馈就是插件中负责播放这些内容的部分。它把一次击中转换为撞击事件,查找该事件应当发出什么声音、呈现什么样的特效,并在击中的位置播放。

开启 LogHits 控制台变量后,插件会报告它判定的每一次击中。在编辑器的游戏会话中,每次击中都会留下一个持续 5 秒的标记:击中点处的一个球体,以及沿表面法线的一个箭头。撞击播放时为绿色,处于稳定接触中的击中为黄色,低于最小值的击中为红色。每个被判定的击中还会在日志中写入一行,类别为 LogCrushformImpact,格式如下:

<owner>: <component> (bone <bone>) hit by <actor> / <component>, impulse N, support N, mass N kg, other mass N kg, normal Z N, intensity N at <point>: <verdict>

判定结果为 played、below minimum 或 steady contact, suppressed 之一。

每个撞击都是一个 Gameplay 标签,例如 Crushform.Impact.Prop.Break.Wood,并在某个位置播放。UCFImpactFeedbackSubsystem 完成其余工作:

  1. 它在当前生效的撞击集合中查找该标签的行。如果集合中没有这个确切标签的行,它就使用最接近的父标签所在的行。如果没有任何行匹配,则什么也不播放。
  2. 它从该行中随机播放一个声音,并生成该行的 Niagara 特效,同时设置其 Intensity 参数。
  3. 它按该行的 Volume by Intensity 数值缩放音量,并从其 Pitch Range 中选取音高。
  4. 它应用该行的过滤器:强度低于 Min Intensity 的击中不播放任何内容;同一来源若刚刚播放过,则要等待其 Cooldown Seconds;该行的 Max Concurrent Voices 全部占用时,新的撞击会播放其特效,但不播放声音。在整个插件范围内,一帧内最多启动 Max Sounds Per Frame 个声音(默认为 2):其余的按从强到弱的顺序等待之后的帧,等待超过 Sound Queue Seconds(0.15 s)仍未播放的声音会被丢弃。如果让一次同时发出许多声音的碰撞在同一帧里启动全部声音,就会造成卡顿,所以改为把它们分散到几帧中。特效从不排队。

撞击集合是一个 UCFImpactFeedbackSet 数据资产。项目默认使用 /Crushform/Impact/DA_CF_ImpactFeedback,其中为插件的每个标签都有一行。项目可以制作自己的集合,并把 Default Set 指向它。

撞击来自附着在刚体上的组件。你在蓝图编辑器的 Add 菜单中添加它们,三者都列在 Custom 下,而不在 Crushform 下:

  • UCFImpactFeedbackComponent 报告它所观察的基元的击中,使用它所被赋予的 Event Tag(默认为 Crushform.Impact.Hit)。在你用 SetObservedPrimitive(仅限 C++)为它指定基元之前,它不观察任何东西。击中按刚体每千克的冲量判定:低于 Min Impulse Per Kg(50)时没有声音,达到 Impulse Per Kg for Full Intensity(600)时为满强度。稳定接触(例如刚体静置在地面上)不会重复触发撞击。
  • UCFNetImpactFeedbackComponent 自行观察其拥有者的根基元,并且还会把击中转发给其他玩家。转发通常至少等待 Min Relay Interval Seconds(0.5 秒),但明显更强的击中会更早转发。
  • UCFPartImpactFeedbackComponent 在铰接部件到达行程末端时播放其撞击声。部件撞向关闭端时播放 Slam Event Tag,默认为 Crushform.Impact.Part.Slam。部件被甩向敞开端时播放 Open Slam Event Tag,默认为 Crushform.Impact.Part.Slam.Open;若撞击集合中没有该标签的行,则播放 Part.Slam 行。

车辆通过其适配器获得这些组件。破坏定义会指定它自己的标签,留空则播放插件的默认标签。

插件在 CFImpactTags 中声明了 19 个撞击标签:

标签播放时机
Crushform.Impact撞击事件的根。
Crushform.Impact.Hit挂有撞击反馈组件的刚体撞上某物。
Crushform.Impact.Vehicle.Crash车辆车身撞上坚硬物体。
Crushform.Impact.Prop.Break非玻璃的可破坏道具破碎。
Crushform.Impact.Prop.Break.Wood木制道具碎裂开来,例如木制围栏段。
Crushform.Impact.Prop.Break.Metal金属道具整块脱落,例如铁丝网围栏板。
Crushform.Impact.Prop.Debris可破坏道具的一块碎片撞上某物。
Crushform.Impact.Prop.Debris.Wood木制碎片落地。
Crushform.Impact.Prop.Debris.Metal金属碎片落地。
Crushform.Impact.Glass.Crack玻璃板出现裂纹。
Crushform.Impact.Glass.Break玻璃板破碎并散开。
Crushform.Impact.Glass.Shard玻璃碎片撞上某物。
Crushform.Impact.Tree.Hit某个刚体推动可破坏的树。
Crushform.Impact.Tree.Break可破坏的树折断并开始倒下。
Crushform.Impact.Tree.Bend可破坏的电线杆弯曲并保持倾斜。
Crushform.Impact.Tree.Land倒下的树干撞击地面。
Crushform.Impact.Part.Hit车辆上绕铰链摆动或已脱落的部件撞上某物。
Crushform.Impact.Part.Slam车辆上绕铰链摆动的部件撞入行程的关闭端。
Crushform.Impact.Part.Slam.Open车辆上绕铰链摆动、被甩开的部件撞入行程的敞开端。

撞击集合中某个标签的行是一个 FCFImpactFeedbackEntry。它的 Sounds 是一个列表,其中随机播放一个,因此你无需声音 Cue 即可获得变化。Effect 是一个接收浮点参数 Intensity 的 Niagara 系统。Attenuation 决定声音随距离的衰减方式。

本节使用随附的集合,然后制作一行你自己的。

  1. 在 Content Browser 中,默认集合位于 Plugins › Crushform Content › Impact。
  2. 打开 DA_CF_ImpactFeedback。它的 Entries 列表为每个标签提供一行。找到 Crushform.Impact.Prop.Break.Wood 的那一行,即可看到一行包含哪些内容:Sounds、Effect、Attenuation、音量与音高范围,以及过滤器。
Impact
Entries18 Array elements
Index [ 0 ]Crushform.Impact.Prop.Break
Index [ 1 ]Crushform.Impact.Prop.Debris
Index [ 2 ]Crushform.Impact.Glass.Crack
Index [ 3 ]Crushform.Impact.Glass.Break
Index [ 4 ]Crushform.Impact.Glass.Shard
Index [ 5 ]Crushform.Impact.Vehicle.Crash
Index [ 6 ]Crushform.Impact.Hit
Index [ 7 ]Crushform.Impact.Tree.Hit
Index [ 8 ]Crushform.Impact.Tree.Break
Index [ 9 ]Crushform.Impact.Tree.Bend
Index [ 10 ]Crushform.Impact.Tree.Land
Index [ 11 ]Crushform.Impact.Prop.Break.Wood
Event TagCrushform.Impact.Prop.Break.Wood
Sounds5 Array elements
EffectNS_CF_ImpactWood_Break
AttenuationSA_CF_CarImpact
Volume by Intensity0.41.0
Pitch Range0.921.08
Min Intensity0.05
Cooldown Seconds0.1
Max Concurrent Voices4
Surface Overrides0 Array element
Index [ 12 ]Crushform.Impact.Prop.Debris.Wood
Index [ 13 ]Crushform.Impact.Prop.Break.Metal
Index [ 14 ]Crushform.Impact.Prop.Debris.Metal
Index [ 15 ]Crushform.Impact.Part.Hit
Index [ 16 ]Crushform.Impact.Part.Slam
Index [ 17 ]Crushform.Impact.Part.Slam.Open
DA_CF_ImpactFeedback 的 Entries,其中 Crushform.Impact.Prop.Break.Wood 那一行已展开。
  1. 要制作自己的集合,请选择 Add › Miscellaneous › Data Asset,选择类 CFImpact Feedback Set,并在 Entries 中添加一行。把它的 Event Tag 设为该标签,并填写它的声音和特效。
  2. 打开 Project Settings › Plugins › Crushform Impact Feedback,把 Default Set 设为你的集合。集合只需要包含你希望听到的标签的行。没有行的标签会使用其最接近父标签的行,因此 Crushform.Impact 的一行会覆盖所有没有自己行的标签。
  3. 要为车辆提供撞击,请保持适配器上的 Impact Feedback 开启(默认即为开启)。它的 CF_ImpactFeedback 会播放适配器的 Impact Event Tag。CF_PartImpactFeedback 保留它自己的标签,并且只有在 Dirt and Dents 也开启时,适配器才会创建它。组件表见核心概念。

在你自己的项目中,蓝图通过 Play Impact 节点播放撞击。它接收标签、位置、法线(默认朝上)、0 到 1 的强度、表面,以及一个可选的来源。来自同一来源的撞击共享一个冷却时间。下方的蓝图在自定义事件触发时,在 Actor 的位置播放一次树木折断的撞击。

当自定义事件触发时,蓝图在 Actor 的位置播放一次树木折断的撞击。

Play Impact 只在调用它的端上播放,在专用服务器上则完全不播放。希望每位玩家都听到的游戏,应当在每个端上调用它,例如从一个多播事件中调用。

在 C++ 中,同样的功能通过库函数实现,标签来自 CFImpactTags。这段代码需要在你的 Build.cs 中加入 CrushformImpact 模块;模块列表见安装。

#include "CFImpactFeedbackLibrary.h"
#include "CFImpactTags.h"
void PlayTreeBreakHere(const UObject* WorldContext, const FVector& Location)
{
UCFImpactFeedbackLibrary::PlayImpact(WorldContext, CFImpactTags::Impact_Tree_Break, Location);
}

签名为 static void PlayImpact(const UObject* WorldContextObject, FGameplayTag Tag, FVector Location, FVector Normal = FVector::UpVector, float Intensity = 1.f, EPhysicalSurface Surface = SurfaceType_Default, const UObject* Source = nullptr)。UCFImpactFeedbackSubsystem::Get(WorldContext)->PlayImpact(FCFImpactEvent) 是同一调用的更底层版本,适用于自己构建事件的代码。

项目设置位于 UCFImpactFeedbackSettings 上。行和组件有各自的数值,列在下面。默认值来自插件本身。

属性作用默认值
UCFImpactFeedbackSettings::DefaultSet播放的撞击集合。/Crushform/Impact/DA_CF_ImpactFeedback
UCFImpactFeedbackSettings::MaxAudibleDistanceCm超过此距离(厘米)时不播放声音。6000
UCFImpactFeedbackSettings::MaxEffectDistanceCm超过此距离(厘米)时不生成特效。8000
UCFImpactFeedbackSettings::MinVoiceSeconds一次播放的声音占用声音通道的最短时间。0.25
UCFImpactFeedbackSettings::MaxVoiceSeconds一次播放的声音占用声音通道的最长时间。3
UCFImpactFeedbackSettings::MaxSoundsPerFrame一帧内启动的声音数量上限。其余的按从强到弱的顺序等待之后的帧。0 表示关闭该上限。2
UCFImpactFeedbackSettings::SoundQueueSeconds超出该帧上限的声音最多可以等待多久;超过则该声音被丢弃。0.15
UCFImpactFeedbackSettings::IgnoredHitActorClasses被其中某个类的 Actor 击中时,不产生撞击。空
UCFImpactFeedbackSettings::bUseDefaultBreakableHost插件是否把可破坏的反馈映射为撞击事件。true
FCFImpactFeedbackEntry::VolumeByIntensity强度为 0 和强度为 1 时的音量倍数。(0.4, 1.0)
FCFImpactFeedbackEntry::PitchRange随机音高倍数的范围。(0.92, 1.08)
FCFImpactFeedbackEntry::MinIntensity低于此强度的撞击不播放任何内容。0 关闭该过滤器。0
FCFImpactFeedbackEntry::CooldownSeconds同一来源两次撞击之间的时间。0.1
FCFImpactFeedbackEntry::MaxConcurrentVoices该行的声音同时播放的最大数量。4
UCFImpactFeedbackComponent::ImpulsePerKgForFullIntensity以满强度播放的每千克冲量。600
UCFImpactFeedbackComponent::MinImpulsePerKg低于此每千克冲量的击中没有声音。50
UCFImpactFeedbackComponent::ContactGapSeconds组件的下一次击中被视为新接触之前的静默时长。0.1
UCFImpactFeedbackComponent::ContactSpikeRatio在一次接触中,击中只有在其冲量达到当前冲量的此倍数时才播放。2
UCFNetImpactFeedbackComponent::MinRelayIntervalSeconds该刚体发送给其他玩家的两次撞击之间的最短间隔;明显更强的击中会更早发送。0.5
UCFPartImpactFeedbackComponent::OpenSlamEventTag部件撞入行程敞开端时的撞击事件。为空则在那里没有声音。Crushform.Impact.Part.Slam.Open
UCFPartImpactFeedbackComponent::SlamSpeedForFullIntensity部件以满强度撞击时的速度,单位 cm/s。500
UCFPartImpactFeedbackComponent::MinSlamSpeed低于此速度(cm/s)的撞击没有声音。40

在除 Shipping 之外的任何构建中,用波浪号键打开控制台。

  • crushform.Impact.LogHits 1 开启标记。让轿车撞上墙、栅栏或树。每个被判定的击中都会留下一个约 5 秒的标记:撞击播放的接触撞击为绿色,稳定接触中的击中为黄色,低于最小值的击中为红色。用 crushform.Impact.LogHits 0 关闭它们。默认值为 0。
  • stat CFImpactFeedback 显示撞击的周期。
  • 用车撞墙。你会听到声音,并在接触处看到特效。

专用服务器上没有任何声音,或其他玩家听不到

Section titled “专用服务器上没有任何声音,或其他玩家听不到”

原因。 专用服务器完全不播放撞击。Play Impact 调用只在发起调用的端上播放。组件产生的撞击只有通过 UCFNetImpactFeedbackComponent 转发,才会到达其他玩家。

修复。 在每个应当听到它的端上调用 Play Impact。若刚体的撞击应当被其他玩家听到,请在它上面使用 UCFNetImpactFeedbackComponent。

原因。 当前集合中没有该标签的行,也没有它任何父标签的行,因此 UCFImpactFeedbackSet::FindEntry 找不到任何东西。组件或适配器的标签被手动清空时,同样不会播放任何内容。

修复。 在集合中为该标签添加一行,或为其最接近的父标签添加一行。把组件或适配器上的标签恢复,或将其留空,以播放插件的默认标签。

原因。 有两种情况。UCFImpactFeedbackComponent 在 C++ 中调用 SetObservedPrimitive 之前不观察任何基元,因此仅在蓝图中添加的组件会保持静默。UCFNetImpactFeedbackComponent 会观察其拥有者的根基元;如果该根不是基元,它会记录下面这行日志并保持静默:

%s: owner %s has no primitive root; its impacts stay silent

修复。 对于基础组件,请在 C++ 中用 SetObservedPrimitive 为它指定基元。对于应当被其他玩家听到的刚体,请使用 UCFNetImpactFeedbackComponent,并确保拥有者的根组件是基元,例如刚体的网格体。

击中没有声音,或只播放了特效

Section titled “击中没有声音,或只播放了特效”

原因。 击中低于 Min Impulse Per Kg(50)或低于该行的 Min Intensity,因此没有声音。或者该行的 Max Concurrent Voices 全部被占用,因此撞击只播放特效,没有声音。

修复。 降低该行的 Min Impulse Per Kg 或 Min Intensity,或提高 Max Concurrent Voices。开启 crushform.Impact.LogHits 1,查看击中显示为红色(低于最小值),还是根本没有被判定。

稳定接触一次又一次地播放撞击

Section titled “稳定接触一次又一次地播放撞击”

原因。 只有当击中在其接触中是一个尖峰时,组件才会播放新的撞击:即它的冲量至少是当前冲量的 Contact Spike Ratio(2)倍。保持冲量不变的接触不会重复。超过 Contact Gap Seconds(0.1 秒)仍断开的接触,会被视为新的接触。

修复。 如果单次撞击被拆分成了好几次,请把 Contact Gap Seconds 提高到几个物理步长以上。除非稳定接触仍然重复,否则请保持尖峰比率的默认值。

关于这些击中所源自的破坏,参见围栏与道具和可破坏的树木与电线杆。车辆自身的撞击声音设置在其适配器上,见核心概念。