损伤玻璃
玻璃
Crushform 车身的每一块玻璃板都可能开裂、碎裂,并散落成玻璃碎片。车窗附近的命中会使其开裂,更猛烈的命中会使其碎裂;车身在玻璃板周围凹陷时,也可能使其开裂。裂纹会一直留在玻璃板上,直到车身被修复。
玻璃板是车身上的一个静态网格体,即一个 UCFGlassComponent。它没有自己的碰撞。射击或投掷的石块会击中车窗处的车身碰撞体,Crushform 会把这次命中匹配到它所到达的那块玻璃板,匹配范围为 Hit Reach Cm(UCFGlassDefinition::HitReachCm,新定义中为 15 cm)以内。
每块玻璃板是一个单元,有三个等级:完好、开裂和碎裂。玻璃板按名称编号,因此各端对每块玻璃板的对应关系一致。
有三种情况可以使玻璃板开裂。玻璃板的定义为每一种情况设定阈值:
- 命中。 玻璃板附近的命中,其速度变化量达到 Crack Delta V(
UCFGlassDefinition::CrackDeltaV)时使其开裂,达到 Break Delta V(UCFGlassDefinition::BreakDeltaV)时使其碎裂。低于开裂阈值的命中对玻璃没有影响。 - 车框。 车身在凹陷时会向玻璃板周围弯曲(凹陷与晶格对凹陷有说明)。玻璃板盒体内所有节点中位移最大的那个,达到 Frame Crack Displacement Cm 时使玻璃板开裂。这些节点的平均位移达到 Frame Break Displacement Cm 时使其碎裂。
- 关联部件。 车身的某个部件,例如挡风玻璃前方的引擎盖(铰接部件对铰接部件有说明),可以通过玻璃板的 Crush Link Box(
UCFGlassComponent::CrushLinkBox)使其开裂。该盒体内最深的节点会在离它最近的边缘处使玻璃板开裂,平均值则使其碎裂。
车框和关联部件只有在车身凹陷时才会评估玻璃。因此,玻璃需要车辆适配器上的 Dirt and Dents 开启。适配器通过它找到玻璃板。关于适配器和晶格,参见核心概念。
碎裂前的开裂次数。 将 Cracks Before Break(UCFGlassDefinition::CracksBeforeBreak)设为某个数值,可使玻璃板在被命中开裂该次数后碎裂。设为 0 时,玻璃板不会按次数碎裂。只有命中计入次数,车框不计入,因此一个保留下来的凹陷不会随时间使玻璃板碎裂。
裂纹外观。 每块玻璃板最多保留四道裂纹。裂纹的半径随命中强度增长,范围在 Min Crack Radius Cm 与 Max Crack Radius Cm 之间。Crack Style 设定图案:网状、孔状或网格状。
外部命中。 子弹或投掷的石块只有在 Takes Point Damage(UCFGlassDefinition::bTakesPointDamage)开启时,才能使玻璃板开裂。随后,定义中的 Hit Responses 表决定每一种命中使玻璃板达到哪个等级。表中没有对应行的命中种类不会产生任何效果(UCFBreakableDefinition::HitResponses)。
碎裂。 碎裂的玻璃板会被隐藏,并散落成玻璃碎片。玻璃板被方形分块覆盖,离命中点最近的分块优先。每个分块的边长为 Shard Tile Cm,碎裂成 Shards Per Tile 个碎片。至多 Max Shard Tiles 个分块会变成碎片,因此碎片最多覆盖玻璃板上这么多个分块,其余部分则直接消失:五个 50 cm 的分块覆盖 1.25 m²,夹层玻璃预设即如此。每个分块会绕玻璃板的朝外轴旋转一个随机角度,这样两个分块的碎片不会排成一条线。Shard Shape 决定碎片的形状:颗粒是小块,如钢化玻璃;板块是平片,如碎裂而不飞散的夹层玻璃。每个碎片都是碎片网格体之一,随机选取并缩放到该碎片的大小。插件自带六块不规则的多边形板,从 SM_CF_GlassShard_01 到 SM_CF_GlassShard_06,用 M_CF_GlassShard 绘制,那是边缘更亮的透明玻璃。每块碎裂的玻璃板都以同一个 Niagara 系统的粒子来抛出它的碎片,详见下文的“碎片系统”。
谁负责绘制。 服务器判定玻璃的状态,并复制裂纹,每一个绘制裂纹的端都会显示它们。专用服务器判定玻璃板,但不绘制任何内容,也不加载任何碎片系统。修复会使每块玻璃板恢复完整,并清除其裂纹。
- 打开车辆的蓝图,例如你项目中
BP_VehicleAdvSedan的副本。选中它的 Crushform Chaos Vehicle Adapter 组件。确认 Dirt and Dents 和 Glass 已开启。两者默认都是开启的。 - 创建一个玻璃定义。在 Content Browser 中选择 Add > Miscellaneous > Data Asset,选择 CFGlass Definition,然后为其命名。你也可以从 Plugins › Crushform Content › Presets 中复制一个自带的预设:
DA_CF_GlassLaminated或DA_CF_GlassTempered。 - 检查定义中的碎片设置。没有碎片的定义会发出警告:碎裂的玻璃板会直接消失,而不产生任何碎片。请在编辑器工具蓝图或 Python 中调用 Build Glass Shards(
UCFContentLibrary::BuildGlassShards,位于 Crushform|Content 分类)来生成碎片。该调用需要传入定义和一个碎片网格体列表;插件自带六个,由 Build Glass Shard Mesh(UCFContentLibrary::BuildGlassShardMesh)生成。
| Glass | ||
|---|---|---|
| Crushform Hits | ||
| Crack Delta V | 5.0 | |
| Break Delta V | 14.0 | |
| Hit Reach Cm | 20.0 | |
| Frame Crack Displacement Cm | 4.0 | |
| Frame Break Displacement Cm | 12.0 | |
| Link Crack Displacement Cm | 30.0 | |
| Link Break Displacement Cm | 15.0 | |
| Cracks Before Break | ||
| Look | ||
| Crack Style | ||
| Min Crack Radius Cm | 10.0 | |
| Max Crack Radius Cm | 45.0 | |
| Crack Merge Cm | 12.0 | |
| Shards | ||
| Shard Shape | ||
| Shard Tile Cm | 50.0 | |
| Shards Per Tile | 12 | |
| Shard Min Size Cm | 3.0 | |
| Shard Max Size Cm | 9.0 | |
| Shard Thickness Cm | 0.6 | |
| Max Shard Tiles | 5 | |
- 为每扇车窗添加一块玻璃板。在车辆的蓝图中,选择 Add › CFGlass,位于 Crushform 分组中。将其 Static Mesh 设为车窗的网格体,将 Definition 设为你的玻璃定义。把玻璃板附着在车身下,或附着在它所在的铰接部件下。
- 打开车窗的静态网格体,开启 Allow CPU Access。若不开启,弯曲或倾斜车窗的碎片会落在一个平面上,因为插件无法读取车窗的形状。
- 可选:设置玻璃板的 Crush Link Box,例如像轿车那样设为挡风玻璃周围的区域。对于引擎盖会折叠进去的玻璃板,也可以选择引擎盖。在 Crush Link Box 上勾选 Is Valid,然后以厘米为单位,在根组件的空间中设置其 Min 和 Max。盒体无效时,不会关联任何内容。在蓝图编辑器中选中玻璃板,即可在视口中看到绘制的盒体。该盒体只在蓝图编辑器中绘制,关卡和 PIE 中不显示。
用 Get Glass 从车辆适配器获取玻璃单元。只有 Glass 和 Dirt and Dents 都开启时,它才会返回结果。然后将 On Unit Level Changed(UCFBreakableUnitsComponent::OnUnitLevelChanged)绑定到一个自定义事件。该事件传递 Units、UnitIndex、Level 和 PreviousLevel。
每当某块玻璃板的等级发生变化时,每一端都会触发该事件。这也包括中途加入的玩家,他会收到已经开裂或碎裂的玻璃板状态。因此,应在处理函数中检查 Level,而不要假定该事件就意味着碎裂。不要由它启动一次性的特效。若要读取车身每一侧的凹陷程度,请使用损伤区域与事件。
在 C++ 中,可以让离某个点最近的玻璃板开裂或碎裂。这仅在权威端生效。
#include "CFScriptedHits.h"
// Authority only: crack the pane nearest WorldPoint, within 50 cm of it.const bool bCracked = CFScriptedHits::HitPaneNear(*Deform, WorldPoint, 50.0f, /*bBreak=*/false);Deform 是车身的 UCFDeformComponent,WorldPoint 是世界空间中的 FVector。将 bBreak 设为 true,则改为使该玻璃板碎裂。如果没有玻璃板在该距离之内,或者什么都没有改变,函数返回 false。
以上是新定义的数值。自带的预设有所不同。轿车使用的 DA_CF_GlassLaminated 在 5 m/s 开裂,在 14 m/s 碎裂,覆盖范围为 20 cm。它的分块为 50 cm,每块 12 个 3 到 9 cm 的碎片,最多 5 个分块。DA_CF_GlassTempered 在 6 m/s 开裂,在 9 m/s 碎裂。它的分块为 30 cm,每块 16 个 1 到 2.5 cm 的碎片,最多 4 个分块。
每块碎裂的玻璃板都通过一个取自对象池的 Niagara 系统来抛出它的碎片。该系统在播完时、玻璃板被修复时以及游戏结束时回到池中。距离本地摄像机超过 UCFBreakableSettings::DebrisMaxSpawnDistanceCm(10000 cm)的玻璃板不抛出碎片。
定义可在 Shard System(UCFGlassDefinition::ShardSystem)中指定自己的系统。没有 Shard System 的定义,使用项目在 Project Settings › Plugins › Crushform 的 Glass 分组中的 Default Glass Shard System。插件将它设为 NS_CF_GlassShards。默认系统在游戏开始时于后台加载。在它进入内存之前,碎裂不会抛出碎片,而游戏也绝不会在碎裂的那一帧里等待它。定义自己的系统随定义一起加载,因此没有这段空档。
网格体槽位。 每个碎片从一个网格体槽位取得它的网格体,槽位是一个静态网格体用户参数:User.CF_ShardMesh0、User.CF_ShardMesh1,依此类推。槽位 0 取定义中第一个不同的碎片网格体,槽位 1 取第二个,顺序即这些网格体首次出现在各碎片中的顺序。默认系统有六个槽位,从 CF_ShardMesh0 到 CF_ShardMesh5。第七个不同的网格体复用槽位 0,第八个复用槽位 1,依此类推。定义自己的系统需要为每个不同的网格体准备一个槽位。当没有 Shard System 的定义拥有六个以上不同的碎片网格体时,编辑器会发出警告。系统中没有的槽位不会绘制任何网格体。
碎片声音。 当碎片撞上物体时,玻璃板会为其中最快的那个播放碎片撞击声,最多每 0.25 s 一次(UCFBreakableSettings::DebrisImpactCooldownSeconds)。撞击小于 UCFBreakableSettings::DebrisMinImpulsePerKg 的碎片不播放任何声音。自定义系统需要下文所述的 Export 数据接口才能发出这种声音。
自定义系统必须公开什么。 请按下列名称为系统提供这些用户参数。插件在每次生成时都会设置其中每一个,因此被复用于另一块玻璃板的池中系统,不会保留上一块玻璃板的任何数值。系统中没有的参数会被忽略。
| 参数 | 类型 | 含义 | 单位 |
|---|---|---|---|
User.CF_ShardPositions | 位置数组 | 每个碎片在世界空间中的中心。 | cm |
User.CF_ShardRotations | 四元数数组 | 每个碎片在世界空间中的旋转。 | 无 |
User.CF_ShardVelocities | 向量数组 | 每个碎片的初始线速度。 | cm/s |
User.CF_ShardAngularVelocities | 向量数组 | 每个碎片绕世界坐标轴的初始自旋。 | deg/s |
User.CF_ShardScales | 向量数组 | 每个碎片的缩放。它与碎片网格体的大小相乘。 | 无 |
User.CF_ShardMeshIndices | 整数数组 | 每个碎片取用网格体的槽位。 | 槽位编号 |
User.CF_ShardCount | 整数 | 本次生成中的碎片数量。 | 无 |
User.CF_ShardLifetime | 浮点数 | 碎片最多存活的秒数。 | s |
User.CF_ShardRestSeconds | 浮点数 | 静止的碎片在下沉前一动不动躺着的秒数。 | s |
User.CF_ShardSinkSeconds | 浮点数 | 碎片下沉消失所需的秒数。 | s |
User.CF_ShardCollisionDelay | 浮点数 | 生成后多少秒,系统才让碎片参与碰撞。插件设为 0.1。 | s |
User.CF_ShardCallback | 对象 | 系统的 Export 数据接口把每次碎片命中报告给的回调。 | 无 |
User.CF_ShardMesh0、User.CF_ShardMesh1…… | 静态网格体 | 槽位 0、槽位 1 等的网格体。 | 无 |
碎片声音还需要一个 Export 数据接口,把每个粒子的位置和速度报告给 User.CF_ShardCallback。没有它,碎片照样飞散,只是少了它们的声音。
三个控制台变量设定玻璃抛出多少碎片以及何时抛出:
Crushform.Glass.ShardScale(1)是一块碎裂玻璃板的碎片中被抛出的比例,范围 0 到 1。0 表示一个也不抛,玻璃板仍会碎裂。Crushform.Glass.MaxLiveShards(512)是碎裂玻璃板的碎片同时存活的最大数量。0 表示不设上限。超过上限时,最旧的碎裂玻璃板的碎片最先被释放,而同一次碎裂自己的碎片超出上限的部分会被裁掉。Crushform.Glass.ShardSystemsPerFrame(1)是一帧内启动的碎片系统数量,0 表示不限。一起碎裂的玻璃板,抛出碎片的时间会相差一帧或几帧,这样碰撞的那一帧就不会把它们全部启动。
每一个的开销见性能页面。
检查是否正常工作
Section titled “检查是否正常工作”在除 Shipping 之外的任何构建中,于编辑器内运行。Crushform.DebugImpact 不会命中你驾驶的车辆,因为轨迹会跳过它。开车靠近一辆停放的车,例如 Proving Ground 地图上的 BP_VehicleAdvSedan2,并将视野中心对准它的挡风玻璃。输入 Crushform.DebugImpact 8,速度变化量为 8 m/s。车窗上应出现裂纹,并听到开裂声。先设置 crushform.Impact.LogHits 1,即可记录每次命中的位置和强度。
然后在同一车窗上输入 Crushform.DebugImpact 15。在 15 m/s 时,玻璃板碎裂:它消失,碎片从车身上飞散。Crushform.RepairAll 会让玻璃板恢复完整。
- 车窗附近的命中没有留下裂纹。 命中低于 Crack Delta V(新定义中为 5 m/s),或车辆适配器的 Dirt and Dents 已关闭,因此车框和关联部件从不评估玻璃。请加大命中力度,或降低 Crack Delta V,或开启 Dirt and Dents。
- 碎裂的车窗消失了,但没有碎片。 定义中没有碎片。请按设置第 3 步,使用 Build Glass Shards 生成碎片。
- 子弹穿过车窗却没有留下裂纹。 Takes Point Damage 关闭,或定义的 Hit Responses 表中没有该命中种类的行。请开启该开关,并为该种类添加一行。
- 弯曲车窗的碎片飞向错误的平面。 车窗的静态网格体没有开启 Allow CPU Access,因此插件会为其套上一个平面盒体。请开启该选项,然后重新生成碎片。
- 游戏刚开始就碎裂时没有碎片。 默认碎片系统在游戏开始时于后台加载,在它进入内存之前,碎裂不会抛出碎片。请把定义的 Shard System 设为它自己的系统,该系统随定义一起加载。默认系统进入内存后,之后的碎裂就会照常抛出碎片。
- 多块玻璃板同时碎裂时,缺了一些碎片。 玻璃碎片不使用碎片池,因此 Max Live Debris Pieces(
UCFBreakableSettings::MaxLiveDebrisPieces)并不限制它们。所有碎裂玻璃板的存活碎片,受Crushform.Glass.MaxLiveShards限制,默认为 512。超过上限时,最旧的碎裂玻璃板的碎片最先消失,也就是提前消失。使用自带预设时,一块夹层挡风玻璃最多碎裂成 12 × 5 = 60 个碎片,一块钢化侧窗最多碎裂成 16 × 4 = 64 个,所以约八块夹层挡风玻璃就会达到默认上限。将上限设为 0 即不设上限。碎片所用的碎片池,见碎片。 - 专用服务器上没有裂纹。 这是设计如此。专用服务器判定玻璃,但不绘制任何内容。请在客户端上查看裂纹。

