【UE4】NiagaraSystem / ParticleSystem 自动添加 Module

本文记录在 Niagara 和 Cascade 编辑器中分别对 NiagaraSystem 和 ParticleSystem 特效的发射器自动添加 Module 的方法,目的在于批量、自动添加美术(或程序)所需的模块,便于美术做通用效果,便于程序做通用接口

一、背景

  Particle 和 Cascade (ParticleSystem 的编辑器的名称)相关知识和术语可以参考:ParticleSystem
  Niagara 编辑器与 Cascade 表现上看结构类似,但是实际复杂很多,大体结构都如下图所示:
在这里插入图片描述

  都是 Parameters -> Modules -> Emitters -> System 的四层结构,其中:

  • System - 指的就是这整个特效资源
  • Emitters - 指每一个发射器(由他们控制发射粒子,不管是 Sprite、Mesh、Ribbon 还是其他),编辑器中竖着的一整条
  • Modules - 指每一个模块,控制发射器的不同属性和功能,编辑器中每一个横着的一条
  • Parameters - 指每一个模块中的参数,可以由美术人员配置,也可在程序中由程序控制

  其中:

  • System - 运行时会变成 Component,ParticleSystem 创建出 UParticleSystemComponent,NiagaraSystem 创建出 UNiagaraComponent
  • Emitters - 静态时是 UParticleEmitterUNiagaraEmitter,运行时就是 FParticleMeshEmitterInstanceFNiagaraEmitterInstance
  • Modules - Particle 的都是代码写死的,都在 Engine\Source\Runtime\Engine\Classes\Particles 文件夹下,可以用的就只有这么多种,比如图中的 ColorOverLife 代码中就是 UParticleModuleColorOverLife;而 Niagara 的是资产,可以认为都是蓝图,引擎自带的都在 Engine\Plugins\FX\Niagara\Content\Modules 目录下,当然也可以自己连一下自己用,作为自己项目的资产,非常灵活好用。
  • Parameters - 可以配置的参数,Cascade 也都是固定的,如果像开给外部,让蓝图或者程序设置,可以像下图这样设置成 Parameter 即可,蓝图或者代码就可以 SetVectorParameter("ABC", DummyVec); 这样控制了;Niagara 不太一样,需要在 UserExposed 中添加一个参数,并且把参数和想用的输入关联上之后,在外部控制(外部只能设置 User Expoosed 的参数,感觉很不爽= =)

在这里插入图片描述

二、具体方法

  两种特效进行自动添加 Module 的前提都是在代码中能拿到这个特效资源,不管是在 Content Browser 中打开还是多选,保存时触发还是有按钮统一执行,都可以(Niagara 可能不行,需要把编辑器打开)。

2.1 ParticleSystem

参考在 Cascade 中右键新增一个 Emitter 时的代码:void FCascade::OnNewEmitter(),会自动添加几个默认的 Module(UParticleSpriteEmitter::SetToSensibleDefaults()),比如 Life,直接照抄引擎即可:

// Lifetime module
UParticleModuleLifetime* LifetimeModule = NewObject<UParticleModuleLifetime>(GetOuter());
UDistributionFloatUniform* LifetimeDist = Cast<UDistributionFloatUniform>(LifetimeModule->Lifetime.Distribution);
if (LifetimeDist)
{
	LifetimeDist->Min = 1.0f;
	LifetimeDist->Max = 1.0f;
	LifetimeDist->bIsDirty = true;
}
LifetimeModule->LODValidity = 1;
LODLevel->Modules.Add(LifetimeModule);

  如果想要添加一个自定义而不是引擎自带的 Module,就需要照葫芦画瓢,抄一下引擎写法,自己在代码中新增 Module,很简单。这里可以直接设上默认值,比如像添加一个外部可以控制的参数,可以在 ::InitializeDefaults()NewObject<UDistributionFloatParticleParameter>(
  值的注意的是,有一些结构没有加 ENGINE_API 开放出来,如 FComposableFloatDistribution,如果需要用到,需要自己加一下。

2.2 NiagaraSystem

  Niagara 稍微复杂些,层级结构是:

  • FNiagaraSystemViewModel - 编辑器中特效的整体
  • FNiagaraEmitterHandleViewModel - 编辑器中每一个发射器
  • UNiagaraStackViewModel - 每一个发射器的 Stack(Modules 的堆叠)
  • UNiagaraStackItemGroup - 每一个Group(一共有7个,如下图所示)
  • UNiagaraStackModuleItem - 每一个 Module
    在这里插入图片描述
      分别对应编辑器中的七个深色框及其下边的浅色框所代表的组。
    在这里插入图片描述

  具体方法参考 void UNiagaraEmitterConversionContext::InternalFinalizeStackEntryAddActions()

FNiagaraEditorModule& NiagaraEditorModule = FModuleManager::GetModuleChecked<FNiagaraEditorModule>("NiagaraEditor");
TSharedPtr<FNiagaraSystemViewModel> const SystemViewModel = NiagaraEditorModule.GetExistingViewModelForSystem(NS);
if (!SystemViewModel.IsValid())
{
	return;
}
const TArray<TSharedRef<FNiagaraEmitterHandleViewModel>>& EmitterViewModels = SystemViewModel->GetEmitterHandleViewModels();
// 每一个发射器
for (TSharedRef<FNiagaraEmitterHandleViewModel> EmitterHandleViewModel : EmitterViewModels)
{
	UNiagaraStackViewModel* StackViewModel = EmitterHandleViewModel->GetEmitterStackViewModel();
	TArray<UNiagaraStackItemGroup*> StackItemGroups;
	StackViewModel->GetRootEntry()->GetUnfilteredChildrenOfType<UNiagaraStackItemGroup>(StackItemGroups);
	// 每一个 StackItemGroup
	for (UNiagaraStackItemGroup* StackItemGroup : StackItemGroups)
	{
		// 筛选出 ParticleSpawn Stack
		FName const ExecutionCategoryName = StackItemGroup->GetExecutionCategoryName();
		if (ExecutionCategoryName != UNiagaraStackEntry::FExecutionCategoryNames::Particle)
		{
			continue;
		}
		FName const ExecutionSubcategoryName = StackItemGroup->GetExecutionSubcategoryName();
		if (ExecutionSubcategoryName != UNiagaraStackEntry::FExecutionSubcategoryNames::Spawn)
		{
			continue;
		}

		// 自动添加一个预设 Module
		UNiagaraClipboardContent* const ClipboardContent = UNiagaraClipboardContent::Create();
		UNiagaraScript* const Script = LoadObject<UNiagaraScript>(nullptr, TEXT("/Game/Effects/NiagaraModules/Test.Test"), nullptr, LOAD_None, nullptr);
		const UNiagaraClipboardFunction* const ClipboardFunction = UNiagaraClipboardFunction::CreateScriptFunction(ClipboardContent, "NS_ScaleSizeBySystem", Script, FGuid());
		ClipboardContent->Functions.Add(ClipboardFunction);

		// Commit the clipboard content to the target stack entry
		FText PasteWarning = FText();
		StackItemGroup->Paste(ClipboardContent, PasteWarning);
	}
}

  如果新增的 Module 有输入,需要关联参数,可以参考 SNiagaraStackFunctionInputValue::OnFunctionInputDrop,这个是把一个参数拖到 Input Pin 上的函数,直接设置的话可以直接 FunctionInput->SetLinkedValueHandle(Handle);

三、参考资料

  1. ParticleSystem
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值