想象一下你正在创建一款游戏,游戏中涉及对玩家、敌人或物体施加某种类型的伤害。在这种情况下,你很可能遇到这样的情形:你需要确定这些物体是否被击中,如果是,是什么击中了它们,击中点在哪里,或者有关检测到的攻击的其他信息。 OnHit事件(OnHit Event) 会在发生碰撞时提供此信息,然后你可以利用数据来推动游戏中的变化,无论是要影响生命值、摧毁物体,还是导致其他Gameplay相关操作。 在本教程中,你将使用 OnComponentHit 和 Function 事件,将伤害施加到Actor,这一效果将通过更改Actor的网格体材质呈现。事件还将在击中位置施加推力推动Actor,模拟被发射物击中的效果,并在击中位置施加作用力。
项目设置
- 首先,点击 游戏(Games) > 第一人称(First Person) > 蓝图(Blueprint) ,创建名为 OnHit 的项目。

创建网格体材质
-
找到 内容浏览器(Content Browser) ,然后找到 LevelPrototyping/Materials 文件夹。
-
选择 MI_SolidBlue ,然后复制( CTRL+ D ),并将新复制资产重命名为 MI_Solid_Red 。
-
双击打开资产,然后选择并编辑 基础颜色(Base Color) ,设为红色。
-
保存 该资产。
创建立方体Actor
-
找到 内容浏览器(Content Browser) ,然后找到 LevelPrototyping/Meshes 文件夹。
-
右键点击 SM_ChamferCube静态网格体(SM_ChamferCube Static Mesh) ,选择 资产操作(Asset Actions) > 使用此……创建蓝图(Create Blueprint Using This..) 。
-
将蓝图命名为 BP_Cube 。
务必为新蓝图选择文件夹保存位置。
-
在 细节(Details) 面板中,将 物理(Physics) > 模拟物理效果(Simulate Physics) 设置为 True 。
-
在 细节(Details) 面板中,点击 事件(Events) > 击中组件时(On Component Hit) 。
-
这将添加新节点并打开 事件图表(Event Graph) 。
-
左键点击 并拖出 其他Actor(Other Actor) 引脚,然后搜索并添加 Cast To FirstPersonProjectile 节点。
此处你将转换到名为 FirstPersonProjectile 的另一蓝图,确保当你击中BP_Cube时,击中它的是FirstPersonProjectile蓝图。如果是这样,你可以应用其他脚本,更改网格体的材质,表示它已承受伤害。如果不是,你不用进行任何操作。
-
拖出 击中(Hit) 引脚,并添加 Break Hit Result 节点。

- 从 Break Hit Result 拖出 击中Actor(Hit Actor) 引脚,并添加 Apply Point Damage 节点。

- 在 Apply Point Damage 节点上,将 基础伤害(Base Damage) 设置为 100 ,并将 伤害类型类(Damage Type Class) 设置为 伤害类型(Damage Type) 。

Apply Point Damage节点将指定伤害量和承受伤害的位置。
- 在 Apply Point Damage 节点上,连接其余的线。
- 将 作为蓝图第一人称发射物(As BP First Person Projectile) 引脚连接至 伤害施加者(Damage Causer) 引脚。
- 将 碰撞点(Impact point) 引脚连接至 击中方向(Hit from Direction) 引脚。
- 将 击中(Hit) 引脚连接至 击中信息(Hit Info) 引脚。
- 编译(Compile) 并 保存(Save) 。
实现伤害功能
现在你创建好了功能,可在发射物与立方体发生碰撞时对立方体造成伤害,你需要实现一个函数,该函数用于设置当立方网格体受到伤害时的材质,然后在延迟之后,将网格体重置回原始材质。
- 找到 我的蓝图(My Blueprint) > 函数(Functions) ,并点击 添加(+)(Add ( + )),创建名为 OnTakeDamage 新函数。

-
从 组件(Components) 选项卡中,点击 StaticMesh组件(StaticMesh Component) 并将其拖到事件图表。
-
点击并拖出静态网格体(Static Mesh)引脚,然后从操作菜单中,选择 设置材质(Set Material) ,然后将 材质(Material) 参数更改为你早前创建的 MI_Solid_Red 材质资产。

-
编译(Compile) 并 保存(Save) 。
-
找到 我的蓝图(My Blueprint) > 函数(Functions) ,并点击 添加(+)(Add ( + )),创建名为 Damage Reset 新函数。

