组件是一种 对象(Object),可用作 Actor 中的子对象。
组件具有通用性,可以让不同类实现相同行为,例如显示视觉效果、播放声音、处理行为逻辑。
你可以创建三种组件:Actor组件、场景组件 和 图元组件。
-
Actor组件 (UActorComponent类)可用于实现一些抽象行为,例如移动、物品栏、属性管理以及其他非物理概念。Actor组件没有变换(Transform)属性,这意味着它们在场景中没有物理位置或旋转。
-
场景组件 (USceneComponent类,UActorComponent的子类)支持基于位置相关的行为,并且此类行为不需要表现几何体。这包括弹簧臂、摄像机、物理力和约束(非物理对象)以及音频。
-
图元组件 (UPrimitiveComponent类,USceneComponent的子类)是一种能够显示几何体的场景组件,通常用于渲染视觉元素,或用于与物理对象发生碰撞或重叠。这种组件包括静态或骨骼网格体、Sprite、公告板和粒子系统以及盒体、胶囊体和球体碰撞体积。
项目设置
在本教程中,你的任务是为名为HealthComponent的Actor组件创建Gameplay逻辑。将此组件添加给Actor后,你的Actor将包含从其生命值中扣除伤害的功能。
我们将在示例中使用第三人称模板项目中包含的第三人称角色。
-
首先,点击 新建(New) > 游戏(Games)> 第三人称(Third Person)> 蓝图(Blueprint),创建名为 Health Component 的项目。
Click for full view.
-
找到 编辑(Edit) > 项目设置(Project Settings) > 引擎(Engine) > 输入(Input) >绑定(Bindings) > 操作映射(Action Mappings),然后点击 添加(Add)(+),创建名为 OnDamagePressed 的新操作映射,并将值设置为 1。
Click for full view.
输入将包含操作映射,可用于将按钮或按键映射到事件驱动型行为,例如对玩家角色造成伤害。
创建Actor组件:生命值组件
在本小节中,你将创建生命值组件,这是可以附加到任意Actor的Actor组件。
按照以下步骤开始创建将持续包含和扣除玩家生命值的逻辑。
-
在 内容浏览器(Content Browser) 中点击 添加/新建(Add/New) > 蓝图类(Blueprint Class)。然后,在 选取原生类(Pick a Native Class) 菜单中选择 Actor组件(Actor Component),创建名为 Bp_HealthComponent 的新Actor组件。
-
在 类默认值(Class Defaults) 中,找到"我的蓝图(My Blueprint)"面板。然后,在 变量(Variables) 类别中点击 添加(Add)(+)按钮,创建名为 Health 和 MaxHealth 的两个 浮点 变量。
-
选择 最大生命值(Max Health) 变量并找到"细节(Details)"面板,将默认的 最大生命值(Max Health) 值设置为 100.0。
-
将 Max Health 浮点的副本拖入 事件图表(Event Graph)。
-
将 生命值(Health) 浮点拖到 事件图表(Event Graph),然后选择 设置生命值(Set Health),创建 Set Health 节点。
-
将 Max Health 浮点上的 输出 引脚连接到 Set Health 节点上的 生命值(Health) 输入 引脚,然后将 Event Begin Play 节点的输出执行引脚连接到 Set Health 节点的输入执行引脚。
-
点击 编译(Compile) 和 保存(Save)。
创建事件分发器:处理伤害
事件分发器可以调用Actor中绑定自身或"侦听"事件分发器的方法。然后,当事件分发器调用事件时,侦听它的Actor将响应该事件。
按照以下步骤开始创建"Handle Take Damage 事件分发器"。
-
在 我的蓝图(My Blueprint) 面板中找到 事件分发器(Event Dispatchers) 类别,并点击 添加(Add)(+)按钮,创建名为 HandleTakeDamage 的新事件分发器。
-
找到 细节(Details) 面板,然后在 输入(Input) 类别中点击 添加(Add)(+)按钮,创建以下变量:
变量名称 类型 说明 DamageActor Bp_HealthComponent 将承受伤害的Actor。 生命值(Health) 浮点(Float) Actor的基础生命值。 伤害(Damage) 浮点(Float) 要施加的基础伤害。 DamageType 伤害类型(Damage Type) 用于描述所造成伤害的类。 InstigatedBy 控制器(Controller) 负责造成伤害的控制器。例如,投掷手榴弹的玩家。 DamageCauser Actor 代表伤害原因的Actor。例如,已经爆炸的手榴弹。 -
点击 编译(Compile) 和 保存(Save)。
创建函数:承受伤害
你将需要创建函数,处理在玩家角色受到伤害时扣除玩家角色生命值的逻辑。
为此,请按照以下步骤操作:
-
在 我的蓝图(My Blueprint) 窗口中找到"函数(Functions)"类别,点击 添加(Add)(+)按钮,创建名为 TakeDamage 的新函数。
-
找到 细节(Details) 面板。然后在 输入(Inputs) 类别中点击 添加(Add)(+) 按钮,创建以下输入。
变量名称 类型 DamageActor Actor 伤害(Damage) 浮点(Float) DamageType 伤害类型(Damage Type) InstigatedBy 控制器(Controller) DamageCauser Actor -
在 我的蓝图(My Blueprint) 面板中找到 变量(Variables) 类别,并将 生命值(Health) 变量拖到 事件图表(Event Graph)。

