世界围栏与道具
围栏与道具
围栏是沿关卡中一条样条线排列的一行立柱和段。车辆撞上围栏的某个单元时,会把该单元破坏:栅栏条碎裂,铁丝网板倒塌。车辆会损失一部分速度,破碎的部分则作为碎片抛向空中。
Proving Ground 上有两段这样的围栏:一段木制栅栏和一段铁丝网围栏。两者分别随附为 BP_CF_FencePicket 和 BP_CF_FenceChainLink。
围栏是一个 ACFBreakableFence Actor。它的 Spline 就是这条线。Actor 沿这条线放置立柱和段,每个立柱或段就是一个单元。单元有一组等级,从完好到最后一种破损形态。击中会提高单元的等级,最高可达该单元的最高等级。
- 定义。 单元由
UCFBreakablePropDefinition类的数据资产构成。围栏为它的段使用一个定义,如果这条线有立柱,还可以再为立柱使用一个定义。定义包含网格体以及各项规则:如何破碎、车辆会损失多少、何时重生。 - 布局。 在编辑器中构建围栏时,这条线会变成单元。布局随 Actor 一起保存。在联网游戏中,围栏不会重新布局:它的布局来自已保存的那一份。
- 烘焙碎片。 单元抛出的碎片,由其完好网格体通过定义上的 Bake Pieces 生成。烘焙会把它们作为资产保存在定义旁边。未烘焙的定义不会抛出任何碎片。
- 车辆。 车辆以足够快的速度撞上单元时,会将其破坏。每个被破坏的单元都会分走车辆的一部分速度,并且单元还可能使车身凹陷。
- 任何刚体。 开启 Breaks from Any Body 的定义,会被任何足够重、足够快的物理刚体或行走的 Pawn 破坏,而不只是车辆。
| Bake | ||
|---|---|---|
| Bake as Single Piece | ||
| Density Kg Per Cm 3 | 0.0005 | |
| Max Piece Length Cm | 125.0 | |
| Min Piece Volume Cm 3 | 50.0 | |
| Cut Tilt Degrees | ||
| Bake Seed | 1 | |
| Bake Revision | 1 | |
| Pieces | 16 Array elements | |
定义的 Bake 分类中有 Bake Pieces 按钮。它会替换之前的烘焙结果。编辑器工具蓝图可以通过 CFBreakable Piece Bake Library 的静态函数 BakePieces(Definition, OutError) 执行同样的烘焙(仅限编辑器)。
本节使用 Proving Ground 上的木栅栏。要制作自己的围栏,请参见第 3 步。
- 在 Content Browser 中,随附的围栏位于 Plugins › Crushform Content › Fences。
- 把
BP_CF_FencePicket拖入关卡,或者从 Place Actors 面板放置一个ACFBreakableFence。样条线的长度决定数量:围栏大约每隔 Post Spacing Cm 的距离放置一段。要得到更长的一排,请拉长样条线。 - 要制作自己的围栏,首先创建一个定义。在 Content Browser 中选择 Add › Miscellaneous › Data Asset,选择类 CFBreakable Prop Definition,并为它命名。把它的 Intact Mesh 设为该单元的网格体,然后在 Bake 分类中点击 Bake Pieces。碎片会出现在该资产旁边。
- 在关卡中选中该围栏。在 Details 面板中,于 Fence 下把 Span Definition 设为你的定义。如果这条线上有立柱,也请设置 Post Definition。
| Fence | ||
|---|---|---|
| Post Definition | None | |
| Span Definition | DA_CF_FencePicket | |
| Post Spacing Cm | 0.0 | |
- 把 Post Spacing Cm 设为立柱之间的距离。设为
0时使用段定义的瓦片长度。围栏会把立柱沉入地面,沉入深度为 Post Sink Cm(默认 5 cm);开启 Snap to Ground 时,立柱会吸附到地面上。
蓝图可以通过一次击中破坏单元。用单元所属的对象类型(而不是通道)通过 Line Trace For Objects 追踪该单元。默认的单元配置文件为 OverlapAllDynamic:它的单元是与所有通道重叠的 WorldDynamic 对象,因此通道追踪(例如基于 Visibility 的追踪)永远不会在它们上停下。把追踪的对象类型设为单元的类型(默认为 WorldDynamic),然后用 ApplyHitResultOnAuthority 把击中应用到追踪找到的单元上。它接收追踪得到的 Hit Result、一个 Strength 和一个 Hit Kind:该类型必须在定义的 Hit Responses 中有一行,并且强度必须达到其某个 Hit Level Thresholds。请在自定义事件中运行此操作,并且从服务器代码调用 ApplyHitResultOnAuthority,而不要从客户端调用:它是静态函数,只在权威端运行。
在 C++ 中,同样的调用无需追踪即可把击中应用到单元上:
#include "CFBreakableInstancesComponent.h"#include "CFBreakableTypes.h"
// Needs the CrushformBreakable module in your Build.cs (see Installation).void BreakFirstUnitOnServer(UCFBreakableUnitsComponent* Units, const FVector& Location, const FVector& Direction){ if (IsValid(Units) && Units->GetUnitCount() > 0) { FCFBreakableHit Hit; Hit.Location = Location; Hit.Direction = Direction; Hit.Strength = 900.f; Hit.Kind = FName(TEXT("Bullet")); Units->ApplyHitOnAuthority(0, Hit); }}签名为 UCFBreakableUnitsComponent 上的 virtual bool ApplyHitOnAuthority(int32 UnitIndex, const FCFBreakableHit& Hit)。FCFBreakableHit 有 Location、Direction、Strength 和 Kind。用 Fence->GetBreakableInstances() 从 Actor 获取围栏的组件;它是 UCFBreakableInstancesComponent,是 UCFBreakableUnitsComponent 的子类。
在 Hit Responses 中没有对应行的类型不会产生任何效果,并且单元只会被它所达到的 Hit Level Thresholds 提升等级。因此在测试之前,请在定义的 Hit Responses 中为你的类型添加一行,并设置你的强度能够达到的阈值。示例中的 Bullet 类型只是一个例子。
定义的设置位于其 Details 面板中。围栏的设置位于围栏 Actor 上。默认值来自插件本身。
项目设置位于 Project Settings › Plugins › Crushform Breakable 下的 Units、Any Body 和 Respawn 分组中。被破坏的单元,在定义允许的情况下,会在没有玩家 Pawn 位于 RespawnClearRadiusCm(4000 cm)之内、并且没有属于 RespawnBlockingObjectTypes 的刚体与其位置重叠时重新立起。
验证是否生效
Section titled “验证是否生效”在除 Shipping 之外的任何构建中,用波浪号键打开控制台。
stat CFBreakableInstances会显示 Breakable Respawn Check 和任意刚体的周期。stat CFPropContact显示车辆一侧的情况。- 撞上
BP_CF_FencePicket:一个单元碎裂开来,它的碎片四散。 - 撞上
BP_CF_FenceChainLink:它的网板倒塌。
调试模式可以让世界中所有被破坏的道具同时重生:在其面板的 World 下,点击 Respawn props 按钮。参见调试模式。
围栏没有单元
Section titled “围栏没有单元”原因。 围栏没有已保存的布局,而且这是一个联网游戏。日志会显示这一行:
%s: no saved layout; a networked fence must be placed and laid out in the editor单机(Standalone)游戏会在运行时为围栏布局。联网游戏则不会,否则每个端都会各自选出自己的单元。
修复。 在编辑器中放置围栏,设置好它的样条线和段定义,并让布局完成构建。保存关卡。布局会随围栏一起保存。
围栏比它的线短
Section titled “围栏比它的线短”原因。 这条线要求的单元数超过了围栏能容纳的上限,即 256。若带有立柱,则上限为 127 段。日志会显示:
%s: %d spans requested, capped to %d; split the line into several fences修复。 把这条线拆分成几座围栏,每座的段数都少一些。
被破坏的单元不抛出碎片
Section titled “被破坏的单元不抛出碎片”原因。 该单元的定义没有被烘焙,而没有烘焙碎片的定义不会生成任何碎片。破坏仍然会发生,但没有东西可以抛出。
修复。 在定义的 Bake 分类中点击 Bake Pieces。烘焙会替换之前的结果,因此在更改网格体或烘焙设置后需要重新烘焙。
非车辆的刚体撞上单元却无法破坏它
Section titled “非车辆的刚体撞上单元却无法破坏它”原因。 定义上的 bBreaksFromAnyBody 是关闭的。或者该刚体比 AnyBodyMinMassKg 轻,或比 AnyBodyMinSpeedCmS 慢,又或者 Pawn 比 PawnMinSpeedCmS 慢。
修复。 开启定义上的 Breaks from Any Body,并降低质量和速度限制,使其适合你希望破坏该单元的刚体。
