前置文章:《游戏事件、效果模式设计》
大多数游戏中,伤害不仅仅是一个数值。MOBA 游戏中,存在物理伤害、法术伤害、真实伤害等;月圆之夜中,存在物理伤害、各种元素伤害、穿透伤害等。如果要实现上述功能,代码如下即可。
namespace DamageDesign
{
public enum DamageType { Physics, Spell, Real, }
public class Damage
{
public DamageType Type { get; set; }
public int Value { get; set; }
}
}
但伤害不仅存在类别差异,还可能有其他差异。
参考一个效果 “当受到伤害时,再次受到 1 点真实伤害”。如果不区分 “伤害” 的概念,这个效果则不可能存在,只能改成 “受到的伤害增加 1 点”。但这种情况下,如果存在 “元素免疫” 类似的效果,且原来受到的伤害为 “元素伤害” ,那么这个附加的 1 点伤害也无法造成。
有一种方案可以解决这个问题,添加属性 “附加伤害” 。“附加伤害” 不会触发 “伤害” 扳机,也就理所当然不会造成上面的死循环,同时也不会成为伤害计数器的目标。在这个动机下,我们可以考虑在伤害类里面集成 “附加伤害”。
namespace DamageDesign
{
public enum DamageType { Physics, Spell, Real, }
public class Damage
{
public DamageType Type { get; set; }
public int Value { get; set; }
public List<Damage> Attach { get; }
}
}
附加伤害不单独触发事件,其只在 “伤害过滤器” 层级、生命值扣除层级进行结算。(一般来说,事件不应该关注附加伤害)
应用效果的设计模式一,伤害结算逻辑应该如下:
namespace DesignMode1_Damage_Ver
{
public enum DamageType { Physics, Spell, Real, }
public class Damage
{
public DamageType Type { get; set; }
public int Value { get; set; }
public List<Damage> Attach { get; }
public Damage(DamageType type, int value)
{
Type = type;
Value = value;
Attach = new List<Damage>();
}
}
public class BattleArgs
{
public Battle Battle { get; set; }
public Character Raiser { get; set; } // Who Raise the Event.
}
public class DamageArgs : BattleArgs
{
public IDealDamage Source { get; set; }
public IGetDamage Target { get; set; }
public Damage Damage { get; set; }
}
public interface IGetDamageAction
{
bool HasGetDamage(DamageArgs args);
void GetDamage(DamageArgs args);
}
public interface IGetDamageFilter
{
bool HasGetDamageFilter(DamageArgs damage);
void GetDamageFilter(Damage damage);
}
public abstract class Buff
{
public abstract string Name { get; }
public abstract string Description { get; }
public abstract string Abstract { get; }
}
public class Character : IDealDamage, IGetDamage
{
public Battle Battle { get; }
public List<Buff> Buffs { get; }
public void GetDamage(IDealDamage source, Damage damage)
{
DamageArgs args = new DamageArgs()
{
Battle = Battle,
Raiser = this,
Source = source,
Target = this,
Damage = damage,
};
foreach (Buff b in Buffs)
if (b is IGetDamageFilter filter && filter.HasGetDamageFilter(args))
filter.GetDamageFilter(damage);
OnGetDamage(args);
// Modify HP Here.
}
public void OnGetDamage(DamageArgs args)
{
foreach (Buff b in Buffs)
if (b is IGetDamageAction ac && ac.HasGetDamage())
ac.GetDamage(args);
}
}
}