将组件添加到Actor

关于将组件添加到Actor的教程。

Choose your operating system:

Windows

macOS

Linux

本页面的内容

组件是一种 对象(Object),可用作 Actor 中的子对象。

组件具有通用性,可以让不同类实现相同行为,例如显示视觉效果、播放声音、处理行为逻辑。

你可以创建三种组件:Actor组件场景组件图元组件

  • Actor组件 (UActorComponent类)可用于实现一些抽象行为,例如移动、物品栏、属性管理以及其他非物理概念。Actor组件没有变换(Transform)属性,这意味着它们在场景中没有物理位置或旋转。

  • 场景组件 (USceneComponent类,UActorComponent的子类)支持基于位置相关的行为,并且此类行为不需要表现几何体。这包括弹簧臂、摄像机、物理力和约束(非物理对象)以及音频。

  • 图元组件 (UPrimitiveComponent类,USceneComponent的子类)是一种能够显示几何体的场景组件,通常用于渲染视觉元素,或用于与物理对象发生碰撞或重叠。这种组件包括静态或骨骼网格体、Sprite、公告板和粒子系统以及盒体、胶囊体和球体碰撞体积。

选择实现方法:

Blueprints

C++

项目设置

在本教程中,你的任务是为名为HealthComponent的Actor组件创建Gameplay逻辑。将此组件添加给Actor后,你的Actor将包含从其生命值中扣除伤害的功能。

我们将在示例中使用第三人称模板项目中包含的第三人称角色。

  1. 首先,点击 新建(New) > 游戏(Games)> 第三人称(Third Person)> 蓝图(Blueprint),创建名为 Health Component 的项目。

image_0.png

  1. 找到 编辑(Edit) > 项目设置(Project Settings) > 引擎(Engine) > 输入(Input) >绑定(Bindings) > 操作映射(Action Mappings),然后点击 添加(Add)+),创建名为 OnDamagePressed 的新操作映射,并将值设置为 1

image_1.png

[输入](InteractiveExperiences/Input/)
将包含操作映射,可用于将按钮或按键映射到事件驱动型行为,例如对玩家角色造成伤害。

创建Actor组件:生命值组件

在本小节中,你将创建生命值组件,这是可以附加到任意Actor的Actor组件。

按照以下步骤开始创建将持续包含和扣除玩家生命值的逻辑。

  1. 内容浏览器(Content Browser) 中点击 添加/新建(Add/New) > 蓝图类(Blueprint Class)。然后,在 选取原生类(Pick a Native Class) 菜单中选择 Actor组件(Actor Component),创建名为 Bp_HealthComponent 的新Actor组件。

    image_2.png

  2. 类默认值(Class Defaults) 中,找到"我的蓝图(My Blueprint)"面板。然后,在 变量(Variables) 类别中点击 添加(Add)+)按钮,创建名为 HealthMaxHealth 的两个 浮点 变量。

    image_3.png

  3. 选择 最大生命值(Max Health) 变量并找到"细节(Details)"面板,将默认的 最大生命值(Max Health) 值设置为 100.0

image_4.png

  1. Max Health 浮点的副本拖入 事件图表(Event Graph)

    image_5.png

  2. 生命值(Health) 浮点拖到 事件图表(Event Graph),然后选择 设置生命值(Set Health),创建 Set Health 节点。

    image_6.png

  3. Max Health 浮点上的 输出 引脚连接到 Set Health 节点上的 生命值(Health) 输入 引脚,然后将 Event Begin Play 节点的输出执行引脚连接到 Set Health 节点的输入执行引脚。

    image_7.png

  4. 点击 编译(Compile)保存(Save)

    image_8.png

创建事件分发器:处理伤害

事件分发器可以调用Actor中绑定自身或"侦听"事件分发器的方法。然后,当事件分发器调用事件时,侦听它的Actor将响应该事件。

