这种RPG游戏效果系统的设计有什么问题,或者我如何从不同的派生效果列表中删除所有特定效果?



我想我遇到了一个设计问题......我不知道如何解决自己。很抱歉,但我必须提供一些基本的设计信息...

一个游戏。玩家、怪物、物体、不同事物可能受到不同影响:

public interface IEffect {}

可能会有不同的特定效果:

public interface IResistanceBuff : IEffect
{
void modifyIncomingDamage(Damage damage);
}
public interface IDamageBuff : IEffect
{
void modifyOutgoingDamage(Damage damage);
}

为什么这样?好吧,这个概念是为了避免重复代码。例如,通过这种方式,我可以统一类型抗性的伤害减少,例如,穿着盔甲。简单地说,我使Type类和Armor类实现IResistanceBuff。(类似地,Weapon类将实现IDamageBuff(。

但有些影响是暂时的,不是永久性的。为此,我们将有:

public interface ITemporaryEffect : IEffect
{
long TimeRemaining {get; set;}
}

同样,这里的目的是避免重复代码。例如,PotionOfMight类将实现IDamageBuff接口并从抽象类派生Potion抽象类,而抽象类又将实现ITemporaryEffect接口。或者,我们将拥有IStun接口,它将实现ITemporaryEffect.

现在,每个可以处于特定效果下的对象都将存储它所承受的所有效果的集合(也许HashSet?这是为了能够更容易地调用这些效果。例如,一个字符将具有属性public HashSet<IResistanceBuff> ResistanceBuffs {get; private set;}因此我们可以使字符的TakeDamage方法如下所示:

void TakeDamage(Damage damage)
{
this.ResistanceBuffs.ForEach(resistanceBuff =>
resistanceBuff.modifyIncomingDamage(damage)
);
this.HP -= damage.Amount;
if(this.HP <= 0) {
this.Faint();
}
}

但问题来了。我想有一段代码来更新临时效果的剩余持续时间并删除持续时间已过期的效果。

我的想法(通过 C# 的设计不起作用(是使Character类(以及表示可以在任何类型的Effect下的对象的任何其他类 - 这意味着甚至WorldMap,因为我认为我会存储Effects应用于世界的天气条件(实现IEffectStorage接口:

public interface IEffectStorage
{
List<HashSet<IEffect>> AllEffects {get;}
}
public class Character : IEffectStorage
{
// ...
public HashSet<IResistanceBuff> ResistanceBuffs {get; private set;}
public HashSet<IDamageBuff> DamageBuffs {get; private set;}
// ...
public List<HashSet<IEffect>> AllEffects =>
new List<HashSet<IEffect>> {ResistanceBuffs, DamageBuffs, /* ... */};
}

现在处理临时效果过期将是Effect静态类的责任:

public static class Effect
{
public static void ExpireTemporaryEffects(IEffectStorage effectStorage)
{
effectStorage.AllEffects.ForEach(effectSet =>
{
var effects = effectSet.ToList();
effects.ForEach(effect =>
{
if(effect is ITemporaryEffect) {
effect.TimeRemaining--;
if(effect.TimeRemaining <= 0) {
effectSet.Remove(effect);
}
}
});
});
}
}

但是,当然,正如我所说,我不能这样做。

现在我可能会开始寻找一些黑客和/或丑陋的方法来使这个设计工作。另外,我可能会认为我可能会试图做一些反对 C# 编程语言和/或 OO 编程范式设计的事情,因为我没有很好地理解这种范式......特别是因为这是我自己做的第一个这种规模的项目,我选择做后者......所以我问这个问题:)我的设计都是错的吗,如果是,那么如何解决它?

很抱歉这篇文章的长度,但我觉得上面的所有信息都是必要的。

我不是游戏开发人员,也没有游戏引擎的经验,但我认为大多数游戏或引擎都使用帧之类的东西来衡量时间(也许我完全错了(。我的想法是将您的 ITemporaryEffect 更改为

public interface ITemporaryEffect : IEffect
{
bool IsActive(long currentFrame)
}

并按如下方式实现:

public class SilencedEffect : ITemporaryEffect 
{
private long duration = 1000;
private long endFrame;
public SilencedEffect(int currentFrame)
{
endFrame = currentFrame + duration;
}
public bool IsActive(long currentFrame)
{
return endFrame > currentFrame;
}
}

通过这种方式,您可以在角色或世界地图类中迭代它,如果不再处于活动状态,则将其删除。

public class Character
{
private IList<ITemporaryEffect> temporaryEffect;         
public void UpdateEffects(long currentFrame)
{
var outdatedTempEffects = temporaryEffect.Where(p => !p.IsActive(currentFrame));
temporaryEffects.RemoveRange(outdatedTempEffects);
}
}