【转载】将 Houdini 的 Curve 转成 UE 的 Spline

639 阅读2分钟

原文链接:《将 Houdini 的 Curve 转成 UE 的 Spline》 | 作者:Melody

正文

Houdini 的 Curve 确实很方便,但在 UE 里的 Spline 会更方便。Houdini 的 Curve 要转成 UE 的 Spline ,很简单,一句代码搞定:

i@unreal_output_curve = 1;

Curve 输出前将 unreal_output_curve 置为 1 。上面的节点就是简单的将一个文本转成 Curve ,注意要计算法线。

制作的 HDA 我开了这几个参数,但没开字体(主要是我不会!)

  • Text 是 Curve 的文本,
  • FontSize 是字号大小,
  • Length 是每个控制点的间隔。

点击 HDA 的 Bake 就可以将这个 Curve 转成 Spline 的 Actor 或者蓝图,但这里会一个问题,曲线会拆分到多个 BP 中,不是一个整体了,这不是我想要的。所以这里先建一个Actor,将 HoudiniAssetBlueprint 设为根组件,然后在 Actor 里进行 Bake,可以将所有的 Spline 放在一个 Actor 下。

但这样有一个问题我没有解决,在这里修改的参数是不会按着新参数进行 Bake 。暂时只想到两种临时解决办法:

  • 一是试好参数后,直接设为 HDA 的默认参数。
  • 二就是还是上面那种生成 Spline 的方式,让它拆分成多个 BP,然后一个一个将 Spline 粘贴到一个 Actor 中。

这时就得到了一个将文本转 Spline 的组件了,这里我做了一个拖尾的 Niagara 特效,用沿着 Spline 循环移动,在 Timeline 中移动特效,代码如下:

TextSplineFxActor.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimelineComponent.h"
#include "TextSplineFxActor.generated.h"

class USplineComponent;
class UNiagaraComponent;
class USceneComponent;
class UNiagaraSystem;
class UCurveFloat;

UCLASS(BlueprintType)
class NB_NIAGARA_API ATextSplineFxActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATextSplineFxActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

public:

	UFUNCTION(BlueprintCallable, Category = "Spline")
	void StartSpline(USceneComponent* ParentComp);

	UFUNCTION(BlueprintCallable, Category = "Spline")
	void NiaSplineMovement(float Alpha, USplineComponent* SplineComponent, UNiagaraComponent* NiaComponent);

private:

	UFUNCTION()
	void UpdateTimeline(float Alpha);

public:

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spline")
	UNiagaraSystem* NiagaraAsset;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spline")
	UCurveFloat* AniCurve = nullptr;

private:

	UPROPERTY()
	FTimeline AniTimeline;
};

TextSplineFxActor.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "TextSplineFxActor.h"
#include "Components/SplineComponent.h"
#include "NiagaraComponent.h"
#include "Components/TimelineComponent.h"

// Sets default values
ATextSplineFxActor::ATextSplineFxActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void ATextSplineFxActor::BeginPlay()
{
	Super::BeginPlay();
	
	if (AniCurve)
	{
		AniTimeline.SetLooping(true);
		FOnTimelineFloat Delegate;
		Delegate.BindUFunction(this, "UpdateTimeline");
		AniTimeline.AddInterpFloat(AniCurve, Delegate);
	}
}

// Called every frame
void ATextSplineFxActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	AniTimeline.TickTimeline(DeltaTime);
}

void ATextSplineFxActor::StartSpline(USceneComponent* ParentComp)
{
	if (!NiagaraAsset || !ParentComp)
	{
		return;
	}

	TArray<USceneComponent*> ChildrenComponet = ParentComp->GetAttachChildren();
	for (USceneComponent* ChildComp : ChildrenComponet)
	{
		if (USplineComponent* Spline = Cast<USplineComponent>(ChildComp))
		{
			UNiagaraComponent* NewNiaComponent = NewObject<UNiagaraComponent>(this);
			NewNiaComponent->SetAsset(NiagaraAsset);
			NewNiaComponent->SetupAttachment(Spline);
			NewNiaComponent->RegisterComponent();
		}
	}
	AniTimeline.PlayFromStart();
}

void ATextSplineFxActor::NiaSplineMovement(float Alpha, USplineComponent* SplineComponent, UNiagaraComponent* NiaComponent)
{
	if (!SplineComponent || !NiaComponent)
	{
		return;
	}

	const float Distance = SplineComponent->GetSplineLength() * Alpha;
	const FTransform& NewTransform = SplineComponent->GetTransformAtDistanceAlongSpline(Distance, ESplineCoordinateSpace::World, true);
	NiaComponent->SetWorldTransform(NewTransform);
}

void ATextSplineFxActor::UpdateTimeline(float Alpha)
{
	const TArray<UActorComponent*>& Components = GetComponentsByClass(USplineComponent::StaticClass());
	for (UActorComponent* TmpComp : Components)
	{
		if (USplineComponent* Spline = Cast<USplineComponent>(TmpComp))
		{
			if (UNiagaraComponent* NiaComp = Cast<UNiagaraComponent>(Spline->GetChildComponent(0)))
			{
				NiaSplineMovement(Alpha, Spline, NiaComp);
			}
		}
	}
}

然后在蓝图里开始: