You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

RPG游戏效果系统设计问题排查与临时效果批量移除方案咨询

解决RPG效果系统的临时效果统一更新问题

首先,你的方案无法运行的核心原因是C#泛型集合的不变性HashSet<IResistanceBuff>不能直接赋值给HashSet<IEffect>——HashSet是不变泛型类型,只有像IEnumerable这样的只读接口才支持协变。所以你在Character.AllEffects里试图将不同类型的HashSet放进List<HashSet<IEffect>>的代码会直接编译失败,这是C#类型系统的硬性限制,不是你的逻辑问题。

下面是两个符合C#规范和OO设计原则的优化方案,你可以根据自己的架构偏好选择:

方案1:保留分类存储,统一枚举临时效果

这个方案保留你原来按效果类型分类存储的设计(方便在TakeDamage等方法里直接调用对应效果集合),同时解决临时效果的统一更新问题:

重构IEffectStorage接口

public interface IEffectStorage
{
    // 枚举所有临时效果
    IEnumerable<ITemporaryEffect> GetAllTemporaryEffects();
    // 按类型移除效果(因为效果存放在不同的HashSet中)
    void RemoveEffect<T>(T effect) where T : IEffect;
}

实现Character类的接口方法

public class Character : IEffectStorage
{
    public HashSet<IResistanceBuff> ResistanceBuffs { get; private set; } = new();
    public HashSet<IDamageBuff> DamageBuffs { get; private set; } = new();
    // 其他效果集合(比如眩晕、减速等)...

    public IEnumerable<ITemporaryEffect> GetAllTemporaryEffects()
    {
        // 从各个效果集合中筛选出临时效果并合并
        return ResistanceBuffs.OfType<ITemporaryEffect>()
            .Concat(DamageBuffs.OfType<ITemporaryEffect>())
            // 追加其他效果集合的筛选结果
            .ToList();
    }

    public void RemoveEffect<T>(T effect) where T : IEffect
    {
        // 根据效果类型找到对应的集合并移除
        switch (effect)
        {
            case IResistanceBuff resistanceBuff:
                ResistanceBuffs.Remove(resistanceBuff);
                break;
            case IDamageBuff damageBuff:
                DamageBuffs.Remove(damageBuff);
                break;
            // 其他效果类型的case...
        }
    }

    // 原有的TakeDamage方法保持不变
    void TakeDamage(Damage damage)
    {
        foreach (var resistanceBuff in ResistanceBuffs)
        {
            resistanceBuff.modifyIncomingDamage(damage);
        }
        this.HP -= damage.Amount;
        if (this.HP <= 0)
        {
            this.Faint();
        }
    }
}

更新临时效果过期逻辑

public static class EffectManager
{
    public static void ExpireTemporaryEffects(IEffectStorage storage)
    {
        // 先转成List,避免枚举集合时修改集合导致的异常
        var tempEffects = storage.GetAllTemporaryEffects().ToList();
        foreach (var tempEffect in tempEffects)
        {
            tempEffect.TimeRemaining--;
            if (tempEffect.TimeRemaining <= 0)
            {
                storage.RemoveEffect(tempEffect);
                // 可选:添加过期回调逻辑,比如触发效果的过期事件
                (tempEffect as IHasExpireCallback)?.OnExpire(storage);
            }
        }
    }
}

方案2:统一效果存储,通过类型筛选调用

如果不想维护多个分散的效果集合,可以改用一个统一的HashSet<IEffect>存储所有效果,需要特定类型效果时用OfType<T>筛选,架构更简洁:

简化IEffectStorage接口

public interface IEffectStorage
{
    HashSet<IEffect> AllEffects { get; }
}

实现Character类

public class Character : IEffectStorage
{
    public HashSet<IEffect> AllEffects { get; private set; } = new();

    // 辅助方法:简化添加特定类型效果的操作
    public void AddResistanceBuff(IResistanceBuff buff) => AllEffects.Add(buff);
    public void AddDamageBuff(IDamageBuff buff) => AllEffects.Add(buff);

    // 修改TakeDamage方法,通过类型筛选获取抗性buff
    void TakeDamage(Damage damage)
    {
        foreach (var resistanceBuff in AllEffects.OfType<IResistanceBuff>())
        {
            resistanceBuff.modifyIncomingDamage(damage);
        }
        this.HP -= damage.Amount;
        if (this.HP <= 0)
        {
            this.Faint();
        }
    }
}

简化临时效果过期逻辑

public static class EffectManager
{
    public static void ExpireTemporaryEffects(IEffectStorage storage)
    {
        // 筛选出即将过期的效果,先转成List避免枚举时修改集合
        var expiredEffects = storage.AllEffects.OfType<ITemporaryEffect>()
            .Where(e => --e.TimeRemaining <= 0)
            .ToList();
        
        foreach (var effect in expiredEffects)
        {
            storage.AllEffects.Remove(effect);
            // 可选:触发过期回调
            effect.OnExpire(storage);
        }
    }
}

额外的OO优化建议

  • 把静态类Effect改名为EffectManager,更符合“管理效果生命周期”的语义。
  • ITemporaryEffect添加OnExpire(IEffectStorage target)方法,让效果可以自定义过期逻辑(比如移除buff时恢复玩家的原始属性)。
  • 尽量封装效果集合的操作(比如用AddEffect<T>代替直接操作HashSet),避免外部代码直接修改内部集合,提升封装性。

内容的提问来源于stack exchange,提问作者user4385532

火山引擎 最新活动