假设我们在Unity的环境中。
假设我们有PlayerA对象和PlayerB对象。玩家A要造成伤害(即降低玩家B的生命值)。
我正在设计一个交互系统,该系统将允许修改如果PlayerA表示要对PlayerB造成X伤害的意图,并且可以进行修改而无需修改PlayerA或PlayerB的代码。
简而言之,一旦播放器效果叠加,我不希望DealDamage函数具有几十个if(hasArmorEffect),if(isImmuneEffect),if(hasDamageReflectionEffect)检查是否将检查所有这些东西。理想情况下,系统将以程序员可以编写Armor组件的方式工作,将其挂接到某个位置以表示他对DealDamage计算感兴趣并将其附加到GameObject,而无需修改A和B代码。
答案 0 :(得分:1)
我会用修饰符列表解决这个问题。假设您具有以下接口:
public interface IHealth
{
float Health {get};
void TakeDamage(float damage);
}
public interface IDamageModifier
{
float Apply(float damage);
}
现在让我们进行一些修改器实现:
public class ShieldModifier : ScriptableObject, IDamageModifier
{
private float shieldAmount = 10;
public float Apply(float damage)
{
var actualDamage = Mathf.Max(0, damage - this.shieldAmount)
return actualDamage;
}
}
public class InvulnerabilityModifier: ScriptableObject, IDamageModifier
{
public float Apply(float damage)
{
return 0;
}
}
最后,您的敌人将拥有一系列修饰语:
public class Enemy : MonoBehaviour, IHealth
{
public float Health {get; private set;}
public List<IDamageModifier> modifiers; // Pretend this has both modifiers above.
public void TakeDamage(float damage)
{
var actualDamage = damage;
foreach(var mod in this.modifiers)
{
actualDamage = mod.Apply(actualDamage);
}
this.Health -= actualDamage; // 0 because of Invulnerability
}
}
因此,如果您的敌人的盾牌修改器的盾牌值为10,那么攻击50伤害只会造成40伤害。
如果您希望修饰符可以通过检查器进行配置,则它们可以是可编写脚本的对象。
这种方法很基本。对于ReflectDamageModifier
这样的效果,您的修改器界面可能需要以IHealth attacker
为例:
public class ReflectDamageModifier: IDamageModifier
{
private float reflectionFactor = 0.5; // Reflect 50% of damage
public float Apply(float damage, IHealth attacker)
{
attacker.TakeDamage(damage * this.reflectionFactor);
return damage;
}
}
尽管如此,该方法容易受到修饰符的影响。例如,如果您将反射修改器放在第一位,则其反射的损害将比放在最后的反射更多。例如,您可能需要使用优先级/订购系统来扩展系统。
如果攻击者和目标都具有反射修饰符,则此反射修饰符也容易受到stackoverflow异常的影响,因此请当心。
希望这会有所帮助。