按照以下步骤开始创建"Handle Take Damage 事件分发器"。

  1. 我的蓝图(My Blueprint) 面板中找到 事件分发器(Event Dispatchers) 类别,并点击 添加(Add)+)按钮,创建名为 HandleTakeDamage 的新事件分发器。

    image_9.png

  2. 找到 细节(Details) 面板,然后在 输入(Input) 类别中点击 添加(Add)+)按钮,创建以下变量:

    变量名称

    类型

    说明

    DamageActor

    Bp_HealthComponent

    将承受伤害的Actor。

    生命值(Health)

    浮点(Float)

    Actor的基础生命值。

    伤害(Damage)

    浮点(Float)

    要施加的基础伤害。

    DamageType

    伤害类型(Damage Type)

    用于描述所造成伤害的类。

    InstigatedBy

    控制器(Controller)

    负责造成伤害的控制器。例如,投掷手榴弹的玩家。

    DamageCauser

    Actor

    代表伤害原因的Actor。例如,已经爆炸的手榴弹。

    image_10.png

  3. 点击 编译(Compile)保存(Save)

    image_11.png

创建函数:承受伤害

你将需要创建函数,处理在玩家角色受到伤害时扣除玩家角色生命值的逻辑。

为此,请按照以下步骤操作:

  1. 我的蓝图(My Blueprint) 窗口中找到"函数(Functions)"类别,点击 添加(Add)+)按钮,创建名为 TakeDamage 的新函数。

    image_12.png

  2. 找到 细节(Details) 面板。然后在 输入(Inputs) 类别中点击 添加(Add)(+) 按钮,创建以下输入。

    变量名称

    类型

    DamageActor

    Actor

    伤害(Damage)

    浮点(Float)

    DamageType

    伤害类型(Damage Type)

    InstigatedBy

    控制器(Controller)

    DamageCauser

    Actor

    image_13.png

  3. 我的蓝图(My Blueprint) 面板中找到 变量(Variables) 类别,并将 生命值(Health) 变量拖到 事件图表(Event Graph)

image_14.png

  1. Take Damage 节点,拖移 伤害(Damage) 变量。然后,在 操作(Actions) 菜单中搜索并选择 float - float

    image_15.png

  2. 生命值(Health) 引脚拖移,并将其连接到 float - float 节点的 B减法浮点

    image_16.png

  3. float - float 节点拖移结果,然后,在 操作(Actions) 菜单中搜索并选择 Clamp (float)

    image_17.png

  4. 我的蓝图(My Blueprint) 面板中找到 变量(Variables) 类别,然后将 最大(Max) 生命值(Health) 变量拖到 Clamp(float) 节点的 最大 浮点变量引脚

    image_18.png

  5. Take Damage 节点拖移执行引脚,然后在 操作(actions) 菜单中搜索并选择 Branch (if)

image_19.png

  1. Take Damage 节点,拖移 伤害(Damage) 变量。然后,在 操作(Actions) 菜单中搜索并选择 <= 小于等于) 运算符。

image_20.png

  1. Less than equal to 运算符节点,拖出 布尔值 返回引脚,并将其插入 Branch 节点的 条件(Condition) 引脚。

    image_21.png

  2. Branch 节点,拖移 False 执行引脚。然后在 操作(Actions) 菜单中搜索并选择 Set Health

    image_22.png

  3. 找到 Clamp (float) 节点,并拖移 返回值(Return Value)。然后将其连接到 Set Health 节点的 生命值(Health) 引脚。

    image_23.png

  4. Set Health 节点,拖移输出执行引脚,然后在 操作(Actions) 菜单中搜索并选择 Call Handle Take Damage

    image_24.png

  5. Set Health 节点,将 生命值(Health) 输出引脚连接到 Call Handle Take Damage 节点的 生命值(Health) 输入引脚。

    image_25.png

  6. 找到 Take Damage 节点,然后将 伤害(Damage)、 伤害类型(Damage Type)实施者(Instigated By)伤害原因(Damage Causer) 引脚连接到 Call Handle Take Damage 节点的各个引脚。

    image_26.png

  7. 点击 编译(Compile)保存(Save)

    image_27.png

  8. 找到 Event Graph > Begin Play 节点,然后从 Set Max Health节点 拖移输出执行引脚。在 操作(Actions) 菜单中取消选中 上下文有关(Context Sensitive) 方框,然后搜索并选择 Bind Event to On Take Any Damage

    image_28.png

  9. Bind Event to On Take Any Damage 节点,拖移 目标(Target) 引脚,然后在 操作(Actions) 菜单的 组件(Components) 类别中搜索并选择 Get Owner

    image_29.png

  10. Bind Event to On Take Any Damage 节点,拖移 事件(Event) 引脚,并在 操作(Actions) 菜单中搜索并选择 Create Event

    image_30.png

  11. Create Event 节点,点击 选择函数(Select Function) 下拉菜单,并选择函数 TakeDamage

    image_31.png

  12. 点击 编译(Compile)保存(Save)

    image_32.png

