当前位置: 首页 > news >正文

虚幻基础:攻击 与 受击 之间的联动


能帮到你的话,就给个赞吧 😘


文章目录

  • 联动方式:组件通过合理的交互方式共同完成复杂功能
    • 直接调用:一个组件直接持有另一个组件的引用,通过直接调用实现联动:耦合度较高,组件直接绑定另一个组件,组件间不再独立
    • 回调:It's just don't call me until I call you
      • A让B执行时把A的也执行
        • B.setTurnOnCallback(A.TurnOn):A让B开机时把A也开机
      • 实现:通过lambda直接将成员函数发送给其他组件执行
    • 事件驱动
    • 接口依赖
    • 消息队列
  • 联动:组件间通过联动逻辑完成任务
    • 组件的状态 联动 其他组件的行为
    • 组件的行为 受 其他组件的影响
  • 信号发送机制:接收信号并发送:组件间的交互方式
    • 事件驱动
    • 观察者
    • 回调
  • 组件A的状态变化 联动 组件B的行为
    • 1.信号发送:组件 A 发送信号
    • 2.行为响应:组件 B 接收信号后并执行预设行为
    • 示例:角色死亡时,UI&音效组件 执行预设行为
  • 组件A 的行为 受 组件B 的影响
    • 行为调整:组件 A 接收信号 并根据 信号内容 调整自身行为
    • 信号发送:组件 B 发送信号
    • 示例:攻击组件 要 根据 防御组件的 “无敌状态” 攻击。
  • 攻击&受击之间的联动
    • 攻击时不能受击
    • 受击时不能攻击
  • 联动设计
    • 1.避免双向联动:若 A 联动 B,B 联动 A,则导致组件状态无法预测
  • 参考
    • 编程 组件之间的联动

联动方式:组件通过合理的交互方式共同完成复杂功能

直接调用:一个组件直接持有另一个组件的引用,通过直接调用实现联动:耦合度较高,组件直接绑定另一个组件,组件间不再独立

