引子:那个让制作人摔键盘的慢动作镜头
去年Q3,我们接了一个赛车游戏过场动画的外包,客户要求“碰撞瞬间进入子弹时间,镜头环绕,粒子定格,但UI和背景音乐不能停”。听起来简单?我们团队当时也这么想。结果第一版交付后,测试反馈:慢动作时角色动画卡顿、粒子特效直接消失、音频跟着变调、联机模式下玩家状态不同步……制作人差点把键盘摔了。接下来两周,我们几乎把UE5时间膨胀相关的坑踩了个遍,从蓝图节点到C++底层,从本地单机到网络同步,最后硬是啃出了一套稳定方案。今天这篇文章,我就以这个项目为线索,把时间膨胀(Time Dilation)那点事儿彻底聊透——不只是怎么调参数,更要讲清楚引擎底层怎么处理时间缩放,以及为什么你会在项目里遇到那些“灵异现象”。

需求拆解:慢动作不是全局变速那么简单
客户的需求翻译成技术语言其实包含四个维度:第一,游戏世界时间(World Time)局部缩放,只影响特定Actor;第二,音频需要独立控制,不能跟着变调;第三,UI和输入响应必须保持实时;第四,联机模式下所有客户端看到一致的慢动作效果。我们最初的做法很“蓝图”:直接调用Set Global Time Dilation,把全局时间膨胀调到0.2。结果就是上面说的翻车现场——因为全局时间膨胀影响的是整个世界,包括音频、UI动画、甚至网络心跳。所以第一个关键认知是:时间膨胀必须分层控制,不能一刀切。
技术选型:Custom Time Dilation vs. Global Time Dilation
UE5提供了两种时间膨胀机制:全局(Global)和自定义(Custom)。全局作用于整个World,自定义只作用于单个Actor及其组件。我们的需求是“局部慢动作”,所以果断选择Custom Time Dilation。但这里有个隐藏坑:Custom Time Dilation只影响Actor自身的Tick、动画和物理,不影响它生成的粒子系统(Niagara)和音频组件。也就是说,你给角色设置了0.2的膨胀,角色动作慢了,但 Niagara 粒子仍然按正常速度飞——这就是第一版粒子“消失”的原因(其实是粒子飞太快,在慢动作镜头里一闪而过)。
技巧提示:Niagara 组件需要单独设置
Custom Time Dilation,并且要确保粒子系统的Simulation Target设置为CPU或GPU时行为不同。GPU粒子对时间膨胀的支持较差,建议在慢动作场景中使用CPU粒子或手动控制粒子系统的时间缩放。
底层原理:时间膨胀到底改了什么?
要真正驾驭时间膨胀,得先明白引擎的时间管理模型。UE5的World维护了一个TimeSeconds和DeltaTime,每帧通过UWorld::Tick更新。全局时间膨胀会乘以DeltaTime,而自定义时间膨胀则是在Actor的Tick中乘以自己的膨胀系数。听起来简单,但问题出在“时间膨胀的传递性”上:父Actor的膨胀会传递给子Actor,但不会传递给通过SpawnActor动态生成的Actor(除非显式设置)。我们在项目中就遇到了角色慢动作但武器(动态生成)正常速度的bug,原因就在于此。
// C++中设置自定义时间膨胀的正确姿势
void AMyCharacter::EnterSlowMotion(float Dilation)
{
// 设置自身时间膨胀
CustomTimeDilation = Dilation;
// 同步设置所有附着组件(包括 Niagara 和音频)
TArray<UActorComponent*> Components;
GetComponents(Components);
for (UActorComponent* Comp : Components)
{
if (UNiagaraComponent* NiagaraComp = Cast<UNiagaraComponent>(Comp))
{
NiagaraComp->SetCustomTimeDilation(Dilation);
}
if (UAudioComponent* AudioComp = Cast<UAudioComponent>(Comp))
{
// 音频通常不希望变调,所以保持1.0,或使用Pitch调制
AudioComp->SetCustomTimeDilation(1.0f);
}
}
// 动态生成的Actor需要手动设置
for (AActor* Child : SpawnedActors)
{
Child->CustomTimeDilation = Dilation;
}
}
另外,物理模拟(Chaos)对时间膨胀的处理也有讲究。Chaos物理在Tick时会读取Actor的CustomTimeDilation,但如果你使用了Substepping(子步进),时间膨胀会影响子步进的次数,可能导致物理精度下降。我们的赛车碰撞场景就遇到了这个问题:慢动作下碰撞检测变得不稳定,车辆会轻微穿透地面。解决方案是开启Substepping并适当增加Max Substep Delta Time,但这会带来性能开销,需要权衡。

