主题
Buff 系统
Buff 加属性后看起来无变化时,先按完整链路排查检查启用、叠层、已应用修改及最终属性。Modifications 自动维护应用与撤销,不需要普遍改为手动 AddFixed;增加最大生命不等于回血。
Buff 系统管理单位上的临时效果(增益/减益),包括持续时间、层数叠加、周期效果、属性修改和状态变更。
核心类型
| 类型 | 说明 |
|---|---|
GameDataBuff | Buff 数编配置(静态数据) |
Buff | Buff 运行时实例 |
BuffManager | 管理单位上所有 Buff(继承自 ObjectManager<Buff, GameDataBuff>) |
GameDataEffectBuffAdd | 效果树中添加 Buff 的效果节点 |
GameDataEffectBuffRemove | 效果树中移除 Buff 的效果节点 |
GameDataBuff 配置
csharp
public partial class GameDataBuff
{
// 显示信息
public LocalizedString? DisplayName { get; set; }
public LocalizedString? Description { get; set; }
public Icon? Icon { get; set; }
// 同步和分类
public SyncType SyncType { get; set; } = SyncType.Sight;
public BuffPolarity Polarity { get; set; } // Neutral / Positive / Negative
public List<BuffCategory> Categories { get; set; } // CanBeDisabled, CanBeDispelled(可扩展)
public BuffFlags BuffFlags { get; set; }
// 叠加配置
public uint StackStart { get; set; } = 1; // 初始层数
public uint? InstanceMax { get; set; } // 最大实例数
public FuncUIntEffect? StackMax { get; set; } // 最大层数
// 时间配置
public FuncTimeEffect? Duration { get; set; } // 持续时间(null 为永久)
public BuffDurationSyncMode DurationSyncMode { get; set; } = BuffDurationSyncMode.Owner;
public FuncTimeEffect? Period { get; set; } // 周期效果间隔
// 生命周期效果
public IGameLink<GameDataEffect>? InitialEffect { get; set; } // 首次附加时执行
public IGameLink<GameDataEffect>? PeriodicEffect { get; set; } // 每个周期执行
public IGameLink<GameDataEffect>? RefreshEffect { get; set; } // 同施法者重复施加时执行
public IGameLink<GameDataEffect>? ExpireEffect { get; set; } // 自然到期时执行
public IGameLink<GameDataEffect>? FinalEffect { get; set; } // 被移除时执行(任何原因)
// 属性修改和状态变更
public List<UnitPropertyModification> Modifications { get; set; }
public List<UnitState> AddStates { get; set; } // 附加时添加的状态
public List<UnitState> RemoveStates { get; set; } // 附加时移除的状态
public List<UnitState> ImmuneStates { get; set; } // 附加期间免疫的状态
// 验证器
public ValidatorEffect? EnableValidator { get; set; } // 控制 Buff 启用/禁用
public ValidatorEffect? PersistValidator { get; set; } // 验证失败时销毁 Buff
// 响应(Behavior 系统)
public List<IGameLink<GameDataResponse>?>? Responses { get; set; }
// 启用/禁用其他 Buff 和技能
public List<IGameLink<GameDataBuff>> BuffsEnable { get; set; }
public List<IGameLink<GameDataBuff>> BuffsDisable { get; set; }
public List<BuffCategory> BuffCategoriesEnable { get; set; }
public List<BuffCategory> BuffCategoriesDisable { get; set; }
public List<IGameLink<GameDataAbility>> AbilitiesEnable { get; set; }
public List<IGameLink<GameDataAbility>> AbilitiesDisable { get; set; }
public List<AbilityCategory> AbilityCategoriesEnable { get; set; }
public List<AbilityCategory> AbilityCategoriesDisable { get; set; }
// 表现层
public List<IGameLink<GameDataActor>?>? ActorArray { get; set; } // Actor 模型/特效
public BuffUISettings UISettings { get; set; } // UI 显示配置
public DebugDrawSettings? DebugDrawSettings { get; set; }
}持续时间同步方式
GameDataBuff.DurationSyncMode 同时控制运行时 Buff.Duration 和 Buff.RemainingTime 的同步范围。默认值为 Owner,未填写该字段的 Buff 也采用此默认值。
| 配置值 | 数据编辑器选项 | 行为 |
|---|---|---|
BuffDurationSyncMode.None | 不同步 | 服务端照常计时,所有客户端均不接收计时信息 |
BuffDurationSyncMode.Owner | 仅同步给Buff持有者 | 仅所挂单位的所属玩家接收计时信息,且不超出 Buff 本体的同步范围 |
BuffDurationSyncMode.Inherit | 继承Buff同步方式 | 计时信息跟随 GameDataBuff.SyncType;例如 Sight 会向视野内玩家公开倒计时 |
在 PvP 游戏中,可使用 SyncType.Sight 配合默认的 Owner:敌方玩家能看到 Buff 及其表现,但读不到它的持续时间和剩余秒数。需要公开倒计时的 Buff 再显式选择 Inherit。这里的“持有者”指受 Buff 影响的单位所属玩家,与施法者无关。
客户端没有收到计时信息时,运行时 Duration 和 RemainingTime 返回 null。此时 UI 应隐藏倒计时,不应根据数编配置或首次看见 Buff 的时间推算剩余时间;null 也不能用来判断 Buff 是否永久。设置持续时间同步方式不影响服务端到期、暂停和续时逻辑。
BuffFlags
控制 Buff 的行为标志,只有 4 个字段:
csharp
public class BuffFlags
{
// 同一施法者对同一目标只能有一个实例。
// 重复施加时合并层数并刷新持续时间。
public bool SingleInstancePerCaster { get; set; }
// 永久效果:不会因变身、死亡等条件消失。
// 注意:这不是"持续时间无限"的意思。
public bool Permanent { get; set; }
// 单位死亡时是否禁用此 Buff(默认 true)。
// 设为 false 可让 Buff 在死亡后继续生效(需配合 Permanent = true 使用)。
public bool DisableWhenDead { get; set; } = true;
// 引导效果:施法者引导被打断时,此 Buff 也会被移除。
public bool Channeling { get; set; }
}BuffPolarity
csharp
public enum BuffPolarity
{
Neutral, // 中性
Positive, // 增益
Negative // 减益
}BuffCategory
可扩展枚举,内置两个值:
csharp
BuffCategory.CanBeDisabled // 可被禁用
BuffCategory.CanBeDispelled // 可被驱散游戏开发者可通过 [EnumExtension(Extends = typeof(EBuffCategory))] 自定义分类。
BuffUISettings
csharp
public class BuffUISettings
{
public bool ShowStackCount { get; set; } = true;
public UIDurationFormat DurationFormat { get; set; } = UIDurationFormat.CircularProgress;
public bool CombineInstances { get; set; } = true; // 合并同类实例为一个 UI 条目
public TimeSpan? BlinkThreshold { get; set; } = TimeSpan.FromSeconds(2); // 即将过期时闪烁
}
public enum UIDurationFormat { None, Numeric, CircularProgress }Buff 运行时实例
属性
| 属性 | 类型 | 可用端 | 说明 |
|---|---|---|---|
Link | IGameLink<GameDataBuff> | 双端 | 数编 Link |
Cache | GameDataBuff | 双端 | 数编缓存(Link.Data 快捷访问) |
Stack | uint | 服务端读写,客户端只读 | 当前层数 |
StackMax | uint? | 仅服务端 | 最大层数 |
Duration | TimeSpan? | 双端 | 总持续时间 |
RemainingTime | TimeSpan? | 双端 | 剩余时间 |
Manager | BuffManager | 双端 | 所属管理器 |
StackParam | Effect | 仅服务端 | 创建此 Buff 的 EffectBuffAdd 效果 |
IsLinkEnabled | bool | 双端 | 当前 Link 是否启用 |
DisableOnDeath | bool | 双端 | 死亡时是否禁用 |
RemoveOnDeath | bool | 双端 | 死亡时是否移除 |
服务端方法
csharp
uint AddStack(int stack); // 增减层数,返回新层数
uint SetStack(uint stack); // 设置层数
bool SetRemainingTime(TimeSpan? newRemainingTime); // 设置剩余时间(null 移除计时器)
void Pause(bool on); // 暂停/恢复生命周期和周期计时器
void Disable(); // 禁用 Buff(AddState(Disabled))
void Destroy(); // 销毁 Buff(触发 FinalEffect)通过 IExecutionContext 访问上下文
服务端的 Buff 实现了 IExecutionContext,可访问创建时的施法者、目标等信息:
csharp
#if SERVER
var caster = ((IExecutionContext)buff).Caster; // 施法者
var ability = ((IExecutionContext)buff).Ability; // 关联技能
var item = ((IExecutionContext)buff).Item; // 关联物品
#endifBuffManager
通过 entity.GetComponent<BuffManager>() 获取,通过 entity.GetOrCreateComponent<BuffManager>() 获取或创建。
继承自 ObjectManager 的方法
csharp
Buff? Get(IGameLink<GameDataBuff> link); // 获取第一个匹配的 Buff
HashSet<Buff> GetAll(); // 获取所有 Buff(复制集合)
ReadOnlySet<Buff>? GetAll(IGameLink<GameDataBuff> link); // 获取同一 Link 的所有实例
IEnumerable<Buff> Get(Func<Buff, bool> condition); // 按条件筛选
Buff? GetOne(Func<Buff, bool> condition); // 按条件查找一个
IEnumerable<Buff> YieldAll(); // 惰性遍历所有 Buff
bool IsEnabled(IGameLink<GameDataBuff> link); // 检查 Link 是否启用BuffManager 自有方法
csharp
// 查询
bool HasBuff(IGameLink<GameDataBuff> buffLink);
bool HasAnyBuff(params IGameLink<GameDataBuff>[] buffLinks);
bool HasAllBuffs(params IGameLink<GameDataBuff>[] buffLinks);
bool HasState(UnitState state);
IEnumerable<UnitState> GetCurrentStates();
// 按极性查询
IEnumerable<Buff> GetBuffsByPolarity(BuffPolarity polarity);
IEnumerable<Buff> GetPositiveBuffs();
IEnumerable<Buff> GetNegativeBuffs();
IEnumerable<Buff> GetDispellableBuffs();
// 统计
int CountBuffs(Func<Buff, bool> predicate);
int CountBuffsByPolarity(BuffPolarity polarity);
int CountPositiveBuffs();
int CountNegativeBuffs();
// 移除和驱散
int RemoveByPredicate(Func<Buff, bool> predicate); // 内部调用 Destroy()
int DispelBuffs(BuffPolarity polarity, bool onlyDispellable = true);
int DispelPositiveBuffs(bool onlyDispellable = true);
int DispelNegativeBuffs(bool onlyDispellable = true);
int ClearAllDispellableBuffs();> 注意:BuffManager.Add() 和 BuffManager.Remove() 是 internal 方法,不可直接调用。要添加 Buff 请使用便利方法或效果树,要移除 Buff 请调用 buff.Destroy() 或使用 RemoveByPredicate。
事件
| 事件 | 说明 |
|---|---|
OnLinkActivated | 某个 Link 从无激活实例变为有激活实例 |
OnLinkDeactivated | 某个 Link 从有激活实例变为无激活实例 |
OnObjectAttached | 任何 Buff 实例被附加到管理器 |
OnObjectRefresh | 任何 Buff 实例被刷新(包括合并时) |
此外,Buff 系统通过事件发布器发布以下事件(可通过 EventAggregator 订阅):
| 事件类型 | 触发时机 |
|---|---|
EventBuffAttached | Buff 附加到单位时 |
EventBuffRemoved | Buff 从单位移除时(在 FinalEffect 之前) |
EventBuffPropertyChange | Buff 属性变化时(如层数、持续时间) |
添加 Buff
方式一:效果树(推荐)
在技能或效果树中使用 GameDataEffectBuffAdd,保持完整的效果树上下文:
csharp
var buffAddEffect = new GameDataEffectBuffAdd(myLink)
{
Buff = myBuffLink, // 要添加的 Buff 类型
Stack = new FuncUIntConstant(2), // 可选:层数覆盖
Duration = new FuncTimeConstant(TimeSpan.FromSeconds(10)) // 可选:持续时间覆盖
};方式二:便利方法(TriggerEncapsulation)
在没有效果树环境的脚本中直接添加 Buff:
csharp
#if SERVER
// Unit 扩展方法(最常用)
Buff? buff = unit.AddBuff(myBuffLink, caster);
// 带参数
Buff? buff = unit.AddBuff(myBuffLink, caster, stack: 3, duration: TimeSpan.FromSeconds(30));
// 静态方法
Buff? buff = BuffUtility.AddBuff(target, myBuffLink, caster);
// 在执行上下文中添加(保持效果树父子关系)
Buff? buff = BuffUtility.AddBuff(executionContext, myBuffLink, target);
// 或扩展方法:
Buff? buff = executionContext.AddBuff(myBuffLink, target);
#endif> 注意:便利方法内部创建虚拟的 EffectBuffAdd 来记录施法者,但会丢失完整的效果树信息。在技能效果树中应使用 GameDataEffectBuffAdd。
BuffManager 扩展方法
csharp
#if SERVER
var buffManager = unit.GetOrCreateComponent<BuffManager>();
// 单个添加
Buff? buff = buffManager.AddBuff(myBuffLink, caster, stack: 2);
// 批量添加
List<Buff> buffs = buffManager.AddBuffs(new[] { buffLink1, buffLink2 }, caster);
// 添加或刷新(已存在则更新剩余时间)
Buff? buff = buffManager.AddOrRefreshBuff(myBuffLink, caster, duration: TimeSpan.FromSeconds(30));
#endif移除 Buff
csharp
#if SERVER
// 直接销毁 Buff 实例
buff.Destroy();
// 通过 BuffManager 按条件移除
var buffManager = unit.GetComponent<BuffManager>();
if (buffManager != null)
{
// 移除所有减益
buffManager.DispelNegativeBuffs();
// 按条件移除
int removed = buffManager.RemoveByPredicate(b => b.Cache.Polarity == BuffPolarity.Negative);
// 清除所有可驱散 Buff
buffManager.ClearAllDispellableBuffs();
}
#endif效果树中可使用 GameDataEffectBuffRemove:
csharp
var removeEffect = new GameDataEffectBuffRemove(myLink)
{
Buff = targetBuffLink, // 指定类型(null 不限类型)
Stack = new FuncUIntConstant(1), // 移除层数(null 全部移除)
CategoryFilter = new HashSetFilter<BuffCategory> // 按分类筛选
{
BuffCategory.CanBeDispelled
},
BuffValidator = (buff) => // 自定义验证
{
return buff.Cache.Polarity == BuffPolarity.Negative ? CmdResult.Ok : CmdResult.Fail;
}
};Buff 生命周期
创建 Buff
│
├── OnAttached → 初始化响应、状态修改、启动计时器
│ │
│ └── OnLinkAttached → 执行 InitialEffect,应用启用/禁用配置
│
├── 周期触发 → Period 计时器到期 → 执行 PeriodicEffect → 重新启动计时器
│
├── 刷新(同施法者重复施加 + SingleInstancePerCaster)
│ └── 合并层数 + 延长剩余时间 → 执行 RefreshEffect
│
├── 验证器检查(每帧 Think)
│ ├── PersistValidator 失败 → Destroy()
│ └── EnableValidator 切换 → Disable() / Enable()
│
├── 自然到期 → Duration 计时器到期 → 执行 ExpireEffect → Destroy()
│
└── Destroy()
├── 发布 EventBuffRemoved
├── 执行 FinalEffect
├── 停止引导
└── 从 BuffManager 移除 → OnLinkDetached生命周期效果说明
| 效果字段 | 触发时机 | 典型用途 |
|---|---|---|
InitialEffect | Buff 首次附加时 | 播放添加特效、应用一次性效果 |
PeriodicEffect | 每个 Period 间隔 | 持续伤害、持续治疗 |
RefreshEffect | 同施法者重复施加(合并时) | 播放刷新特效 |
ExpireEffect | 持续时间自然到期 | 到期时的一次性效果 |
FinalEffect | Buff 被移除时(包括手动销毁、到期、驱散) | 清理效果、结束特效 |
叠加机制
SingleInstancePerCaster
当 BuffFlags.SingleInstancePerCaster = true 时,同一施法者对同一目标重复施加同一 Buff:
- 合并层数(新旧实例的
Stack相加,不超过StackMax) - 延长剩余时间(取新旧的较大值)
- 触发
RefreshEffect
不同施法者的 Buff 实例独立存在,受 InstanceMax 限制。
InstanceMax
当同一 Link 的 Buff 实例数超过 InstanceMax 时,最后一个实例被销毁。
层数操作
csharp
#if SERVER
buff.AddStack(2); // 增加 2 层
buff.AddStack(-1); // 减少 1 层(注意参数是 int,可以为负)
buff.SetStack(5); // 直接设置为 5 层
// 层数变化后自动刷新属性修改和状态
#endif属性修改
通过 Modifications 列表配置,Buff 激活时自动应用到单位属性系统:
csharp
var buff = new GameDataBuff(myLink)
{
Modifications = new List<UnitPropertyModification>
{
new()
{
Property = UnitProperty.AttackDamage,
Value = (_) => 10 // 固定增加 10 点攻击力
},
new()
{
Property = UnitProperty.MovementSpeed,
SubType = MyPropertySubType.Percentage, // 自定义子属性(需在属性公式中实现逻辑)
Value = (_) => 20 // 20%
}
}
};属性修改随 Buff 的启用/禁用/销毁自动生效和移除,无需手动管理。
状态管理
csharp
var stunBuff = new GameDataBuff(myLink)
{
Polarity = BuffPolarity.Negative,
Categories = [BuffCategory.CanBeDispelled],
Duration = new FuncTimeConstant(TimeSpan.FromSeconds(3)),
AddStates = [UnitState.Stunned, UnitState.Uncommandable]
};
var immuneBuff = new GameDataBuff(myLink)
{
Polarity = BuffPolarity.Positive,
Duration = new FuncTimeConstant(TimeSpan.FromSeconds(5)),
RemoveStates = [UnitState.Stunned, UnitState.Silenced], // 附加时移除这些状态
ImmuneStates = [UnitState.Stunned, UnitState.Silenced] // 附加期间免疫这些状态
};查询状态:
csharp
var buffManager = unit.GetComponent<BuffManager>();
if (buffManager != null)
{
bool isStunned = buffManager.HasState(UnitState.Stunned);
var allStates = buffManager.GetCurrentStates(); // 所有 Buff 添加的状态
}验证器
PersistValidator
每帧检查,验证失败时立即销毁 Buff:
csharp
PersistValidator = myValidator // ValidatorEffect 委托,返回 CmdResult
// 当 PersistValidator(StackParam) != CmdResult.Ok 时,Buff 被 Destroy()EnableValidator
每帧检查,控制 Buff 启用/禁用状态(不销毁):
csharp
EnableValidator = myValidator // ValidatorEffect 委托,返回 bool
// 返回 true → 启用 Buff(移除 Disabled 状态)
// 返回 false → 禁用 Buff(添加 Disabled 状态)禁用的 Buff 仍然存在且计时继续,但属性修改和状态变更不生效。
启用/禁用其他 Buff 和技能
GameDataBuff 可以在附加时启用或禁用其他 Buff 和技能:
csharp
var buff = new GameDataBuff(myLink)
{
// Buff 存在期间启用指定的 Buff 和技能
BuffsEnable = [otherBuffLink],
AbilitiesEnable = [abilityLink],
BuffCategoriesEnable = [myCategory],
// Buff 存在期间禁用指定的 Buff 和技能
BuffsDisable = [someBuffLink],
AbilitiesDisable = [someAbilityLink],
BuffCategoriesDisable = [someCategory],
AbilityCategoriesDisable = [someAbilityCategory]
};附加时应用配置,移除时自动反转。使用引用计数机制,多个 Buff 同时禁用同一目标时不会冲突。
引导 Buff
设置 BuffFlags.Channeling = true 使 Buff 成为引导效果。引导 Buff 与施法者的引导状态绑定——当施法者的引导被打断时,Buff 自动销毁。
csharp
var channeledBuff = new GameDataBuff(myLink)
{
BuffFlags = new BuffFlags { Channeling = true },
Duration = new FuncTimeConstant(TimeSpan.FromSeconds(8)),
Period = new FuncTimeConstant(TimeSpan.FromSeconds(1)),
PeriodicEffect = healEffectLink
};注意:引导 Buff 不会与 SingleInstancePerCaster 合并(源码中特别排除了 Channeling Buff 的合并逻辑)。
死亡处理
Permanent | DisableWhenDead | 行为 |
|---|---|---|
false | 任意 | 死亡时移除 Buff |
true | true(默认) | 死亡时禁用(暂停周期计时器),复活后恢复 |
true | false | 死亡后继续生效 |
客户端表现
Actor 表现
ActorArray 配置 Buff 关联的 Actor(模型/特效),由框架的 IActorScopeOwner 机制自动管理生命周期。
UI 组件
框架提供 BuffBar 和 BuffIcon 控件(在 GameSystemUI.BuffSystemUI.Advanced 命名空间):
csharp
#if CLIENT
// 创建 BuffBar 并绑定单位
var buffBar = BuffBar.Create(unit);
buffBar.Parent = myPanel;
// 或手动设置
var buffBar = BuffBar.Create();
buffBar.SetUnit(unit);
buffBar.Parent = myPanel;
// 单独的 BuffIcon
var icon = BuffIcon.Create(buff);
icon.Parent = myPanel;
#endifGameDataControlBuffBar 配置 BuffBar 的过滤和样式:
csharp
var barConfig = new GameDataControlBuffBar(myLink)
{
BuffWidth = 32,
BuffHeight = 32,
BuffMargin = 2,
BuffCategoryFilter = BuffCategoryFilter.All,
BuffPolarityFilter = BuffPolarityFilter.All
};BuffIcon 提供详细的显示控制:
csharp
#if CLIENT
var icon = BuffIcon.Create(buff);
icon.SetPolarity(BuffPolarity.Positive);
icon.SetIcon("path/to/icon");
icon.SetCooldown(remaining, total, UIDurationFormat.CircularProgress);
icon.SetStack(3);
icon.SetBlink(); // 即将过期时闪烁
#endifBuffDisplayData 封装了 Buff 的显示信息,可通过 BuffIcon.CreateBuffDisplayData(buff) 创建。
完整示例
定义 Buff 数编
csharp
// 攻击力增益
public static readonly GameLink<GameDataBuff, GameDataBuff> StrengthBuffLink = new("StrengthBuff");
static MyBuffs()
{
_ = new GameDataBuff(StrengthBuffLink)
{
DisplayName = new LocalizedString("力量增益"),
Polarity = BuffPolarity.Positive,
Categories = [BuffCategory.CanBeDispelled],
Duration = new FuncTimeConstant(TimeSpan.FromSeconds(30)),
StackStart = 1,
StackMax = new FuncUIntConstant(5),
BuffFlags = new BuffFlags { SingleInstancePerCaster = true },
Modifications =
[
new() { Property = UnitProperty.AttackDamage, Value = (_) => 10 }
]
};
}定义 DOT(持续伤害)Buff
csharp
public static readonly GameLink<GameDataBuff, GameDataBuff> PoisonBuffLink = new("PoisonBuff");
static MyBuffs()
{
_ = new GameDataBuff(PoisonBuffLink)
{
DisplayName = new LocalizedString("中毒"),
Polarity = BuffPolarity.Negative,
Categories = [BuffCategory.CanBeDispelled],
Duration = new FuncTimeConstant(TimeSpan.FromSeconds(12)),
Period = new FuncTimeConstant(TimeSpan.FromSeconds(3)),
PeriodicEffect = poisonDamageEffectLink,
StackStart = 1,
StackMax = new FuncUIntConstant(3)
};
}定义控制类 Buff
csharp
public static readonly GameLink<GameDataBuff, GameDataBuff> StunBuffLink = new("StunBuff");
static MyBuffs()
{
_ = new GameDataBuff(StunBuffLink)
{
DisplayName = new LocalizedString("眩晕"),
Polarity = BuffPolarity.Negative,
Categories = [BuffCategory.CanBeDispelled],
Duration = new FuncTimeConstant(TimeSpan.FromSeconds(3)),
AddStates = [UnitState.Stunned, UnitState.Uncommandable]
};
}添加和查询 Buff
csharp
#if SERVER
public static void OnGameTriggerInitialization()
{
Game.OnGameStart += OnGameStart;
}
private static async Task OnGameStart()
{
// 示例:给所有玩家英雄添加增益 Buff
foreach (var player in Player.AllPlayers)
{
var hero = player.Hero;
if (hero == null) continue;
var buff = hero.AddBuff(StrengthBuffLink);
if (buff != null)
{
Game.Logger.LogInformation("已为 {Player} 的英雄添加力量 Buff", player.Id);
}
}
// 等待一段时间后检查 Buff 状态
await Game.Delay(TimeSpan.FromSeconds(10));
foreach (var player in Player.AllPlayers)
{
var hero = player.Hero;
if (hero == null) continue;
var buffManager = hero.GetComponent<BuffManager>();
if (buffManager == null) continue;
// 检查是否有增益 Buff
if (buffManager.HasBuff(StrengthBuffLink))
{
var buff = buffManager.Get(StrengthBuffLink);
Game.Logger.LogInformation("力量 Buff 剩余时间: {Time}", buff?.RemainingTime);
}
// 统计
Game.Logger.LogInformation("增益数: {Count}, 减益数: {Debuffs}",
buffManager.CountPositiveBuffs(), buffManager.CountNegativeBuffs());
}
}
#endif驱散逻辑
csharp
#if SERVER
public static void DispelTarget(Unit caster, Unit target)
{
var buffManager = target.GetComponent<BuffManager>();
if (buffManager == null) return;
if (target.IsAllyWith(caster))
{
// 友方:驱散减益
int removed = buffManager.DispelNegativeBuffs();
Game.Logger.LogInformation("驱散了 {Count} 个减益", removed);
}
else
{
// 敌方:驱散增益
int removed = buffManager.DispelPositiveBuffs();
Game.Logger.LogInformation("驱散了 {Count} 个增益", removed);
}
}
#endif