完成的蓝图

Take Damage函数

image_33.png

开始运行

image_34.png

将生命值组件添加到第三称角色

现在你已经创建生命值组件类,你需要将其Actor组件添加到第三人称角色类,并绑定OnDamagePressed操作映射,以便对玩家造成伤害。

  1. 找到 内容(Content) > ThirdPersonBp > 蓝图(Blueprints) 文件夹,并双击 ThirdPersonCharacter 打开其 类默认值(Class Defaults)

    image_35.png

  2. 组件(Components) 面板中,点击 添加组件(Add Component) 按钮,然后搜索并选择 Bp Health Component

    image_36.png

  3. 右键点击 事件图表(Event Graph),然后在 操作(Actions) 菜单中搜索并选择 OnDamagePressed

    image_37.png

  4. 从 InputAction OnDamagePressed 节点,拖移 Pressed 执行引脚,然后在 操作(Actions) 菜单中搜索并选择 Apply Damage

    image_38.png

  5. Apply Damage 节点,拖移 已受伤Actor(Damaged Actor) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Get a reference to self。将 基础伤害(Base Damage) 浮点值设置为 20.0

    image_39.png

    应用伤害函数是Gameplay静态库(Gameplay Statics Library)的一部分,库中包括用于Gameplay的实用泛型函数。

  6. 右键点击 事件图表(Event Graph),然后在 操作(Actions) 菜单中搜索并选择 Event Begin Play

    image_40.png

  7. 组件(Components) 面板中,将 BP_HealthComponent 的副本拖放到 事件图表(Event Graph)

    image_41.png

  8. 拖移 Bp Health Component 引脚,然后在 操作(Actions) 菜单中搜索并选择 Bind Event to Handle Take Damage

    image_42.png

  9. 拖移 Bind Event to Handle Take Damage事件(Event) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Create Event,创建名为 OnHealthChanged 的自定义事件。

    image_43.png

  10. OnHealthChanged 节点,拖移 生命值(Health) 引脚然后在 操作(Actions) 菜单中搜索并选择 <= 小于等于)运算符。

    image alt text

  11. OnHealthChanged 节点,拖移 执行(Execution) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Branch

    image_45.png

  12. <= (Less than equal to) 运算符节点,拖出 布尔值 返回引脚,并将其连接到 Branch 节点的 条件(Condition) 引脚。

    image_46.png

  13. Branch 节点拖移 True 执行引脚,然后在 操作(actions) 菜单中搜索并选择 DestroyActor

    image_47.png

    当玩家的生命值为零时,他们将被移出世界。

  14. OnHealthChanged 节点,拖移 执行(Execution) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Print String

    image_48.png

    每当玩家角色受到伤害时,我们使用打印字符串将当前的生命值打印到屏幕上。

  15. 点击 编译(Compile)保存(Save)

    image_49.png

完成的蓝图

image_50.png

最终结果

你现在可以测试生命值组件的功能。

你可以使用WASD键找到你的角色。当按下数字键1时,你的角色将受到伤害,直到其生命值为零,此时角色将从世界上消失。

  1. 找到 工具栏(Toolbar),点击 运行(Play)

image_51.png

image_52.gif

项目设置

在本教程中,你的任务是为名为HealthComponent的Actor组件创建Gameplay逻辑。将此组件附加到Actor后,你的Actor将包含从其生命值中扣除伤害的功能。