踩坑实录:联机同步与音频变调
单机效果调好后,我们信心满满地接入联机测试,结果又翻车了:客户端A看到慢动作,客户端B却是正常速度。原因很简单:CustomTimeDilation是本地属性,不会自动复制。在联机游戏中,必须通过RPC或属性复制来同步时间膨胀状态。我们的做法是使用Server RPC来统一设置,并在OnRep函数中更新本地表现。但要注意,服务器上的时间膨胀会影响游戏逻辑(比如技能冷却),所以需要谨慎设计——通常只让服务器控制逻辑时间,客户端只做视觉表现。
音频变调是另一个经典问题。全局时间膨胀会让音频引擎的Global Pitch跟着变,导致声音低沉。解决办法是使用Sound Mix或Audio Component的Pitch参数独立控制,或者在项目设置中关闭Allow Global Time Dilation to Affect Audio。我们选择了后者,并在慢动作时手动播放一个低频音效来增强氛围。
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 全局时间膨胀 | 简单,影响所有系统 | 音频变调,UI卡顿,联机难同步 | 单机过场动画 |
| 自定义时间膨胀(Actor) | 局部控制,灵活 | 需手动处理组件和子Actor | 角色慢动作、局部特效 |
| 手动时间缩放(自定义DeltaTime) | 完全可控,不影响引擎全局 | 实现复杂,需改造动画和物理 | 高定制化需求 |
交付方案:分层时间控制架构
最终我们设计了一套分层时间控制架构:全局时间膨胀只用于过场动画的瞬间(UI和音频通过独立通道隔离);自定义时间膨胀用于角色和载具;Niagara和音频单独设置;联机同步通过属性复制和RPC结合。具体实现上,我们创建了一个TimeController组件,挂载到需要慢动作的Actor上,暴露参数给蓝图,并处理所有子组件的膨胀设置。这套方案在项目中稳定运行,客户验收时特意夸了慢动作镜头的流畅性。
注意事项:时间膨胀的恢复要平滑,不要瞬间跳变。使用
FMath::FInterpTo或时间轴做插值,否则会有明显的卡顿感。另外,记得在慢动作期间禁用或调整输入采样率,避免玩家操作响应延迟。

回顾这个项目,时间膨胀看似简单,实则牵扯到引擎的时间管理、物理、音频、网络多个子系统。很多教程只讲怎么调参数,但真正的坑都在细节里。如果你也在做类似需求,记住一句话:时间膨胀是工具,不是魔法,分层控制才是王道。
关于火星人教育
类似的时间膨胀实战案例,在火星人教育的UE5游戏特效课程中还有很多。火星人教育创办24年来,始终专注数字艺术与AIGC实战职业技能培训,开设课程涵盖AI漫剧创作、AIGC视觉设计、UE5游戏特效、室内全屋定制、MAYA三维动画、C4D动态设计、工业设计、NUKE影视后期、摄影摄像实训等方向。我们在北京、深圳双校区开设线下面授班,面向美术生、在校大学生及意向转行的职场人群,以项目化实训教学,夯实专业技能,助力就业与职业提升。如果你也想亲手拆解引擎底层机制,欢迎咨询:010-65918670。








评论(0)