// 角色组件
class CharacterComponent {
private:WeaponComponent* WeaponComp; // 直接持有武器组件引用public:// 初始化时绑定组件void Init(WeaponComponent* Weapon) {WeaponComp = Weapon;}// 联动:角色组件通知武器组件攻击void OnAttackInput() {if (WeaponComp) {WeaponComp->Fire(); // 直接调用武器组件方法}}
};// 武器组件
class WeaponComponent {
public:void Fire() {// 执行攻击逻辑}
};

回调:It’s just don’t call me until I call you

A让B执行时把A的也执行

B.setTurnOnCallback(A.TurnOn):A让B开机时把A也开机

实现:通过lambda直接将成员函数发送给其他组件执行

代码拆解
AttackComp->SetOnHitCallback(…)调用攻击组件(AttackComponent)提供的SetOnHitCallback方法,目的是向攻击组件 “注册一个回调函数”—— 告诉攻击组件:“当你检测到命中时,请执行我给你的这个函数”。
[this](float Damage) { … }这是一个 lambda 表达式(匿名函数),作为回调函数传递给攻击组件:
[this]:捕获当前DamageComponent的指针(this),确保在函数内部可以调用当前组件的方法(如TakeDamage)。
(float Damage):函数参数,接收攻击组件传递的伤害值。
函数体TakeDamage(Damage):当攻击命中时,实际执行的逻辑 —— 调用当前伤害组件的TakeDamage方法处理伤害。
作用与联动流程
这段代码建立了DamageComponent与AttackComponent之间的 “订阅 - 通知” 关系:
订阅阶段:DamageComponent在创建时,通过这段代码向AttackComponent“订阅” 了 “攻击命中事件”。
触发阶段:当AttackComponent检测到攻击命中(调用DetectHit方法)时,会自动执行这里注册的 lambda 函数。
响应阶段:最终调用DamageComponent的TakeDamage方法,完成从 “攻击命中检测” 到 “伤害处理” 的联动。
设计优势
解耦:DamageComponent不需要知道AttackComponent如何检测命中,AttackComponent也不需要知道DamageComponent如何处理伤害,两者通过回调函数间接通信。
灵活扩展:如果后续需要在命中时添加其他逻辑(如播放音效、显示特效),只需新增类似的回调注册代码,无需修改现有组件的核心逻辑。
这是游戏开发中组件协作的经典模式,尤其适合处理 “事件触发后需要多组件协同响应” 的场景。

构造函数参数AttackComponent* AttackComp表示伤害组件需要知道攻击组件的存在(通过指针关联),才能注册后续的回调关系。
注册回调函数
AttackComp->SetOnHitCallback(…):调用攻击组件提供的SetOnHitCallback方法,把一个 “命中处理逻辑” 注册进去。
这相当于DamageComponent(伤害组件)告诉攻击组件:“当你检测到攻击命中时,请执行我给你的这个函数”。
Lambda 表达式(匿名函数)[this](float Damage) { TakeDamage(Damage); } 是核心的响应逻辑:
[this]:捕获当前DamageComponent对象的指针,确保能在函数内部调用自己的TakeDamage方法。
(float Damage):声明参数,接收攻击组件传递过来的伤害值。
函数体TakeDamage(Damage):当攻击命中时,实际执行的操作(调用当前组件的伤害处理方法)。

// 攻击判定组件:负责检测攻击是否命中,并在命中时通知其他组件(如伤害组件)。
class AttackComponent {
private:// 存储伤害回调函数std::function<void(float)> OnHitCallback;public:// 允许外部注册回调void SetOnHitCallback(std::function<void(float)> Callback) {OnHitCallback = Callback;}// 检测到命中(状态变化)void DetectHit(float Damage) {if (OnHitCallback) {OnHitCallback(Damage); // 调用回调,通知伤害组件}}
};// 伤害组件:负责处理伤害逻辑,通过注册回调函数订阅攻击命中事件。
class DamageComponent {
public:DamageComponent(AttackComponent* AttackComp) {// 注册回调:调用攻击组件(AttackComponent)提供的SetOnHitCallback方法,目的是向攻击组件 “注册一个回调函数”—— 告诉攻击组件:“当你检测到命中时,请执行我给你的这个函数”。AttackComp->SetOnHitCallback([this](float Damage) {TakeDamage(Damage); // 响应行为:处理伤害});}void TakeDamage(float Damage) {// 伤害处理逻辑}
};

事件驱动

// 事件基类
class Event {
public:virtual ~Event() = default;
};// 受伤事件(携带数据)
class DamageEvent : public Event {
public:float Damage;DamageEvent(float Dmg) : Damage(Dmg) {}
};// 事件总线(管理事件发布与订阅)
class EventBus {
private:// 存储事件类型与回调函数的映射std::unordered_map<type_index, std::vector<std::function<void(Event*)>>> Listeners;public:// 订阅事件template <typename TEvent>void Subscribe(std::function<void(TEvent*)> Callback) {auto Type = type_index(typeid(TEvent));// 包装回调,转换为通用Event*参数Listeners[Type].push_back([Callback](Event* E) {Callback(static_cast<TEvent*>(E));});}// 发布事件template <typename TEvent>void Publish(TEvent* Event) {auto Type = type_index(typeid(TEvent));if (Listeners.count(Type)) {for (auto& Callback : Listeners[Type]) {Callback(Event); // 通知所有订阅者}}}
};

接口依赖

消息队列

联动:组件间通过联动逻辑完成任务

组件的状态 联动 其他组件的行为

组件的行为 受 其他组件的影响

信号发送机制:接收信号并发送:组件间的交互方式

事件驱动

观察者

回调

组件A的状态变化 联动 组件B的行为

1.信号发送:组件 A 发送信号

2.行为响应:组件 B 接收信号后并执行预设行为

示例:角色死亡时,UI&音效组件 执行预设行为

// 血量组件(状态变化源)
class HealthComponent {
private:float CurrentHealth;EventBus* EventBus; // 事件总线public:HealthComponent(EventBus* Bus) : EventBus(Bus), CurrentHealth(100.0f) {}void TakeDamage(float Damage) {CurrentHealth = FMath::Max(0.0f, CurrentHealth - Damage);// 血量变化时 发布信号EventBus->Publish(new HealthChangedEvent(CurrentHealth));// 血量为0时 发布信号if (CurrentHealth <= 0) {EventBus->Publish(new DeathEvent());}}
};// UI组件
class UIComponent {
public:UIComponent(EventBus* Bus) {// 接收血量变化信号Bus->Subscribe<HealthChangedEvent>([this](HealthChangedEvent* E) {UpdateHealthBar(E->NewHealth); // 执行预设行为:更新UI});}
};// 音效组件
class SoundComponent {
public:SoundComponent(EventBus* Bus) {// 接收死亡信号Bus->Subscribe<DeathEvent>([this](DeathEvent* E) {PlaySound("Death"); // 执行预设行为:播放音效});}
};

组件A 的行为 受 组件B 的影响

行为调整:组件 A 接收信号 并根据 信号内容 调整自身行为

信号发送:组件 B 发送信号

示例:攻击组件 要 根据 防御组件的 “无敌状态” 攻击。

// 攻击组件(接收无敌信号,根据信号内容调整攻击行为)
class AttackComponent {
private:EventBus* EventBus;
public:AttackComponent(EventBus* EventBus) : EventBus(EventBus) {// 接收无敌信号Bus->Subscribe<InvincibilityChangedEvent>([this](InvincibilityChangedEvent* E) {// 根据无敌信号的内容调整攻击行为if (E->bNewInvincibleState) {PlayHitEffect(false); // 播放无效攻击特效(未命中)PlaySound("HitBlocked.wav");}else (E->bNewInvincibleState) {Target->TakeDamage(20.0f, ...); // 造成伤害PlayHitEffect(true); // 播放有效攻击特效}     });}
};// 防御组件(发布无敌变化信号)
class DefenseComponent {
private:EventBus* EventBus;bool bIsInvincible = false;
public:DefenseComponent(EventBus* EventBus) : EventBus(EventBus) {}// 激活无敌状态并发布信号void ActivateInvincibility(float Duration) {bIsInvincible = true;// 发布无敌变更信号(通知所有订阅者)EventBus->Publish(new InvincibilityChangedEvent(bIsInvincible));// 定时取消无敌GetWorld()->GetTimerManager().SetTimer(InvincibilityTimer, [this]() {bIsInvincible = false;EventBus->Publish(new InvincibilityChangedEvent(bIsInvincible));},Duration, false);}
};// 示例
void Example() {// 创建事件总线EventBus* GlobalEventBus = new EventBus();// 创建防御组件DefenseComponent* DefenseComp = new DefenseComponent(GlobalEventBus);// 创建攻击组件AttackComponent* AttackComp = new AttackComponent(GlobalEventBus);// 触发无敌状态变化(自动通知攻击组件)DefenseComp->ActivateInvincibility(3.0f); // 3秒无敌
}

攻击&受击之间的联动

攻击时不能受击

受击时不能攻击

联动设计

1.避免双向联动:若 A 联动 B,B 联动 A,则导致组件状态无法预测

参考

编程 组件之间的联动

http://www.dtcms.com/a/449147.html

相关文章:

