UParticleSystemComponent是ue4中用来管理粒子系统的component,用起来也很简单,一般常用的方法有
virtual void Activate(bool bReset=false) override;
virtual void Deactivate() override;
注意这两个接口是虚函数,最早的定义是在UActorComponent中,在UActorComponent中,还有个函数和变量如下:
UFUNCTION(BlueprintCallable, Category="Components|Activation", meta=(UnsafeDuringActorConstruction="true"))
virtual bool IsActive() const { return bIsActive; }
UPROPERTY(transient, ReplicatedUsing=OnRep_IsActive)
uint8 bIsActive:1;
这里提到bIsActive变量,是因为此变量容易造成误解。
开始看到以上三个函数时,想当然的理解为Activate会将bIsActive设置为true,Deactivate将bIsActive设置为false。
深入源码会发现不是这样,真实的情况是Activate会将bIsActive设置为true,但Deactivate不会将bIsActive设置为false,至少Deactivate不会直接将bIsActive设置为false。
那Deactivate的调用会不会导致bIsActive设置为false呢?答案是跟例子系统本身有关。
正常情况下,当例子系统被Active后,直到例子系统被Deactivate后bIsActive才会被设置为false,引擎中对应代码如下:
void UParticleSystemComponent::Complete()
{
...
bIsActive = false;
...
}
而Complete调用代码如下:
const bool bIsCompleted = HasCompleted();
if (bIsCompleted && !bWasCompleted)
{
Complete();
}
也就是在HasCompleted返回true时,那么理解HasCompleted就变得尤为重要。
分析HasCompleted代码时,里面有两个变量,这两个变量会在Deactivate中进行设置(通过调用DeactivateSystem)
void UParticleSystemComponent::DeactivateSystem()
{
...
bSuppressSpawning = true;
bWasDeactivated = true;
...
}
HasCompleted通过判断EmitterInstance是否循环做不同的处理
1.有限次数循环
分两种情况,强制结束(例如通过调用Deactivate)和循环次数到结束,源码如下:
if (bWasDeactivated && bSuppressSpawning)
{
if (Instance->ActiveParticles != 0)
{
bHasCompleted = false;
}
}
else
{
if (Instance->HasCompleted())
{
if (Instance->bKillOnCompleted)
{
Instance->PreDestructorCall();
// clean up other instances that may point to this one
for (int32 InnerIndex=0; InnerIndex < EmitterInstances.Num(); InnerIndex++)
{
if (InnerIndex != i && EmitterInstances[InnerIndex] != NULL)
{
EmitterInstances[InnerIndex]->OnEmitterInstanceKilled(Instance);
}
}
delete Instance;
EmitterInstances[i] = NULL;
bClearDynamicData = true;
}
}
else
{
bHasCompleted = false;
}
}
从代码可知,强制结束时会判断Instance->ActiveParticles是否等于0,如果不等于0,bHasCompleted设置为false。
所以对于那种一直在动态创建,但每个粒子存在时间都很短,由于bSuppressSpawning被设置为true,也就是抑制新粒子的创建,那么当存在的例子都消失时,就会触发Complete。相反,对于一直存在的粒子,Deactivate是无效的,因为ActiveParticles总是大于0。
循环次数到结束就更容易理解了,判断EmitterInstance是否播放完成,播放完成就complete。
2.无限循环
无限循环的粒子不存在播放完成,只能强制结束(例如通过调用Deactivate),代码如下:
if (bWasDeactivated)
{
if (Instance->ActiveParticles != 0)
{
bHasCompleted = false;
}
}
else
{
bHasCompleted = false;
}
判断有没有Deactivated,如果有,判断有没有激活的粒子,没有才认为HasCompleted,具体分析同上。
ps:以上分析基于ue4 4.21版本