-
从 组件(Components) 选项卡中,点击 StaticMesh组件(StaticMesh Component) 并将其拖到事件图表。
-
点击并拖出 静态网格体(Static Mesh) 引脚,然后从操作菜单中,选择 设置材质(Set Material) ,然后将 材质(Material) 参数更改为 MI_Solid_Blue 材质资产。

- 点击并拖出 Apply Point Damage 节点的执行引脚,然后搜索并选择 On Take Damage 函数。

- 点击并拖出 On Take Damage 函数的执行引脚,然后搜索并选择 延迟(Delay) 。

- 点击并拖出 Delay 节点的 已完成(Completed) 执行引脚,然后搜索并选择 伤害重置(Damage Reset) 。
- 编译(Compile) 并 保存(Save) 。
完成的蓝图
设置关卡
- 在 内容浏览器(Content Browser) 中,将 BP_Cube 拖放到关卡中。

- 找到 大纲视图(Outliner) > 模拟立方体(Simulated Cubes) ,选择全部 SM_ChauferCubes ,然后右键点击并选择 替换所选Actor(Replace Selected Actors with) > BP_Cube 。

- 点击 播放(Play) 在编辑器中播放,点击鼠标左键在立方体上发射发射物。
(w:600)(convert:false)
- 当你在编辑器中播放时,你会发现当用你发射的发射物击中立方体时,会导致立方体受到伤害并改变其网格体材质,并在立方体被击中的位置施加推力,使其向发射物相反的方向移动。
施加的作用力的大小在 FirstPersonProjectile 蓝图中定义,该蓝图通过 Event Hit 节点确定发射物实际击中某物的时间。
- 在 Content/FirstPersonBP/Blueprints 文件夹中,打开 FirstPersonProjectile 蓝图。
本蓝图中的脚本会检查击中对象是否在模拟物理效果(你在立方体蓝图中将其设置为true。)如果是,则会在击中位置施加推力(作用力的大小在绿框中定义)。调整此值可以增加或减少击中时施加的推力大小。
项目设置
-
首先,点击 游戏(Games) > 第一人称(First Person) > C++ ,创建名为 OnHit 的新项目。
创建网格体材质
-
找到 内容浏览器(Content Browser) ,然后找到 LevelPrototyping/Materials 文件夹。
-
选择 MI_SolidBlue ,然后复制( CTRL+ D ),并将新复制资产重命名为 MI_Solid_Red 。
-
双击打开资产,然后选择并编辑 基础颜色(Base Color) ,设为红色。
-
保存 该资产。
创建立方体Actor
-
在 编辑器(Editor) 中,点击 添加(+)(Add(+)) > 新C++类(New C++ Class) ,然后选择 Actor 作为父类,并将你的类命名为 立方体(Cube) 。
-
在你的
cube.h
文件中声明以下类默认值UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) class UStaticMeshComponent* CubeMesh; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UMaterialInstance* CubeMaterial; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UMaterialInstance* DamagedCubeMaterial; FTimerHandle DamageTimer; UFUNCTION() void OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
-
然后,在你的
cube.cpp
文件中,声明以下类库。#include "Kismet/GameplayStatics.h" #include "OnHitProjectile.h"
-
找到立方体构造函数并实现以下功能。
ACube::ACube() { CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh")); DamagedCubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("DamageMaterial")); CubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("CubeMaterial")); CubeMesh->SetSimulatePhysics(true); }
实现伤害功能
现在你创建好了立方体,你需要实现一个函数,该函数用于设置当网格体受到伤害时的材质,然后在延迟之后,将网格体重置回原始材质。
-
在你的
cube.h
文件中声明以下代码。void OnTakeDamage(); void ResetDamage();
-
找到
cube.cpp
文件,为ACube::BeginPlay
函数实现以下代码。void ACube::BeginPlay() { CubeMesh->OnComponentHit.AddDynamic(this, &ACube::OnComponentHit); }
-
实现
ACube::OnTakeDamage
函数。void ACube::OnTakeDamage() { CubeMesh->SetMaterial(0, DamagedCubeMaterial); GetWorld()->GetTimerManager().SetTimer(DamageTimer, this, &ACube::ResetDamage, 1.5f, false); }
-
然后,实现
ACube::ResetDamage
函数。void ACube::ResetDamage() { CubeMesh->SetMaterial(0,CubeMaterial); }
-
最后,找到
ACube::OnComponentHit
函数并实现以下代码。void ACube::OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { if (AOnHitProjectile* HitActor = Cast<AOnHitProjectile>(OtherActor)) { UGameplayStatics::ApplyDamage(this, 20.0f, nullptr, OtherActor, UDamageType::StaticClass()); OnTakeDamage(); } }
-
编译你的代码。
-
在编辑器中,找到 C++类(C++ Classes)> 立方体(Cube) ,然后,右键点击 立方体Actor(Cube Actor) 并选择 基于立方体创建蓝图类(Create Blueprint class based on Cube) 。
-
在组件(Components)选项卡中,选择 立方体网格体(Cube Mesh) ,然后找到 细节(Details) > 静态网格体(Static Mesh) 并选择 SM_ChamferCube 资产。
-
在 BP_Cube 的类默认值中,将 立方体材质(Cube Material) 设置为 MI_Solid_Blue 资产,并将 受伤害立方体材质(Damaged Cube Material) 设置为 MI_Solid_Red 资产。
-
编译(Compile) 并 保存(Save) 。
CubeActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Cube.generated.h"
UCLASS()
class ONHIT_API ACube : public AActor
{
GENERATED_BODY()
public:
// 为此Actor的属性设置默认值
ACube();
protected:
// 当游戏开始或重生(Spawn)时调用
virtual void BeginPlay() override;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
class UStaticMeshComponent* CubeMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UMaterialInstance* CubeMaterial;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UMaterialInstance* DamagedCubeMaterial;
FTimerHandle DamageTimer;
void OnTakeDamage();
void ResetDamage();
UFUNCTION()
void OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
public:
// 每一帧都调用
virtual void Tick(float DeltaTime) override;
};
CubeActor.cpp
#include "Cube.h"
#include "Kismet/GameplayStatics.h"
#include "OnHitProjectile.h"
// 设置默认值
ACube::ACube()
{
// 将此Actor设置为每帧调用更新函数()。 如果不需要此特性,可以关闭以提升性能。
PrimaryActorTick.bCanEverTick = true;
CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));
DamagedCubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("DamageMaterial"));
CubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("CubeMaterial"));
CubeMesh->SetSimulatePhysics(true);
}
// 当游戏开始或重生(Spawn)时调用
void ACube::BeginPlay()
{
Super::BeginPlay();
CubeMesh->OnComponentHit.AddDynamic(this, &ACube::OnComponentHit);
}
void ACube::OnTakeDamage()
{
CubeMesh->SetMaterial(0, DamagedCubeMaterial);
GetWorld()->GetTimerManager().SetTimer(DamageTimer, this, &ACube::ResetDamage, 1.5f, false);
}
void ACube::ResetDamage()
{
CubeMesh->SetMaterial(0,CubeMaterial);
}
void ACube::OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
if (AOnHitProjectile* HitActor = Cast<AOnHitProjectile>(OtherActor))
{
UGameplayStatics::ApplyDamage(this, 20.0f, nullptr, OtherActor, UDamageType::StaticClass());
OnTakeDamage();
}
}
// 每一帧都调用
void ACube::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
设置关卡
-
在 内容浏览器(Content Browser) 中,将 BP_Cube 拖放到关卡中。
-
找到 大纲视图(Outliner) > 模拟立方体(Simulated Cubes) ,选择全部 SM_ChauferCubes ,然后右键点击并选择 替换所选Actor(Replace Selected Actors with) > BP_Cube 。
-
点击 播放(Play) 在编辑器中播放,点击鼠标左键在立方体上发射发射物。
-
当你在编辑器中播放时,你会发现当你用发射的发射物击中立方体时,会导致立方体受到伤害并改变其网格体材质,并在立方体被击中的位置施加推力,使其向发射物相反的方向移动。
(w:600)(convert:false)
-
施加的作用力的大小在
OnHitProjectile.cpp
文件中定义,该文件通过 OnHit 函数确定发射物实际击中某物的时间。void AOnHitCPPProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { // 仅当我们击中物理对象时,才增加推力并摧毁发射物 if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics()) { OtherComp->AddImpulseAtLocation(GetVelocity() * 100.0f, GetActorLocation()); Destroy(); } }