实现移动立方体
在上一篇文章 《创建 C++ 类》 中,我们创建了一个继承于 Actor 的 C++ 类。取的类名是 MovingPlatform,所以我们会基于这个创建的类,实现一个移动的立方体平面。
具体要实现的功能为:以物体的初始点作为中心,朝一个方向移动指定的距离,然后朝相反的方向移动。即实现类似“振荡”的移动效果。
现在把 C++ 类拖入到视口里,是没有东西显示的,也没有 transform 参数。如图 1 所示,我们选中 C++ 类的实例。在细节窗体中,点击“添加”按钮,添加一个 Cube。
添加完组件之后,在视口中就会显示相应的物体,且出现了 transform 参数。我们调整它的形状,调整成合适的平面样子。并将其移动到合适位置。
在参数上右击,可以出现复制、粘贴。
像位置信息,我们想放在人物附近。我们可以先复制、粘贴人物的位置,再在其基础上进行调整。

以上内容完成后,我们就可以在 C++ 侧实现代码了。如代码清单 1 所示,_StartPosition 用于记录物体的初始位置;_CurrentTravelDistance 是物体移动了的距离;MovementVelocity 指定物体的移动向量,包含了大小和方向;MaxTravelDistance 指定移动的范围。
UPROPERTY 宏,我们第一次接触。它用于在 Unreal Engine 编辑器中公开 C++ 类成员变量。EditAnywhere 指定属性可以在编辑器的任何地方进行修改;Category 指定这个属性所在的分组。
UPROPERTY 具体的效果,可以在图 1 右侧的编辑器中看到。可以看到编辑器中多了一个“移动”分组(被自动翻译成了中文),且分组下有我们添加的两个变量。
UPROPERTY 宏是空的。Unreal Engine 应该自己有一套构建工具链,识别翻译这些宏,再进行编译。
- UCLASS()
- class OBSTACLE_API AMovingPlatform : public AActor
- {
- GENERATED_BODY()
- public:
- // Sets default values for this actor's properties
- AMovingPlatform();
- protected:
- // Called when the game starts or when spawned
- virtual void BeginPlay() override;
- // Original position of the platform
- FVector _StartPosition;
- // Current travel distance
- float _CurrentTravelDistance;
- public:
- // Called every frame
- virtual void Tick(float DeltaTime) override;
- // Direction and speed of the platform's movement
- UPROPERTY(EditAnywhere, Category = "Movement")
- FVector MovementVelocity;
- // Maximum distance the platform can move from the start point
- UPROPERTY(EditAnywhere, Category = "Movement")
- float MaxTravelDistance;
- private:
- void HandleMovement(float DeltaTime);
- };
具体的功能实现,见代码清单 2,我们在构造函数中,设置成员变量的默认值。
BeginPlay 函数,我们在学习蓝图的时候已经接触过,和 BeginPlay 事件的概念是一样的。它在此 Actor 开始运行或被生成的时候调用。在此函数中,我们记录 Actor 的初始位置信息。
Tick 函数,我们第一次接触。它在每帧被调用。注意其中的 DeltaTime 参数,它代表自上一帧以来经过的时间,单位是秒。我们通过它来确保物体移动的一致性,即移动距离不会因为帧率的变动而改变。
我们在 HandleMovement 函数中实现移动逻辑。我们首先根据物体的当前位置以及移动速度,计算得到“建议”的位置。如果“建议”的位置没有超出移动范围,就直接用此位置更新物体的位置。如果超过了,我们需要“修正”,把超过的部分,算到“返程”的距离上。这就确保了物体严格在指定范围内“振荡”。
- #include "MovingPlatform.h"
- // Sets default values
- AMovingPlatform::AMovingPlatform()
- {
- // 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;
- MovementVelocity = FVector(100, 0, 0);
- MaxTravelDistance = 500;
- _CurrentTravelDistance = 0;
- }
- // Called when the game starts or when spawned
- void AMovingPlatform::BeginPlay()
- {
- Super::BeginPlay();
- _StartPosition = GetActorLocation();
- _CurrentTravelDistance = 0;
- }
- // Called every frame
- void AMovingPlatform::Tick(float DeltaTime)
- {
- Super::Tick(DeltaTime);
- HandleMovement(DeltaTime);
- }
- void AMovingPlatform::HandleMovement(float DeltaTime)
- {
- FVector CurrentLocation = GetActorLocation();
- FVector TravelDirection = MovementVelocity.GetSafeNormal();
- FVector ProposedNewLocation = CurrentLocation + (MovementVelocity * DeltaTime);
- float DistanceFromStart = FVector::Dist(_StartPosition, ProposedNewLocation);
- if (DistanceFromStart <= MaxTravelDistance)
- SetActorLocation(ProposedNewLocation);
- else
- {
- float Overshoot = DistanceFromStart - MaxTravelDistance;
- FVector EndPoint = _StartPosition + (TravelDirection * MaxTravelDistance);
- FVector NewLocation = EndPoint - (TravelDirection * Overshoot);
- // Reverse direction
- MovementVelocity *= -1;
- SetActorLocation(NewLocation);
- }
- }
最后我们编译程序。因为我们之前已经编译出 Unreal Engine C++ 类了,所以此时我们不需要再完全关闭 Unreal Engine 进行代码编译。我们点击界面最右下角,有一个方块被溶解样式的图标。它是即时编译,不用关闭 Unreal Engine 就可以编译。
我们设置的默认速度是 100 个单位,单位是厘米,即 1 米。乘上 DeltaTime,单位是秒。即我们想要物体以 1 米每秒的速度进行移动。最终的效果如以下视频所示。