-
从 Take Damage 节点,拖移 伤害(Damage) 变量。然后,在 操作(Actions) 菜单中搜索并选择 float - float。
-
从 生命值(Health) 引脚拖移,并将其连接到 float - float 节点的 B减法浮点。
-
从 float - float 节点拖移结果,然后,在 操作(Actions) 菜单中搜索并选择 Clamp (float)。
-
在 我的蓝图(My Blueprint) 面板中找到 变量(Variables) 类别,然后将 最大(Max) 生命值(Health) 变量拖到 Clamp(float) 节点的 最大 浮点变量 引脚 。
-
从 Take Damage 节点拖移执行引脚,然后在 操作(actions) 菜单中搜索并选择 Branch (if)。

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

-
从 Less than equal to 运算符节点,拖出 布尔值 返回引脚,并将其插入 Branch 节点的 条件(Condition) 引脚。
-
从 Branch 节点,拖移 False 执行引脚。然后在 操作(Actions) 菜单中搜索并选择 Set Health。
-
找到 Clamp (float) 节点,并拖移 返回值(Return Value)。然后将其连接到 Set Health 节点的 生命值(Health) 引脚。
-
从 Set Health 节点,拖移输出执行引脚,然后在 操作(Actions) 菜单中搜索并选择 Call Handle Take Damage。
-
从 Set Health 节点,将 生命值(Health) 输出引脚连接到 Call Handle Take Damage 节点的 生命值(Health) 输入引脚。
-
找到 Take Damage 节点,然后将 伤害(Damage)、 伤害类型(Damage Type)、实施者(Instigated By) 和 伤害原因(Damage Causer) 引脚连接到 Call Handle Take Damage 节点的各个引脚。
-
点击 编译(Compile) 和 保存(Save)。

-
找到 Event Graph > Begin Play 节点,然后从 Set Max Health节点 拖移输出执行引脚。在 操作(Actions) 菜单中取消选中 上下文有关(Context Sensitive) 方框,然后搜索并选择 Bind Event to On Take Any Damage。
-
从 Bind Event to On Take Any Damage 节点,拖移 目标(Target) 引脚,然后在 操作(Actions) 菜单的 组件(Components) 类别中搜索并选择 Get Owner。
-
从 Bind Event to On Take Any Damage 节点,拖移 事件(Event) 引脚,并在 操作(Actions) 菜单中搜索并选择 Create Event。
-
从 Create Event 节点,点击 选择函数(Select Function) 下拉菜单,并选择函数 TakeDamage。
-
点击 编译(Compile) 和 保存(Save)。
完成的蓝图
Take Damage函数

开始运行

将生命值组件添加到第三称角色
现在你已经创建生命值组件类,你需要将其Actor组件添加到第三人称角色类,并绑定OnDamagePressed操作映射,以便对玩家造成伤害。
-
找到 内容(Content) > ThirdPersonBp > 蓝图(Blueprints) 文件夹,并双击 ThirdPersonCharacter 打开其 类默认值(Class Defaults)。
-
在 组件(Components) 面板中,点击 添加组件(Add Component) 按钮,然后搜索并选择 Bp Health Component。
-
右键点击 事件图表(Event Graph),然后在 操作(Actions) 菜单中搜索并选择 OnDamagePressed。
-
从 InputAction OnDamagePressed 节点,拖移 Pressed 执行引脚,然后在 操作(Actions) 菜单中搜索并选择 Apply Damage。
-
从 Apply Damage 节点,拖移 已受伤Actor(Damaged Actor) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Get a reference to self。将 基础伤害(Base Damage) 浮点值设置为 20.0。
应用伤害函数是Gameplay静态库(Gameplay Statics Library)的一部分,库中包括用于Gameplay的实用泛型函数。
-
右键点击 事件图表(Event Graph),然后在 操作(Actions) 菜单中搜索并选择 Event Begin Play。
-
在 组件(Components) 面板中,将 BP_HealthComponent 的副本拖放到 事件图表(Event Graph)。
-
拖移 Bp Health Component 引脚,然后在 操作(Actions) 菜单中搜索并选择 Bind Event to Handle Take Damage。
-
拖移 Bind Event to Handle Take Damage 的 事件(Event) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Create Event,创建名为 OnHealthChanged 的自定义事件。
-
从 OnHealthChanged 节点,拖移 生命值(Health) 引脚,然后在 操作(Actions) 菜单中搜索并选择 <= (小于等于)运算符。
-
从 OnHealthChanged 节点,拖移 执行(Execution) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Branch。
-
从 <= (Less than equal to) 运算符节点,拖出 布尔值 返回引脚,并将其连接到 Branch 节点的 条件(Condition) 引脚。
-
从 Branch 节点拖移 True 执行引脚,然后在 操作(actions) 菜单中搜索并选择 DestroyActor。
当玩家的生命值为零时,他们将被移出世界。
-
从 OnHealthChanged 节点,拖移 执行(Execution) 引脚,然后在 操作(Actions) 菜单中搜索并选择 Print String。
每当玩家角色受到伤害时,我们使用打印字符串将当前的生命值打印到屏幕上。
-
点击 编译(Compile) 和 保存(Save)。
完成的蓝图

