首先基于ActorComponent创建一个组件 HealthComponent,将需要的变量与函数创建

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h" UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PVETPC_API UHealthComponent : public UActorComponent
{
GENERATED_BODY() public:
// Sets default values for this component's properties
UHealthComponent();
// 初始化健康值
UFUNCTION(BlueprintCallable)
void Init(int taotalHealth,int currentHealth);
// 造成伤害
UFUNCTION(BlueprintCallable)
void HanldTakeAnyDamaged(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
// 恢复健康值
UFUNCTION(BlueprintCallable)
void RestoreHealth(int restoreValue);
UFUNCTION(BlueprintPure)
float GetHealth() { return CurrentHealth; }
protected:
// 总健康值
float TotalHealth;
// 当前健康值
float CurrentHealth; // Called when the game starts
virtual void BeginPlay() override; public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

这里的 HanldTakeAnyDamaged 函数是通过代理绑定到拥有者身上

HanldTakeAnyDamaged 需要的形参需要与 OnTakeAnyDamage 的宏定义一致

除此之外还有OnTakePointDamageOnTakeRadialDamage 也是一样的操作

#include "Components/HealthComponent.h"
#include "Engine.h"
#include "Kismet/KismetSystemLibrary.h"
// Sets default values for this component's properties
UHealthComponent::UHealthComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true; // ...
} void UHealthComponent::Init(int taotalHealth, int currentHealth)
{
TotalHealth = taotalHealth;
CurrentHealth = currentHealth;
} void UHealthComponent::HanldTakeAnyDamaged(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Damage <= 0) { return; }
CurrentHealth = FMath::Clamp( CurrentHealth - Damage , 0.f, TotalHealth);
UE_LOG(LogTemp, Warning, TEXT("I am Demaged! CurrentHealth = %f"), CurrentHealth);
} void UHealthComponent::RestoreHealth(int restoreValue)
{
CurrentHealth = FMath::Clamp(CurrentHealth + restoreValue, 0.f, TotalHealth);
GEngine->AddOnScreenDebugMessage(-1, 20, FColor::Red, FString(TEXT("I am RestoreHealth!")));
} // Called when the game starts
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();
// 获取拥有者
AActor* MyOwner = GetOwner();
// 如果存在就将伤害接收函数绑定
if (MyOwner)
{
UE_LOG(LogTemp, Warning, TEXT("I am bound!"));
MyOwner->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::HanldTakeAnyDamaged);
}
Init(100,100);
// ... } // Called every frame
void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // ...
}

这时候我们将该组件挂载在角色身上,已经有了效果,但是角色不知道组件生命值是否改变

接着我们在组件头文件的头文件申明下添加代理的宏定义,并创建一个代理对象

并在需要响应的函数中添加广播

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h" // 自定义六参数代理事件
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
......
// 恢复健康值
UFUNCTION(BlueprintCallable)
void RestoreHealth(int restoreValue);
UFUNCTION(BlueprintPure)
float GetHealth() { return CurrentHealth; }
// 定义代理
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnHealthChangedSignature OnHealthChanged; ......
void UHealthComponent::HanldTakeAnyDamaged(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Damage <= 0) { return; }
CurrentHealth = FMath::Clamp( CurrentHealth - Damage , 0.f, TotalHealth);
UE_LOG(LogTemp, Warning, TEXT("I am Demaged! CurrentHealth = %f"), CurrentHealth);
// 每当该函数被调用时,就将调用一次代理函数
OnHealthChanged.Broadcast(this, CurrentHealth, Damage, DamageType, InstigatedBy, DamageCauser);
}

最后再到拥有者类中添加一个用于回调的操作函数,其中形参对应在生命组件中定义的那样(注意命名是否重复)

头文件

    // 代理事件
UFUNCTION()
void OnHealthChanged(UHealthComponent* OnwerHealthComp, float Health, float HealthDelta,
const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);

cpp文件

void APCharacter::OnHealthChanged(UHealthComponent* OnwerHealthComp, float Health, float HealthDelta, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (IsDeath) return;
UE_LOG(LogTemp, Warning, TEXT("I know I was hurt! "));
if (Health <= 0 && !IsDeath)
{
UE_LOG(LogTemp, Warning, TEXT("I am Death! "));
IsDeath = true;
     Death();
GetMovementComponent()->StopMovementImmediately();
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision); // 分离控制器
DetachFromControllerPendingDestroy();
// 3秒后执行
SetLifeSpan(3.0f);
}
}
void APCharacter::BeginPlay()
{
Super::BeginPlay();
HealthComp->OnHealthChanged.AddDynamic(this, &APCharacter::OnHealthChanged); }

最后测试,结果无误

