简单谈谈战斗系统中属性计算

英雄属性 单位状态(State) 单位状态(晕眩/冰冻/沉默/魅惑/嘲讽/石化/禁锢/隐身/无敌等),只需要一个bool标记,多种状态可以并存,因此我们用一个64位整数来存储,每个bit代表一种状态,最多可以有64种状态,足够游戏中战斗用了。 public enum UnitStateType { Stunned = 0; // 晕眩 Sleeping = 1; // 沉睡 Fronzen = 2; // 冰冻 Chaos = 3; // 混乱 Silence = 4; // 沉默 Disarm = 5; // 缴械 Taunted = 6; // 嘲讽 Stone = 7; // 石化 Rooted = 8; // 禁锢 Invisible = 9; // 隐藏 Invincible = 10; // 无敌 // 更多状态定义在下面,最多大值63 } public class UnitStates { public UInt64 Data { get; private set; } public void AddState(UnitStateType type) { Data |= GetStateBits(type); } public void RemoveState(UnitStateType type) { Data &= ~GetStateBits(type); } public bool HasState(UnitStateType type) => (Data & GetStateBits(type)) != 0; public UInt64 GetStateBits(UnitStateType type) => (1 << (int)type); } 单位属性(Attribute) 为了让战斗属性更加灵活,我们并不在一个类里定义所有属性,而是用表格的方式存储所有属性列表,方便以后增加新的属性。我们使用定点数来确保战斗计算结果的确定性,所以数值存储使用FixedNumber类。 public enum UnitAttributeType { Health, MaxHealth, PhysicalDamage, MagicalDamage, PhysicalArmor, MagicalArmor, CriticalChance, CriticalDamage, MoveSpeed, AttackSpeed, AttackRange, // 更多属性添加下面,数字递增 MaxCount } public class UnitAttributes { public List<FixedNumber> Data { get; private set; } public UnitAttributes() { Data = new List<FixedNumber>(); for (int i = 0; i < UnitAttributeType.MaxCount; i++) { Data.Add(0); } } public void SetAttribute(UnitAttributeType type, FixedNumber amount) { Data[(int)type].Set(amount); } public void AddAttribute(UnitAttributeType type, FixedNumber amount) { Data[(int)type].Add(amount); } } 战斗属性计算 属性的改变一般有两种:绝对值加量,百分比加量。我们的游戏中还有一种特殊的技能效果:属性拷贝,即一个英雄偷取另外一个英雄属性。由于属性计算过程比较复杂,被各种Buff/Debuff影响,之前我做的一个战斗系统中,偶尔会出现策划反应数值不大对劲,但又没有精确的证据证明数值有不错错误。 为了解决这个问题,新的战斗属性计算我采用这样的设计:所有属性的改变都单独存储,每帧都重新计算最终属性。 ...

May 11, 2023 · 2 min · Jee