UE控制物体移动,且使用UEC++实现UE蓝图中的TimeLine

方法1:使用 Lerp(线性插值) + Timeline(时间轴)

适合控制移动时长和曲线效果

该方法需要三个变量:

  • StartLocation(Vector):起始位置
  • TargetLocation(Vector):目标位置
  • MoveDuration(Float):移动时长(秒)

设置时间轴:

  • 添加 Timeline 组件,命名为 MoveTimeline
  • 在时间轴中添加 浮点轨道(Float Track),命名为 Alpha
  • 编辑曲线:0.0 秒时值为 0.0MoveDuration 秒时值为 1.0(可调整曲线形状控制缓动效果)。

蓝图逻辑:我这里为了测试方便,直接设置了三个变量的值

image-20250720113010057

方法2:使用 VInterp To(向量插值)

适合每一帧平滑移动,无需设置时间轴,需要配合Event Tick 一起使用

需要两个变量:

  • TargetLocation(Vector):目标位置
  • InterpSpeed(Float):插值速度(推荐 5-10)

蓝图逻辑:

image-20250720113301938

方法3:使用 MoveComponentTo(组件平滑移动)

注意使用此方法移动的是相对位置

蓝图逻辑:

image-20250720113617663

实现物体匀速到达不同长度的目的地

实际上本例子是物体按照样条线上的样条点进行移动

设置的变量:

  • MoveSpeed:移动速度
  • StopThreshold:距离目的地多远可以停止
  • TargetLocation:目的地
  • CurrentLocation:当前位置
  • isMoving:是否正在移动
  • ToTarget:剩余方向向量 (TargetLocation - CurrentLocation
  • VTarget:单位方向向量(Normalize(ToTarget)
  • Distance:距离目的地的距离(VSize(ToTarget)

实现步骤:

  1. 开始移动时,判断是否已经在目的地:若否,执行下面步骤;若是,不执行

  2. 根据当前位置和目的地位置,得到单位方向向量VTarget

  3. 计算当前帧需要移动的距离 MoveEachDeltaSecondsMoveSpeed * DeltaSeconds

    1. 如果当前位置和目标位置小于最小步长,则直接移动到目标位置
    2. 否则向目标前进一步CurrentLocation = CurrentLocation + VTarget * MoveEachDeltaSeconds
  4. 回到步骤一

image-20250720190527911

BoxMove函数

image-20250720190618495

附加:实现物体按照样条线(非样条点)进行移动

蓝图逻辑:

时间轴参照上述方法1设置

image-20250720190832764

触发函数

image-20250720190954965

使用UEC++实现

UEC++实现时间轴

  1. 创建一个时间轴组件
  2. 绑定一个浮点曲线(如果需要的话),用于控制时间轴的变化
  3. 绑定时间轴更新和结束的委托
  4. 实现各个函数内的不同需求

创建一个浮点曲线,内部设置同蓝图创建时间轴的浮点曲线设置

image-20250721151819659

MyTimeLine.h

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

#pragma once

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

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTimelineUpdateDelegate, float, Alpha);
UCLASS()
class ASTAR_TEST2_API AMyTimeLine : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AMyTimeLine();

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

	// 时间轴组件
	UPROPERTY(VisibleAnywhere,BlueprintReadWrite, Category = "Timeline")
	UTimelineComponent* MyTimeline;

	// 浮点曲线(用于驱动数值变化)
	UPROPERTY(BlueprintReadWrite,EditAnywhere, Category = "Timeline")
	UCurveFloat* FloatCurve;

	// 时间轴更新事件
	UPROPERTY(BlueprintAssignable, Category = "Timeline")
	FOnTimelineUpdateDelegate FOnTimelineUpdate;

	// 时间轴更新回调
	UFUNCTION()
	void OnTimelineUpdate(float Value);

	// 时间轴完成回调
	UFUNCTION()
	void OnTimelineFinished();

	// 事件轨道回调
	UFUNCTION()
	void OnTimelineEvent();
	
	// 设置播放速率
	UFUNCTION(BlueprintCallable, Category = "Timeline")
	void SetPlayRate(float NewRate);
	
	// 控制函数
	void PlayTimeline();

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


private:
	
	// 委托绑定
	FOnTimelineFloat UpdateFunction;
	FOnTimelineEvent FinishedFunction;
};

MyTimeLine.cpp

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


#include "MyTimeLine.h"


// Sets default values
AMyTimeLine::AMyTimeLine()
{
	// 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;

	// 创建时间轴组件
	MyTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("MyTimelineComponent"));
	
	// 初始化委托
	UpdateFunction.BindUFunction(this, FName("OnTimelineUpdate"));
	FinishedFunction.BindUFunction(this, FName("OnTimelineFinished"));
	
	// 重点,要在此处绑定curve
	static ConstructorHelpers::FObjectFinder<UCurveFloat> CurveAsset(TEXT("CurveFloat'/Game/BP/TimeLineCurve.TimeLineCurve'"));
	if (CurveAsset.Succeeded()) FloatCurve = CurveAsset.Object;
}

