世界碎片
碎片
围栏段或窗户被破坏时,它的烘焙碎片会飞散出去。碎片落地、滚动、停留一段时间,然后下沉消失。碎片是破坏中的装饰部分。它不会被复制到其他端上:每个端在看到破坏发生时,各自抛出自己的碎片。
碎片是物理刚体。车辆和 Pawn 如何与它们相遇,由定义和碰撞配置文件决定,下面的小节会说明。
碎片使用对象池。UCFDebrisSubsystem 世界子系统保存一组固定数量的碎片组件并反复使用。单元被破坏时,子系统从池中取出空闲碎片,抛出该单元的烘焙碎片。碎片只存在于本机。
- 谁来模拟。 碎片在客户端模拟。专用服务器从不创建该子系统,因此不会生成任何碎片。
- 何处生成。 距本地摄像机超过
DebrisMaxSpawnDistanceCm(10000 cm)的破坏不会生成碎片。 - 抛出。 靠近撞击点的碎片会继承更多来自撞击刚体的速度。远离撞击点的碎片则大多只会翻倒。位于撞击点的碎片会获得撞击刚体速度的
InheritVelocityFraction(0.6),外加向外和向上的ScatterSpeedCmS(300 cm/s)。这股推力会在ImpactFalloffCm(150 cm)范围内衰减。 - 碰撞。 碎片与世界、完好的道具以及其他碎片发生碰撞,但不与 Pawn 碰撞。它使用哪种对象类型,由项目设置中的 Debris Collision Profile 决定。定义中
bDebrisCollidesWithDebris关闭时,它的碎片会忽略其他碎片。 - 车辆。 车辆刚体会穿过碎片,除非定义设置了
bDebrisPushedByVehicles。在这种情况下,破坏之后的DebrisVehicleContactSeconds(3.5 秒)内,汽车会把碎片向一个方向推动。停在车辆上的碎片会持续被推动,直到离开车辆。被推动的碎片永远不会被算作对汽车的击中。 - 生命周期。 进入休眠的碎片会静止
DebrisRestSeconds(3 秒),然后下沉DebrisSinkSeconds(1 秒):它沿垂直方向下沉自身的尺寸,即按缩放后的最长边再加 10 cm,同时缩小到原来的五分之一,最后被隐藏。DebrisLifetimeSeconds(8 秒)是其生命的硬性上限,无论它是否静止。 - 预算。 游戏最多保留
MaxLiveDebrisPieces(96)个碎片;超出时,最旧的碎片会被回收。碎片持续到单元重生为止(bDebrisLastsUntilRespawn)的定义,其碎片单独计数,上限为MaxPersistentDebrisPieces(64)。
破坏抛出的碎片,是单元定义的烘焙碎片。定义如何烘焙,参见围栏与道具。
碎片没有自己的资产。每个可破坏的定义设置它自己的碎片,而项目设置为所有碎片设定预算。
- 在 Content Browser 中,从 Plugins › Crushform Content › Fences(或你自己的定义)打开围栏定义
DA_CF_FencePicket。 - 在 Debris 分类中,设置 Debris Lifetime Seconds、Debris Rest Seconds 和 Debris Sink Seconds。如果希望车辆推动碎片,请开启 Debris Pushed by Vehicles。
- 打开 Project Settings › Plugins › Crushform Breakable。在 Debris 分组中,设置 Max Live Debris Pieces 和 Debris Collision Profile。配置文件的工具提示要求选择一个能停靠在世界上、并忽略 Pawn 的配置。默认值为
None,这会让碎片只参与物理,任何追踪、扫掠或重叠检测都找不到它。
| Debris | ||
|---|---|---|
| Max Live Debris Pieces | 96 | |
| Max Persistent Debris Pieces | 64 | |
| Debris Max Spawn Distance Cm | 10000.0 | |
| Debris Impact Cooldown Seconds | 0.25 | |
| Debris Min Impulse Per Kg | 50.0 | |
| Debris Impulse Per Kg for Full Intensity | 600.0 | |
| Debris Collision Profile | None | |
子系统没有蓝图节点,因此蓝图无法更改对象池,也无法生成碎片。不过它可以读取定义的 Debris 数值,因为这些数值是 BlueprintReadOnly 的。下方的蓝图把一个围栏定义保存在变量中,并在自定义事件触发时打印其 Debris Lifetime Seconds。
想要读取或清空对象池的游戏,可以在 C++ 中这样做:
#include "CFDebrisSubsystem.h"
void ReportAndClearDebris(const UObject* WorldContext){ if (UCFDebrisSubsystem* Debris = UCFDebrisSubsystem::Get(WorldContext)) { UE_LOG(LogTemp, Log, TEXT("Live debris pieces: %d"), Debris->GetLivePieceCount()); Debris->ReleaseAllPieces(); }}签名为 static UCFDebrisSubsystem* Get(const UObject* WorldContext)、int32 GetLivePieceCount() const 和 void ReleaseAllPieces()。ReleaseAllPieces 会一次性把所有存活的碎片归还对象池,且不进行下沉。
想让对象池使用自己的摄像机和反馈的游戏,需要在 C++ 中实现 ICFBreakableHost,并用 SetHost 注册。该接口有两个方法:TryGetBreakableViewLocation(FVector&) const,给出碎片被剔除时所参照的点;PlayBreakableFeedback(const FCFBreakableFeedback&),播放破坏的声音和特效。它仅限 C++:不能在蓝图中实现。
#include "CFBreakableHost.h"#include "CFDebrisSubsystem.h"
class FMyBreakableHost : public ICFBreakableHost{public: virtual bool TryGetBreakableViewLocation(FVector& OutLocation) const override; virtual void PlayBreakableFeedback(const FCFBreakableFeedback& Feedback) override;};
// The pool does not own the host: keep it alive while it is registered.void RegisterBreakableHost(const UObject* WorldContext, ICFBreakableHost* Host){ if (UCFDebrisSubsystem* Pool = UCFDebrisSubsystem::Get(WorldContext)) { Pool->SetHost(Host); }}这段 C++ 代码需要在你的 Build.cs 中加入 CrushformBreakable 模块(参见安装)。除非 Crushform Impact Feedback 设置中的 Use Default Breakable Host 被关闭,否则插件的默认宿主 UCFImpactBreakableHost 会自行注册,并把破坏映射为撞击事件。参见撞击反馈。
代表单元组件中某个单元的基元,需要在 C++ 中通过 GetBreakableUnits 实现 ICFBreakableUnitPart。这样,对该基元的击中就能追溯到它所属的单元。这同样仅限 C++。
碎片的数值位于可破坏定义上的 Debris 分类中。预算与碰撞配置文件位于项目设置中。默认值来自插件本身。
验证是否生效
Section titled “验证是否生效”在除 Shipping 之外的任何构建中,用波浪号键打开控制台。
stat CrushformDebris会显示碎片的周期。存活碎片数即GetLivePieceCount,上面的 C++ 示例读取的就是它。- 在
Lvl_ProvingGround中破坏一段栅栏。碎片飞出,落地、静止并下沉。 - 撞上一根被定义设置了
bDebrisPushedByVehicles的栅栏条。汽车会在DebrisVehicleContactSeconds内推动该碎片,然后从中穿过。 - 打开调试面板,在 World 下使用 Clear debris。每个碎片会立即回到对象池。参见调试模式。
破坏不抛出任何碎片
Section titled “破坏不抛出任何碎片”原因。 定义没有被烘焙,而没有烘焙碎片的定义不会生成任何碎片。或者破坏发生在距本地摄像机超过 DebrisMaxSpawnDistanceCm(10000 cm)的地方,或者发生在专用服务器上,而专用服务器从不生成碎片。
修复。 用 Bake Pieces 烘焙定义。测试时站得离破坏近一些。专用服务器按设计不显示碎片:由客户端显示。
碎片消失得太快
Section titled “碎片消失得太快”原因。 对象池已满。超过 MaxLiveDebrisPieces(96)时,最旧的碎片会被回收;而超过 DebrisLifetimeSeconds(8 秒)的碎片即使仍然躺在那里,也会被移除。
修复。 提高 Max Live Debris Pieces,或把 DebrisLifetimeSeconds 设得更高。对于应当保留到单元重生为止的碎片,请开启 bDebrisLastsUntilRespawn;如果这类碎片的预算用完了,则提高 Max Persistent Debris Pieces。
汽车从碎片中穿过
Section titled “汽车从碎片中穿过”原因。 bDebrisPushedByVehicles 是关闭的,这也是默认值。或者它已开启,但 DebrisVehicleContactSeconds 已经过去,此后车辆会再次穿过该碎片。
修复。 对于应当在汽车下移动的碎片,开启 Debris Pushed by Vehicles,并把 Debris Vehicle Contact Seconds 设为推动应持续的时长。
Pawn 穿过碎片
Section titled “Pawn 穿过碎片”原因。 碎片与世界、完好道具及彼此发生碰撞,但不与 Pawn 碰撞。这是设计如此,而 Debris Collision Profile 决定了这一点。
修复。 若希望碎片阻挡 Pawn,请把 Debris Collision Profile 设为会阻挡 Pawn 的配置。然后检查碎片是否仍然能停靠在世界上。
碎片的撞击反馈会播放定义中 Debris Impact Tag 所指定的撞击标签;该项为空时,播放 Crushform.Impact.Prop.Debris。上面的冷却时间和冲量限制决定它何时播放。关于标签及其声音,参见撞击反馈。