我们将在示例中使用第三人称模板项目中包含的第三人称角色。

  1. 首先,点击 新建(New) > 游戏(Games)> 第三人称(Third Person)> CPP,创建名为 Health Comp 的项目。

    image_53.png

  2. 找到 编辑(Edit) > 项目设置(Project Settings) >引擎(Engine) > 输入(Input) >绑定(Bindings) > 操作映射(Action Mappings),然后点击 添加(Add)+),创建名为 OnDamagePressed 的新操作映射,并将值设置为 1

    image_54.png

    [输入](InteractiveExperiences/Input/)
    将包含操作映射,可用于将按钮或按键映射到事件驱动型行为,例如对玩家角色造成伤害。

创建Actor组件:生命值组件

委托能够以泛型类型安全的方式调用C++对象上的成员函数。当Actor将自己绑定到委托时,它将响应该委托的成员函数事件。

按照以下步骤开始创建On Health Changed委托。

  1. 点击 添加/导入(Add/Import) 可以创建一个 新的C++类(New C++ Class),然后从 选择父类(Choose a Parent Class) 菜单中,选择 Actor组件(Actor Component),创建名为 HealthComponent 的新Actor组件。

    image_55.png

  2. 在HealthComponent.h文件中,声明以下委托:

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComp, float, Health, float, DamageAmount, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
  3. 找到 类默认值(Class Defaults) 并声明以下方法和变量:

    public:
    
    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnHealthChangedSignature OnHealthChanged;
    
    protected:
    
    UPROPERTY(EditDefaultsOnly,BlueprintReadWrite)
    float Health;
    
    UPROPERTY(EditDefaultsOnly,BlueprintReadWrite)
    float MaxHealth;
    
    UFUNCTION(BlueprintCallable)
    void HandleTakeDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
  4. 找到HealthComponent.cpp文件并在 HealthComponent Constructor 中初始化以下类变量:

    UHealthComponent::UHealthComponent()
    
    {
    
    MaxHealth = 100.0f;
    
    }
  5. BeginPlay 类方法实现以下定义:

    void UHealthComponent::BeginPlay
    
    {
    Super::BeginPlay();
    
    //获取此Actor组件的所有者。
    AActor* MyOwner = GetOwner();
    if (MyOwner)
    {
        //所有者对象现在一定会响应OnTakeAnyDamage函数。        
        MyOwner->OnTakeDamage.AddDynamic(this,&UHealthComponent::HandleTakeDamage);
    }
    
    //将生命值设置为最大生命值。         
    Health = MaxHealth;
    }
  6. HandleTakeAnyDamage 方法声明以下代码:

    void UHealthComponent::HandleTakeAnyDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser) 
    { 
    
        if (Damage <= 0.0f) 
        { 
            return; 
        }
    
        Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth); OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser); 
    }
  7. 编译 你的代码。

已完成代码

Health Component.h

    #pragma once

    #include "CoreMinimal.h"
    #include "Components/ActorComponent.h"
    #include "HealthComponent.generated.h"

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComponent, float, Health, float, DamageAmount, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);

    UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class HEALTHCOMP_API UHealthComponent : public UActorComponent
    {
    GENERATED_BODY()

    public: 

    // 为该组件的属性设置默认值
    UHealthComponent();

    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnHealthChangedSignature OnHealthChanged;

    protected:

    // 游戏开始时调用
    UFUNCTION(BlueprintCallable)

    void HandleTakeDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);

    virtual void BeginPlay() override;

    UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
    float Health;

    UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
    float MaxHealth;

    public: 

    // 每一帧都调用
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    };

Health Component.cpp

    #include "HealthComponent.h"

    // 为该组件的属性设置默认值
    UHealthComponent::UHealthComponent()
    {
    // 将此组件设置为在游戏开始时初始化,并且每帧更新函数。  如果你不需要这些功能,你可以把它们
    // 关闭以便提高性能。
    PrimaryComponentTick.bCanEverTick = true;
    MaxHealth = 100.0f;
    }

    // 游戏开始时调用
    void UHealthComponent::BeginPlay()
    {
    Super::BeginPlay();

    //获取此Actor组件的所有者。
    AActor* MyOwner = GetOwner();

    if(MyOwner)
    {
        MyOwner->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::HandleTakeDamage);

        //所有者对象现在一定会响应OnTakeAnyDamage函数。        
        MyOwner->OnTakeAnyDamage.AddDynamic(this,&UHealthComponent::HandleTakeAnyDamage);
    }

    //将生命值设置为最大生命值。
    Health = MaxHealth;
    }

    void UHealthComponent::HandleTakeDamage(AActor* DamageActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
    {

    if (Damage <= 0.0f)
    {
        //伤害量为0或以下。
        return;
    } 

    Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth); OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser);
    }

    // 每一帧都调用
    void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
        Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    }