UEC 利用代理/委托写一个生命组件的更多相关文章

  1. Vue3语法快速入门以及写一个倒计时组件

    Vue3写一个倒计时组件 vue3 beta版本发布已有一段时间了,文档也大概看了一下,不过对于学一门技术,最好的方法还是实战,于是找了一个比较简单的组件用vue3来实现,参考的是vant的count ...

  2. 写一个vue组件

    写一个vue组件 我下面写的是以.vue结尾的单文件组件的写法,是基于webpack构建的项目.如果还不知道怎么用webpack构建一个vue的工程的,可以移步到vue-cli. 一个完整的vue组件 ...

  3. js单行写一个评级组件

    单行写一个评级组件:"★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate); -----------------------------------分隔符- ...

  4. 表单配置项写法,表单写成JSON数组套对象,一行是一个数组单位,一列是一个对象单位,然后再写一个公共组件读取这个配置,循环加载slot,外层载入slot的自定义部分,比如input select等,这种写法就是把组件嵌套改为配置方式

    表单配置项写法,表单写成JSON数组套对象,一行是一个数组单位,一列是一个对象单位,然后再写一个公共组件读取这个配置,循环加载slot,外层载入slot的自定义部分,比如input select等,这 ...

  5. [翻译]怎么写一个React组件库(一)

    本文同步发布于知乎专栏 https://zhuanlan.zhihu.com/p/27401329,喜欢本文的就去知乎点个赞支持下吧- 引言 该系列文章将通过创建一个组件库来引导你学习如何构建自己的组 ...

  6. 【阿菜做实践】利用go语言写一个简单的Pow样例

    本篇博客的主要内容是用go写一个简单的Proof-of-Work共识机制,不涉及到网络通信环节,只是一个本地的简单demo.开发IDE用的是JB Golang. 整个项目的文件结构如下: PoWdem ...

  7. HTML+CSS+JS(+Vue)写一个通讯录组件

    求各位大大的Star(*/ω\*). 没有录屏,所以上传的是图片.后面已补充录屏效果. 效果:(主要是参考小米Note3的通讯录的效果做的) 主要功能: 1. 滚动后,通讯录的模块标题会固定在顶部(图 ...

  8. 在React中写一个Animation组件,为组件进入和离开加上动画/过度

    问题 在单页面应用中,我们经常需要给路由的切换或者元素的挂载和卸载加上过渡效果,为这么一个小功能引入第三方框架,实在有点小纠结.不如自己封装. 思路 原理 以进入时opacity: 0 --> ...

  9. Angular写一个Form组件-TagInput

    前端开发少不了和表单打交道; Angular中, 提供了强大的表单的支持, 响应式表单(Reactive Form) 和 模板驱动的表单(Template-driven Form) 的双向数据流给我们 ...

随机推荐

  1. Sirni题解(最小生成树,埃氏筛)(继 Liang-梁)

    目录 前言 题意 思路 一些建议 前言 本篇是对Liang-梁的Sirni(最小生成树,埃氏筛)的后继博客. 通篇原文:https://blog.csdn.net/qq_37555704/articl ...

  2. 【Java】学习路径57-TCP协议客户端与服务器端的关闭

    在TCP协议中,如果发送端(客户端)关闭了,那么接收端(服务器端)端就会收到这个消息. 那么接收端(服务器端)怎么知道的呢? 我们进行实验: 首先在发送端中编写一段程序,当用户输入"end& ...

  3. 【Java】学习路径30-可变参数 Variable Parameter

    定义一个add函数,要求其功能:传入任意数量的参数然后返回相加的结果. public class VariableParameter { public static void main(String[ ...

  4. 057_末晨曦Vue技术_处理边界情况之强制更新和创建低开销的静态组件

    强制更新和创建低开销的静态组件 点击打开视频讲解更加详细 强制更新 如果你发现你自己需要在 Vue 中做一次强制更新,99.9% 的情况,是你在某个地方做错了事. 你可能还没有留意到数组或对象的变更检 ...

  5. B2. Wonderful Coloring - 2

    链接:Problem - 1551B2 - Codeforces 题意:有m个颜色,要求每种颜色内的数字各不相同,问,颜色的最大长度多少. 题解:  判断每个数字的个数,如果大于m,那么最大长度就加一 ...

  6. 安卓平台RTMP推流或轻量级RTSP服务(摄像头或同屏)编码前数据接入类型总结

    很多开发者在做Android平台RTMP推流或轻量级RTSP服务(摄像头或同屏)时,总感觉接口不够用,以大牛直播SDK为例 (Github) 我们来总结下,我们常规需要支持的编码前音视频数据有哪些类型 ...

  7. 理解virt、res、shr之间的关系(linux系统篇)

    前言 想必在linux上写过程序的同学都有分析进程占用多少内存的经历,或者被问到这样的问题--你的程序在运行时占用了多少内存(物理内存)? 通常我们可以通过top命令查看进程占用了多少内存.这里我们可 ...

  8. CSS之垂直水平居中的背后

    最开始,我想说,这个体系有点大,我写的并不好.就当作是一个思路吧,虽然这个思路有点乱.几乎每一个实现方案的背后都是该属性及其组合的原理,每一个都要剖析其规范细节的话,这篇文章绝不会是这样的篇幅,所以每 ...

  9. Python数据科学手册-机器学习:朴素贝叶斯分类

    朴素贝叶斯模型 朴素贝叶斯模型是一组非常简单快速的分类方法,通常适用于维度非常高的数据集.因为运行速度快,可调参数少.是一个快速粗糙的分类基本方案. naive Bayes classifiers 贝 ...

  10. Kubernetes 存储系统 Storage 介绍:PV,PVC,SC

    要求:先了解数据docker容器中数据卷的挂载等知识 参考网址: https://www.cnblogs.com/sanduzxcvbnm/p/13176938.html https://www.cn ...