// Called when the game starts or when spawned
void AMyTimeLine::BeginPlay()
{
	Super::BeginPlay();
	
	// 绑定时间轴曲线
	if (FloatCurve) {
		MyTimeline->AddInterpFloat(FloatCurve, UpdateFunction);
		UE_LOG(LogTemp, Warning, TEXT("FloatCurve,存在"));
	}

	// 绑定完成事件
	MyTimeline->SetTimelineFinishedFunc(FinishedFunction);
    
}

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

	// 驱动时间轴更新,OnTimelineUpdate得到执行
	if (MyTimeline->IsPlaying()) {
		MyTimeline->TickComponent(DeltaTime, LEVELTICK_TimeOnly, nullptr);
	}
}

void AMyTimeLine::OnTimelineUpdate(float Value)
{
	// 在此处实现基于曲线值的逻辑

	// 通过委托,来实现每次更新时,执行逻辑
	FOnTimelineUpdate.Broadcast(Value);
	UE_LOG(LogTemp, Warning, TEXT("Timeline Value: %f"), Value);
}

void AMyTimeLine::OnTimelineFinished()
{
	UE_LOG(LogTemp, Warning, TEXT("Timeline Finished!"));
}

void AMyTimeLine::OnTimelineEvent()
{
	UE_LOG(LogTemp, Warning, TEXT("Timeline Event Triggered!"));
}

void AMyTimeLine::SetPlayRate(float NewRate)
{
	if (MyTimeline)
	{
		MyTimeline->SetPlayRate(NewRate);
	}
}
void AMyTimeLine::PlayTimeline()
{
	if (MyTimeline) MyTimeline->Play();
}

剩余部分实现:

BoxMove.h

class ASpine;
class USplineComponent;

UCLASS()
class ASTAR_TEST2_API ABoxMove : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	ABoxMove();

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

	UFUNCTION()
	void MoveBySpline();

	UFUNCTION()
	void HandleTimelineUpdate(float Alpha);

	AMyTimeLine* TimelineActor;

	

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

private:
	USplineComponent* SplineComp ;

	ASpine* SpineRef = nullptr ;
	
};

BoxMove.cpp

