虚幻基础:攻击 与 受击 之间的联动
能帮到你的话,就给个赞吧 😘
文章目录
- 联动方式:组件通过合理的交互方式共同完成复杂功能
- 直接调用:一个组件直接持有另一个组件的引用,通过直接调用实现联动:耦合度较高,组件直接绑定另一个组件,组件间不再独立
- 回调: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秒无敌
}