将生命值组件添加到HealthComp角色

现在你已经创建生命值组件类,你需要将其Actor组件添加到生命值组件角色类,并绑定OnDamagePressed操作映射,以便对玩家造成伤害。

  1. 内容浏览器(Content Browser) 中找到 C++类(C++ Classes) > HealthComp,双击 HealthComp Character 打开 HealthCompCharacter.h 文件。

  2. 类默认值 声明以下代码:

    protected:
    
    UPROPERTY(EditDefaultsOnly,BlueprintReadWrite)
    class UHealthComponent* HealthComponent; 
    
    UFUNCTION()
    OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
    
    //调用后会对我们的玩家造成伤害的函数
    UFUNCTION()
    void DamagePlayerCharacter();
  3. 找到HealthCompCharacter.cpp文件并添加以下类库:

    #include "HealthComponent.h"
    #include "Kismet/GameplayStatics.h"

    Gameplay静态库包括许多实用辅助函数,可以满足你的Gameplay目标需求。在本例中,我们将使用:UGameplayStatics::ApplyDamageApplyDamage方法。

  4. AHealthCompCharacter Constructor 中,初始化你的生命值组件类:

    AHealthCompCharacter::AHealthCompCharacter()
    {
        HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent");
    }
  5. 找到 AHealthCompChatacter::BeginPlay 方法,并添加以下代码:

    void AHealthCompCharacter::BeginPlay()
    {
        Super::BeginPlay();
        HealthComponent->OnHealthChanged.AddDynamic(this, &AHealthCompCharacter::OnHealthChanged);
    }
  6. AHealthCompCharacter::OnHealthChanged 方法中,实现以下逻辑,以便在玩家生命值为零时摧毁玩家角色:

    void AHealthCompCharacter::OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
    {
        if (Health <= 0.0f)
    {
        Destroy();
    }
        UE_LOG(LogTemp, Warning, TEXT("The Player's Current Health is: %f"), Health);
    }

    UE_LOG是包含不同

    [冗长度类型]([https://docs.unrealengine.com/4.27/en-US/API/Runtime/Core/Logging/ELogVerbosity__Type/](https://docs.unrealengine.com/4.27/en-US/API/Runtime/Core/Logging/ELogVerbosity__Type/)
    )的宏,可与命令结合使用,将数据可视化到输出日志。

  7. 添加以下代码,实现 AHealthCompCharacter::DamagePlayerCharacter 方法:

    void AHealthCompCharacter::DamagePlayerCharacter()
    {
        UGameplayStatics::ApplyDamage(this, 20.0f, GetInstigatorController(),this,GenericDamageType);
    }
  8. 找到 AHealthCompCharacter::SetupPlayerInputComponent 方法,将 OnDamagePressed 绑定分配到DamagePlayerCharacter函数:

    void AHealthCompCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
    {
        // 绑定OnDamagePressed操作事件
        PlayerInputComponent->BindAction("OnDamagePressed", IE_Pressed, this, &AHealthCompCharacter::DamagePlayerCharacter);
    }
  9. 编译 你的代码。

已完成代码

HealthCompCharacter.h

    #pragma once
    #include "CoreMinimal.h"
    #include "GameFramework/Character.h"
    #include "HealthCompCharacter.generated.h"

    UCLASS(config=Game)
    class AHealthCompCharacter : public ACharacter
    {
        GENERATED_BODY()

    /** 用于将摄像机放置在角色身后的摄像机升降臂 */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class USpringArmComponent* CameraBoom;

    /** 跟随摄像机 */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UCameraComponent* FollowCamera;

    public:

    AHealthCompCharacter();
    /** 基础旋转速度,以度/秒为单位。其他单位可能会影响最终旋转速度。*/

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
    float BaseTurnRate;

    /** 基础向上看/向下看速度,以度/秒为单位。其他单位可能会影响最终速度。*/
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
    float BaseLookUpRate;

    protected:

    virtual void BeginPlay() override;

    UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
    class UHealthComponent* HealthComponent;

    UFUNCTION()
    void OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);

    //调用后会对我们的玩家造成伤害的函数
    UFUNCTION()
    void DamagePlayerCharacter();

    //对玩家造成的伤害类型的容器
    UPROPERTY(EditDefaultsOnly,BlueprintReadOnly, Category = "Weapon")
    TSubclassOf<UDamageType> GenericDamageType;

    /** 重置VR中的HMD方向。*/
    void OnResetVR();

    /** 为向前/向后输入而调用*/
    void MoveForward(float Value);

    /** 为侧面到侧面输入而调用 */
    void MoveRight(float Value);

    /** 
     * 通过输入进行调用,从而以给定的速度旋转 
     * @param 速度    这是规范化速度,即1.0表示100%的所需旋转速度
     */
    void TurnAtRate(float Rate);

    /**
     * 通过输入进行调用,从而以给定的速度向上看/向下看 
     * @param 速度    这是规范化速度,即1.0表示100%的所需旋转速度
     */
    void LookUpAtRate(float Rate);

    /** 在触摸输入开始时使用的处理程序。*/
    void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);

    /** 在触摸输入停止时使用的处理程序。*/
    void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);

    protected:

    // APawn接口
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

    // APawn末端接口

    public:

    /** 返回CameraBoom子对象 **/
    FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }

    /** 返回FollowCamera子对象 **/
    FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }

    };