// Sets default values
ABoxMove::ABoxMove()
{
	// 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 ABoxMove::BeginPlay()
{
	Super::BeginPlay();
}

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

void ABoxMove::MoveBySpline()
{
	// 获取时间轴Actor引用
	TimelineActor = GetWorld()->SpawnActor<AMyTimeLine>();
	if (!TimelineActor) UE_LOG(LogTemp, Warning, TEXT("TimelineActor不存在"));
	if (TimelineActor)
	{
		// 绑定委托
		TimelineActor ->FOnTimelineUpdate.AddDynamic(this, &ABoxMove::HandleTimelineUpdate);
		// 设置播放速度
		TimelineActor->SetPlayRate(0.2f);
		// 播放时间轴
		TimelineActor->PlayTimeline();
	}
	for (TActorIterator<ASpine> It(GetWorld()); It; ++It)
	{
		SpineRef = *It;
	}
	SplineComp = SpineRef -> FindComponentByClass<USplineComponent>();
}

void ABoxMove::HandleTimelineUpdate(float Alpha)
{
	float SplineLength = SplineComp->GetSplineLength();
	double Lerp = UKismetMathLibrary::Lerp(0.0f, SplineLength, Alpha);
	FVector LocationAtDistanceAlongSpline = SplineComp -> GetLocationAtDistanceAlongSpline(Lerp, ESplineCoordinateSpace::World);
	LocationAtDistanceAlongSpline += FVector(0.0f, 0.0f, 25.0f);
	SetActorLocation(LocationAtDistanceAlongSpline);
}

Puerts是一款针对Unreal Engine 4的JavaScript运行时,它允许开发者使用JavaScript来编写UE4的游戏逻辑。下面是一些常见的Puerts语法和函数: ### 基本语法 #### 变量声明 ```javascript const a = 1; // 常量 let b = 2; // 变量 var c = 3; // 变量(不推荐使用) ``` #### 条件语句 ```javascript if (condition) { // 如果条件为真,执行这里的代码 } else if (condition2) { // 如果条件2为真,执行这里的代码 } else { // 如果条件都不为真,执行这里的代码 } ``` #### 循环语句 ```javascript for (let i = 0; i < 10; i++) { // 循环10次 } while (condition) { // 只要条件为真,就一直执行 } do { // 先执行一次,再判断条件是否为真,如果为真就继续执行 } while (condition); ``` #### 函数声明 ```javascript function add(a, b) { return a + b; } const add = function(a, b) { return a + b; }; const add = (a, b) => { return a + b; }; ``` ### Puerts函数 #### UObject Puerts中的UObject类对应UE4中的UObject,它是所有游戏对象的基类。 ```javascript const myObject = JSClass.Load("/Game/MyBlueprint.MyBlueprint_C"); // 加载蓝图类 const myObject2 = JSClass.Load("/Game/MyBlueprint.MyBlueprint_C").GeneratedClass; // 加载C++类 const myActor = myObject.GetDefaultObject(); // 获取默认对象 myActor.SetActorLocation(new FVector(0, 0, 0)); // 设置Actor的位置 ``` #### UFunction Puerts中的UFunction类对应UE4中的UFunction,它用于调用UE4中的函数。 ```javascript const myActor = GWorld.SpawnActor(MyActor.StaticClass(), new FVector(0, 0, 0), new FRotator(0, 0, 0)); // 在世界中生成Actor myActor.K2_AddActorWorldOffset(new FVector(100, 0, 0), false, null, false); // 调用Actor的函数 ``` #### UClass Puerts中的UClass类对应UE4中的UClass,它用于获取游戏类的元信息。 ```javascript const myClass = JSClass.Load("/Game/MyBlueprint.MyBlueprint_C"); // 加载蓝图类 const myClass2 = JSClass.Load("/Game/MyBlueprint.MyBlueprint_C").GeneratedClass; // 加载C++类 const myActorClass = myClass.Class; // 获取Actor类的元信息 ``` #### FObjectFinder Puerts中的FObjectFinder类用于查找UE4中的对象。 ```javascript const myTexture = new FObjectFinder(UTexture2D)("Texture2D'/Game/Textures/MyTexture.MyTexture'").Object; const myMaterial = new FObjectFinder(UMaterial)("Material'/Game/Materials/MyMaterial.MyMaterial'").Object; ``` #### FText Puerts中的FText类对应UE4中的FText,它用于处理游戏中的文本。 ```javascript const myText = new FText("Hello, world!"); // 创建一个新的FText对象 const myString = myText.ToString(); // 将FText转换为字符串 ``` 这里只列举了一些常见的Puerts语法和函数,更详细的内容可以参考官方文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值