  • 如何在不降低画质的前提下缩小图片体积?附实操方案
  • 个人网站注册费用互联网广告价格
  • 【学习笔记02】C++面向对象编程核心技术详解
  • vite与ts的结合
  • arcgis如何将一部分shp地图截取下来并处理成networkx格式
  • .NET Aspire深度解析:重新定义云原生分布式应用开发的“秘密武器“
  • 标准件网站开发手机淘宝网页版
  • 【网络编程】揭秘 HTTPS 数据安全:加密方案与证书体系的协同防护
  • Windows Server 2022 安装教程(从 ISO 文件安装 Server STD CORE 2022 64位系统)​
  • 【STM32】墨水屏驱动开发
  • Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备故障预测与智能运维中的应用
  • 【MySQL】SQL的分类
  • Flutter GridView 使用指南
  • day86——有效的字母异位词(LeetCode-242)
  • 企业的网站建设费用重庆seo什么意思
  • 网站搭建介绍网站建设的原因
  • 怎么建免费网站建设公司网站新闻宣传管理制度
  • Deep Code Research:当 Deep Research 遇上 ABCoder
  • JavaEE初阶——中秋特辑:网络编程送祝福从 Socket 基础到 TCP/UDP 实战
  • 多模卫星导航定位与应用-原理与实践(RTKLib)3
  • 数字婵娟:一部关于中秋节的计算主义宣言
  • ED2K技术
  • 【数据结构】顺序表0基础知识讲解 + 实战演练
  • GPU即服务:Linux与云原生如何联手开启AI算力“自来水“时代
  • 【数据结构】算法复杂度
  • 校园网门户网站建设招聘网站如何做
  • 深度学习(十六):数据归一化处理
  • 力扣70.爬楼梯
  • 【深度学习计算机视觉】10:转置卷积
  • 电子商务网站策划素材网站 模板