最终结果
你现在可以测试生命值组件的功能。
你可以使用WASD键找到你的角色。当按下数字键1时,你的角色将受到伤害,直到其生命值为零,此时角色将从世界上消失。
- 找到 工具栏(Toolbar),点击 运行(Play)。


项目设置
在本教程中,你的任务是为名为HealthComponent的Actor组件创建Gameplay逻辑。将此组件附加到Actor后,你的Actor将包含从其生命值中扣除伤害的功能。
我们将在示例中使用第三人称模板项目中包含的第三人称角色。
- 首先,点击 新建(New) > 游戏(Games)> 第三人称(Third Person)> CPP,创建名为 Health Comp 的项目。
Click for full view.
- 找到 编辑(Edit) > 项目设置(Project Settings) >引擎(Engine) > 输入(Input) >绑定(Bindings) > 操作映射(Action Mappings),然后点击 添加(Add)(+),创建名为 OnDamagePressed 的新操作映射,并将值设置为 1。
Click for full view.
输入将包含操作映射,可用于将按钮或按键映射到事件驱动型行为,例如对玩家角色造成伤害。
创建Actor组件:生命值组件
委托能够以泛型类型安全的方式调用C++对象上的成员函数。当Actor将自己绑定到委托时,它将响应该委托的成员函数事件。
按照以下步骤开始创建On Health Changed委托。
- 点击 添加/导入(Add/Import) 可以创建一个 新的C++类(New C++ Class),然后从 选择父类(Choose a Parent Class) 菜单中,选择 Actor组件(Actor Component),创建名为 HealthComponent 的新Actor组件。
Click for full view.
-
在HealthComponent.h文件中,声明以下委托:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComp, float, Health, float, DamageAmount, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
-
找到 类默认值(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);
-
找到HealthComponent.cpp文件并在 HealthComponent Constructor 中初始化以下类变量:
UHealthComponent::UHealthComponent() { MaxHealth = 100.0f; }
-
为 BeginPlay 类方法实现以下定义:
void UHealthComponent::BeginPlay { Super::BeginPlay(); //获取此Actor组件的所有者。 AActor* MyOwner = GetOwner(); if (MyOwner) { //所有者对象现在一定会响应OnTakeAnyDamage函数。 MyOwner->OnTakeDamage.AddDynamic(this,&UHealthComponent::HandleTakeDamage); } //将生命值设置为最大生命值。 Health = MaxHealth; }
-
为 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); }
-
编译 你的代码。
已完成代码
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操作映射,以便对玩家造成伤害。
-
在 内容浏览器(Content Browser) 中找到 C++类(C++ Classes) > HealthComp,双击 HealthComp Character 打开
HealthCompCharacter.h
文件。 -
为 类默认值 声明以下代码:
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();
-
找到HealthCompCharacter.cpp文件并添加以下类库:
#include "HealthComponent.h" #include "Kismet/GameplayStatics.h"
Gameplay静态库包括许多实用辅助函数,可以满足你的Gameplay目标需求。在本例中,我们将使用ApplyDamage方法。
-
在 AHealthCompCharacter Constructor 中,初始化你的生命值组件类:
AHealthCompCharacter::AHealthCompCharacter() { HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"); }
-
找到 AHealthCompChatacter::BeginPlay 方法,并添加以下代码:
void AHealthCompCharacter::BeginPlay() { Super::BeginPlay(); HealthComponent->OnHealthChanged.AddDynamic(this, &AHealthCompCharacter::OnHealthChanged); }
-
在 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是包含不同冗长度类型的宏,可与命令结合使用,将数据可视化到输出日志。
-
添加以下代码,实现 AHealthCompCharacter::DamagePlayerCharacter 方法:
void AHealthCompCharacter::DamagePlayerCharacter() { UGameplayStatics::ApplyDamage(this, 20.0f, GetInstigatorController(),this,GenericDamageType); }
-
找到 AHealthCompCharacter::SetupPlayerInputComponent 方法,将 OnDamagePressed 绑定分配到DamagePlayerCharacter函数:
void AHealthCompCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { // 绑定OnDamagePressed操作事件 PlayerInputComponent->BindAction("OnDamagePressed", IE_Pressed, this, &AHealthCompCharacter::DamagePlayerCharacter); }
-
编译 你的代码。
已完成代码
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时,你的角色将受到伤害,直到其生命值为零,此时角色将从世界上消失。
- 找到 工具栏(Toolbar),按 运行(Play)。

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