实现移动组件功能

在上一篇文章中,我们已经创建好 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 函数。

代码清单 1 变量定义
  1. UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
  2. class CRYPTRAIDER_API UMover : public UActorComponent
  3. {
  4.     GENERATED_BODY()
  5.  
  6. public:
  7.     // Sets default values for this component's properties
  8.     UMover();
  9.  
  10. protected:
  11.     // Called when the game starts
  12.     virtual void BeginPlay() override;
  13.  
  14. public:
  15.     // Called every frame
  16.     virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
  17.  
  18. private:
  19.     // Target location for the actor
  20.     FVector TargetLocation;
  21.  
  22.     // Original location of the actor
  23.     FVector InitialLocation;
  24.  
  25.     // Speed of the movement
  26.     float MoveSpeed;
  27.  
  28.     // Checkbox to toggle movement
  29.     UPROPERTY(EditAnywhere, Category = "Mover")
  30.     bool bShouldMove = false;
  31.  
  32.     // Offset from the initial location to the target location
  33.     UPROPERTY(EditAnywhere, Category = "Mover")
  34.     FVector TargetOffset = FVector(0.0f, 0.0f, -600.0f);
  35.  
  36.     // Desired time to move from the initial location to the target location
  37.     UPROPERTY(EditAnywhere, Category = "Mover")
  38.     float MoveDuration = 4.0f;
  39.  
  40.     void CalculateMoveSpeed();
  41. };

代码清单 2 是移动速度的计算逻辑,距离除以时间。

代码清单 2 移动速度
  1. void UMover::CalculateMoveSpeed()
  2. {
  3.     float Distance = TargetOffset.Size();
  4.     if (MoveDuration > 0.0f)
  5.     {
  6.         MoveSpeed = Distance / MoveDuration;
  7.     }
  8.     else
  9.     {
  10.         MoveSpeed = 0.0f;
  11.     }
  12. }

代码清单 3 是变量的初始化。我们将初始化逻辑放在 BeginPlay() 中。初始化起点、终点和速度。

代码清单 3 初始化
  1. void UMover::BeginPlay()
  2. {
  3.     Super::BeginPlay();
  4.  
  5.     if (AActor* Owner = GetOwner())
  6.     {
  7.         InitialLocation = Owner->GetActorLocation();
  8.         TargetLocation = InitialLocation + TargetOffset;
  9.         CalculateMoveSpeed();
  10.     }
  11. }

代码清单 4 是移动逻辑。具体的移动逻辑通过 VInterpConstantTo() 实现,因为我们已经讲解过这个函数,所以这边理解起来并不困难。

代码清单 4 移动
  1. void UMover::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
  2. {
  3.     Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
  4.  
  5.     if (bShouldMove)
  6.     {
  7.         if (AActor* Owner = GetOwner())
  8.         {
  9.             FVector CurrentLocation = Owner->GetActorLocation();
  10.             FVector NewLocation = FMath::VInterpConstantTo(
  11.                 CurrentLocation, TargetLocation, DeltaTime, MoveSpeed
  12.             );
  13.             Owner->SetActorLocation(NewLocation);
  14.  
  15.             // Stop moving
  16.             if (FVector::Dist(NewLocation, TargetLocation) < 1.0f)
  17.             {
  18.                 bShouldMove = false;
  19.             }
  20.         }
  21.     }
  22. }

必须把墙面 Actor 的“移动性”属性设置为“可移动”,否则将不能移动墙面。

程序运行时,按 F8 可以把控制权移回编辑器,然后勾选复选框触发墙面移动。最终的效果如下面视频所示。