实现移动组件功能
在上一篇文章中,我们已经创建好 C++ 组件,并将其附加到一面墙上。在这篇文章中,我们将实现具体的移动功能,让这面墙移动起来。在此之前,我们先了解两个函数,因为我们第一次接触。
1. GetOwner
GetOwner() 用于返回当前组件所属的 Actor。其原型为:
- AActor* UActorComponent::GetOwner() const;
通过这个函数我们就能获取到组件所附加的 Actor,从而操控它。此处是让 Actor 移动。
2. VInterpConstantTo
VInterpConstantTo() 用于以恒定速度插值,将一个向量逐步过渡到目标向量。其原型为:
- FVector FMath::VInterpConstantTo(
- const FVector& Current,
- const FVector& Target,
- float DeltaTime,
- float InterpSpeed);
其中,参数 Current 为当前向量值(需要被插值的向量);参数 Target 为目标向量值(插值的最终目标);参数 DeltaTime 为每帧的时间增;参数 InterpSpeed 为插值速度(单位向量长度每秒变化的距离)。
返回值是一个新的 FVector,表示经过恒定速度插值后的向量值。
函数名拆解:V 表示 Vector 向量;Interp 是插值的缩写;Constant 表示插值的速度是恒定的;To 表示插值方向。
总的含义即,对向量进行恒定速率插值,使其逐渐靠近目标。
3. 功能实现
首先如代码清单 1 所示,我们在头文件中定义所需的变量。
TargetLocation 是移动的终点;InitialLocation 是移动的起点;MoveSpeed 是移动速度。这三个变量就是后续需要传递给 VInterpConstantTo 函数的。
bShouldMove 变量做成编辑器中的复选框,用于手动触发移动。
TargetOffset 变量做成编辑器中指定,为终点距离起点的偏移。InitialLocation 加上 TargetOffset 可以得到 TargetLocation。
MoveDuration 变量做成编辑器中指定,为起点到终点预期经过多少秒。MoveDuration 结合 TargetOffset 可以得到 MoveSpeed。计算过程封装成 CalculateMoveSpeed 函数。
- UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
- class CRYPTRAIDER_API UMover : public UActorComponent
- {
- GENERATED_BODY()
- public:
- // Sets default values for this component's properties
- UMover();
- protected:
- // Called when the game starts
- virtual void BeginPlay() override;
- public:
- // Called every frame
- virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
- private:
- // Target location for the actor
- FVector TargetLocation;
- // Original location of the actor
- FVector InitialLocation;
- // Speed of the movement
- float MoveSpeed;
- // Checkbox to toggle movement
- UPROPERTY(EditAnywhere, Category = "Mover")
- bool bShouldMove = false;
- // Offset from the initial location to the target location
- UPROPERTY(EditAnywhere, Category = "Mover")
- FVector TargetOffset = FVector(0.0f, 0.0f, -600.0f);
- // Desired time to move from the initial location to the target location
- UPROPERTY(EditAnywhere, Category = "Mover")
- float MoveDuration = 4.0f;
- void CalculateMoveSpeed();
- };
代码清单 2 是移动速度的计算逻辑,距离除以时间。
- void UMover::CalculateMoveSpeed()
- {
- float Distance = TargetOffset.Size();
- if (MoveDuration > 0.0f)
- {
- MoveSpeed = Distance / MoveDuration;
- }
- else
- {
- MoveSpeed = 0.0f;
- }
- }
代码清单 3 是变量的初始化。我们将初始化逻辑放在 BeginPlay() 中。初始化起点、终点和速度。
- void UMover::BeginPlay()
- {
- Super::BeginPlay();
- if (AActor* Owner = GetOwner())
- {
- InitialLocation = Owner->GetActorLocation();
- TargetLocation = InitialLocation + TargetOffset;
- CalculateMoveSpeed();
- }
- }
代码清单 4 是移动逻辑。具体的移动逻辑通过 VInterpConstantTo() 实现,因为我们已经讲解过这个函数,所以这边理解起来并不困难。
- void UMover::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
- {
- Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
- if (bShouldMove)
- {
- if (AActor* Owner = GetOwner())
- {
- FVector CurrentLocation = Owner->GetActorLocation();
- FVector NewLocation = FMath::VInterpConstantTo(
- CurrentLocation, TargetLocation, DeltaTime, MoveSpeed
- );
- Owner->SetActorLocation(NewLocation);
- // Stop moving
- if (FVector::Dist(NewLocation, TargetLocation) < 1.0f)
- {
- bShouldMove = false;
- }
- }
- }
- }
必须把墙面 Actor 的“移动性”属性设置为“可移动”,否则将不能移动墙面。
程序运行时,按 F8 可以把控制权移回编辑器,然后勾选复选框触发墙面移动。最终的效果如下面视频所示。