创建 C++ 子类

目前我们已经实现好了一个 C++ 基类 BasePawn,并且基于这个基类创建了两个蓝图类,分别用于坦克和炮塔。

坦克和炮塔 Mesh 布局是类似的,但是操作逻辑不一样,比如坦克需要玩家输入控制,而炮塔只需要代码控制。所以我们需要在基类基础上,创建子类进行管理。

因为在这个系列中,我们以 C++ 为主,蓝图为辅,所以我们需要再基于 BasePawn C++ 类,创建两个 C++ 子类,用于坦克和炮塔。

创建 C++ 子类的方式如图 1 所示,我们选中父类 BasePawn,然后右键选择 创建派生自BasePawn的C++类。在弹出的窗体里命名类名,此处我们先创建坦克,命名为 Tank。

图1 创建 C++ 子类

在新生成的 Tank 类中,我们添加新的组件:一个相机组件,用于显示坦克主体视角;一个弹簧臂组件,让相机附加。

弹簧臂的核心作用:

保持距离:可以让附加的子组件(比如此处的相机)保持与其父组件一个固定距离;

平滑移动:内置了平滑追踪功能,像弹簧一样拉近或拉远;

自动避免碰撞:如果弹簧路径上有障碍物,它会自动缩短,避免相机穿模。

代码清单 1 新组件
  1. UCLASS()
  2. class TOONTANKS_API ATank : public ABasePawn
  3. {
  4.     GENERATED_BODY()
  5.    
  6. public:
  7.     ATank();
  8.  
  9. private:
  10.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
  11.     USpringArmComponent* SpringArm;
  12.  
  13.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
  14.     UCameraComponent* Camera;
  15. };

接着我们实现坦克的构造函数。如代码清单 2 所示,我们把弹簧臂组件附加在根组件下,然后把相机附加在弹簧臂组件下。

代码清单 2 Tank.cpp
  1. #include "Tank.h"
  2.  
  3. ATank::ATank()
  4. {
  5.     SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Spring Arm"));
  6.     SpringArm->SetupAttachment(RootComponent);
  7.  
  8.     Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
  9.     Camera->SetupAttachment(SpringArm);
  10. }

实现好坦克子类之后,我们可以和之前一样的操作,派生一个坦克蓝图。但是我们之前已经基于 BasePawn 派生过一个了,而且已经设置好 Mesh 了,再设置就都是重复的工作了。

有一个简便的操作,我们来到之前的坦克蓝图类。如图 2 所示,选择 类设置,将 父类 修改为 Tank。设置好之后,可以发现之前的 Mesh 还在,组件窗口里也新增了弹簧臂和相机组件。

要想改变相机的朝向和位置,我们无需修改相机组件参数,只需要修改其附加的弹簧臂组件即可。可以修改弹簧臂的 目标臂长度 和旋转属性,将相机放置在一个合适的位置。

图2 改变父类