HealthCompCharacter.cpp

    #include "HealthCompCharacter.h"
    #include "HeadMountedDisplayFunctionLibrary.h"
    #include "Camera/CameraComponent.h"
    #include "Components/CapsuleComponent.h"
    #include "Components/InputComponent.h"
    #include "GameFramework/CharacterMovementComponent.h"
    #include "GameFramework/Controller.h"
    #include "GameFramework/SpringArmComponent.h"
    #include "HealthComponent.h"
    #include "Kismet/GameplayStatics.h"

//////////////////////////////////////////////////////////////////////////

// AHealthCompCharacter

    AHealthCompCharacter::AHealthCompCharacter()
    {

    // 设置碰撞胶囊体的大小
    GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

    // 为输入设置我们的旋转速度
    BaseTurnRate = 45.f;
    BaseLookUpRate = 45.f;

    // 当控制器旋转时不旋转使其仅影响摄像机。
    bUseControllerRotationPitch = false;
    bUseControllerRotationYaw = false;
    bUseControllerRotationRoll = false;

    // 配置角色移动
    GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...   
    GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...以此旋转速度
    GetCharacterMovement()->JumpZVelocity = 600.f;
    GetCharacterMovement()->AirControl = 0.2f;

    // 创建摄像机升降臂(发生碰撞则朝着玩家推进)
    CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
    CameraBoom->SetupAttachment(RootComponent);
    CameraBoom->TargetArmLength = 300.0f; // 摄像机以此距离在角色后方跟随 
    CameraBoom->bUsePawnControlRotation = true; // 基于控制器旋转升降臂

    // 创建一个跟随摄像机
    FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
    FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // 将摄像机连接到升降臂末端,对升降臂进行调整以便与控制器方向保持一致
    FollowCamera->bUsePawnControlRotation = false; // 摄像机不相对于升降臂旋转

    // 注意:网格体组件上的骨骼网格体和动画蓝图引用(从角色继承) 
    // 是在名为MyCharacter的推导蓝图资产中设置的(以避免在C++中直接引用内容)

    HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"));

    }

    //////////////////////////////////////////////////////////////////////////

    // 输入

    void AHealthCompCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
    {

    // 设置Gameplay键绑定
    check(PlayerInputComponent);

    PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
    PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
    PlayerInputComponent->BindAxis("MoveForward", this, &AHealthCompCharacter::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &AHealthCompCharacter::MoveRight);

    // 我们提供了两个版本的旋转绑定来处理不同类型的设备,
    // "旋转"负责处理可以提供绝对增量的设备,例如鼠标。
    // "旋转速度"适用于我们选择作为更改速度来处理的设备,例如模拟摇杆

    PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
    PlayerInputComponent->BindAxis("TurnRate", this, &AHealthCompCharacter::TurnAtRate);
    PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
    PlayerInputComponent->BindAxis("LookUpRate", this, &AHealthCompCharacter::LookUpAtRate);

    // 处理触摸设备
    PlayerInputComponent->BindTouch(IE_Pressed, this, &AHealthCompCharacter::TouchStarted);
    PlayerInputComponent->BindTouch(IE_Released, this, &AHealthCompCharacter::TouchStopped);

    // VR头戴设备功能
    PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AHealthCompCharacter::OnResetVR);

    // 绑定OnDamagePressed操作事件
    PlayerInputComponent->BindAction("OnDamagePressed", IE_Pressed, this, &AHealthCompCharacter::DamagePlayerCharacter);
}

    void AHealthCompCharacter::BeginPlay()
    {
    Super::BeginPlay();
    HealthComponent->OnHealthChanged.AddDynamic(this, &AHealthCompCharacter::OnHealthChanged);
    }

    void AHealthCompCharacter::OnHealthChanged(UHealthComponent* HealthComp, float Health, float DamageAmount, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
    {
    if (Health <= 0.0f)
    {
        Destroy();
    }
    UE_LOG(LogTemp, Warning, TEXT("The Player's Current Health is: %f"), Health);
    }

    void AHealthCompCharacter::DamagePlayerCharacter()
    {
        UGameplayStatics::ApplyDamage(this, 20.0f, GetInstigatorController(),this,GenericDamageType);
    }

    void AHealthCompCharacter::OnResetVR()
    {

    // 如果在虚幻编辑器中通过"添加功能(Add Feature)"将HealthComp添加到项目,则HealthComp.Build.cs中HeadMountedDisplay上的依赖关系不会自动传播,
    // 并将产生连接器错误。
    // 你将需要:
    // 将"HeadMountedDisplay"添加到[YourProject].Build.cs,以便成功构建PublicDependencyModuleNames(如果支持VR则适用)
    // 或者:
    // 注释或删除以下对ResetOrientationAndPosition的调用(如果不支持VR则适用)

    UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
    }
    void AHealthCompCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
    {
        Jump();
    }

    void AHealthCompCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
    {
        StopJumping();
    }

    void AHealthCompCharacter::TurnAtRate(float Rate)
    {
    // 根据速度信息计算此帧的增量
    AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
    }

    void AHealthCompCharacter::LookUpAtRate(float Rate) 
    {
    // 根据速度信息计算此帧的增量
    AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
    }

    void AHealthCompCharacter::MoveForward(float Value)
    {
        if ((Controller != nullptr) && (Value != 0.0f))
        {
            // 找出向前方向
            const FRotator Rotation = Controller->GetControlRotation();
            const FRotator YawRotation(0, Rotation.Yaw, 0);

            // 获取向前向量
            const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
            AddMovementInput(Direction, Value);
        }
    }

    void AHealthCompCharacter::MoveRight(float Value)
    {

        if ( (Controller != nullptr) && (Value != 0.0f) )
    {

        // 找出向右方向
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // 获取向右向量 
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

        // 添加该方向动作
        AddMovementInput(Direction, Value);
    }
    }

最终结果

你现在可以测试生命值组件的功能。

你可以使用WASD键找到你的角色。当按下数字键1时,你的角色将受到伤害,直到其生命值为零,此时角色将从世界上消失。

  1. 找到 工具栏(Toolbar),按 运行(Play)

image_56.png

image_57.gif

其他资源

下方链接列出了在蓝图中添加组件时可能涉及的其他概念和信息:

本文基于此前的虚幻引擎版本编写,未针对当前的虚幻引擎5.0版本更新过。