《UE4游戏开发》之 《创建粒子特效的时机,错误的时机特效将无法播放》

  1. 创建特效的方法:【明确:要想特效创建后,就显示正常;必须保证UParticleSystemComponent组件的数组EmitterInstances中的值是有效的
UFUNCTION(BlueprintCallable, Category="Effects|Components|ParticleSystem", meta=(Keywords = "particle system", UnsafeDuringActorConstruction = "true"))
	static UParticleSystemComponent* SpawnEmitterAttached(class UParticleSystem* EmitterTemplate, class USceneComponent* AttachToComponent, FName AttachPointName = NAME_None, FVector Location = FVector(ForceInit), FRotator Rotation = FRotator::ZeroRotator, FVector Scale = FVector(1.f), EAttachLocation::Type LocationType = EAttachLocation::KeepRelativeOffset, bool bAutoDestroy = true, EPSCPoolMethod PoolingMethod = EPSCPoolMethod::None, bool bAutoActivate=true);
  1. 创建粒子特效的时机,先看一段源码
void UParticleSystemComponent::InitParticles()
{
    
    
	LLM_SCOPE(ELLMTag::Particles);

	SCOPE_CYCLE_COUNTER(STAT_ParticleSystemComponent_InitParticles);

	if (IsTemplate() == true)
	{
    
    
		return;
	}
	ForceAsyncWorkCompletion(ENSURE_AND_STALL);

	check(GetWorld());
	UE_LOG(LogParticles,Verbose,
		TEXT("InitParticles @ %fs %s"), GetWorld()->TimeSeconds,
		Template != NULL ? *Template->GetName() : TEXT("NULL"));

	if (Template != NULL)
	{
    
    
		WarmupTime = Template->WarmupTime;
		WarmupTickRate = Template->WarmupTickRate;
		bIsViewRelevanceDirty = true;
		const int32 GlobalDetailMode = GetCurrentDetailMode();
		const bool bCanEverRender = CanEverRender();

		//simplified version.
		int32 NumInstances = EmitterInstances.Num();
		int32 NumEmitters = Template->Emitters.Num();
		const bool bIsFirstCreate = NumInstances == 0;
		EmitterInstances.SetNumZeroed(NumEmitters);

		bWasCompleted = bIsFirstCreate ? false : bWasCompleted;

		bool bClearDynamicData = false;
		int32 PreferredLODLevel = LODLevel;
		bool bSetLodLevels = LODLevel > 0; //We should set the lod level even when creating all emitters if the requested LOD is not 0. 

		for (int32 Idx = 0; Idx < NumEmitters; Idx++)
		{
    
    
			UParticleEmitter* Emitter = Template->Emitters[Idx];
			if (Emitter)
			{
    
    
				FParticleEmitterInstance* Instance = NumInstances == 0 ? NULL : EmitterInstances[Idx];
				check(GlobalDetailMode < NUM_DETAILMODE_FLAGS);
				const bool bDetailModeAllowsRendering = DetailMode <= GlobalDetailMode && (Emitter->DetailModeBitmask & (1 << GlobalDetailMode));
				const bool bShouldCreateAndOrInit = bDetailModeAllowsRendering && Emitter->HasAnyEnabledLODs() && bCanEverRender;

				if (bShouldCreateAndOrInit)
				{
    
    
					if (Instance)
					{
    
    
						Instance->SetHaltSpawning(false);
						Instance->SetHaltSpawningExternal(false);
					}
					else
					{
    
    
						Instance = Emitter->CreateInstance(this);
						EmitterInstances[Idx] = Instance;
					}

					if (Instance)
					{
    
    
						Instance->bEnabled = true;
						Instance->InitParameters(Emitter, this);
						Instance->Init();

						PreferredLODLevel = FMath::Min(PreferredLODLevel, Emitter->LODLevels.Num());
						bSetLodLevels |= !bIsFirstCreate;//Only set lod levels if we init any instances and it's not the first creation time.
					}
				}
				else
				{
    
    
					if (Instance)
					{
    
    
#if STATS
						Instance->PreDestructorCall();
#endif
						delete Instance;
						EmitterInstances[Idx] = NULL;
						bClearDynamicData = true;
					}
				}
			}
		}

		if (bClearDynamicData)
		{
    
    
			ClearDynamicData();
		}

		if (bSetLodLevels)
		{
    
    
			if (PreferredLODLevel != LODLevel)
			{
    
    
				// This should never be higher...
				check(PreferredLODLevel < LODLevel);
				LODLevel = PreferredLODLevel;
			}

			for (int32 Idx = 0; Idx < EmitterInstances.Num(); Idx++)
			{
    
    
				FParticleEmitterInstance* Instance = EmitterInstances[Idx];
				// set the LOD levels here
				if (Instance)
				{
    
    
					Instance->CurrentLODLevelIndex	= LODLevel;

					// small safety net for OR-11322; can be removed if the ensure never fires after the change in SetTemplate (reset all instances LOD indices to 0)
					if (Instance->CurrentLODLevelIndex >= Instance->SpriteTemplate->LODLevels.Num())
					{
    
    
						Instance->CurrentLODLevelIndex = Instance->SpriteTemplate->LODLevels.Num()-1;
						ensureMsgf(false, TEXT("LOD access out of bounds (OR-11322). Please let olaf.piesche or simon.tovey know."));
					}
					Instance->CurrentLODLevel = Instance->SpriteTemplate->LODLevels[Instance->CurrentLODLevelIndex];
				}
			}
		}
	}

}

上述代码中,正真初始化EmitterInstances的值,见截图在这里插入图片描述
而要执行上述截图代码,需要在这里插入图片描述
bShouldCreateAndOrInit这个值为true;而这个条件的重点是bCanEverRender(记录能否渲染的值),而该值是由CanEverRender在这里插入图片描述返回;
再看一段CanEverRender的源码

bool USceneComponent::CanEverRender() const
{
    
    
	AActor* Owner = GetOwner();

	if (Owner)
	{
    
    
		if (UChildActorComponent* ParentComponent = Owner->GetParentComponent())
		{
    
    
			if (!ParentComponent->CanEverRender())
			{
    
    
				return false;
			}
		}
	}

	const bool bShowInEditor =
#if WITH_EDITOR
		GIsEditor ? (!Owner || !Owner->IsHiddenEd()) : false;
#else
		false;
#endif
	UWorld *World = GetWorld();
	const bool bInGameWorld = World && World->UsesGameHiddenFlags();

	const bool bShowInGame = (!Owner || !Owner->IsHidden());
	return ((bInGameWorld && bShowInGame) || (!bInGameWorld && bShowInEditor));
}

发现关键的代码是const bool bShowInGame = (!Owner || !Owner->IsHidden());,所以要想CanEverRender返回为true,必须确保组件所属的actor必须是在游戏中是显示的

  1. 总结:不管是用蓝图还是C++创建特效时,必须确保组件所属的actor必须是在游戏中是显示的,否则,初始化粒子效果,将不会显示

猜你喜欢

转载自blog.csdn.net/qq_21919621/article/details/106843590