Проектирование взаимодействий между объектами, изменяемых без изменения кода объектов - PullRequest
0 голосов
/ 08 марта 2019

Предположим, что мы находимся в среде Unity.

Предположим, у нас есть объект PlayerA и объект PlayerB.PlayerA хочет нанести урон (т.е. уменьшить переменную здоровья PlayerB).

Я планирую разработать систему взаимодействия, которая позволит изменить то, что происходит, если PlayerA сообщает о своем намерении нанести X урона PlayerB, иэто позволит выполнить модификацию без изменения кода PlayerA или PlayerB.

Короче говоря, когда количество эффектов игрока накапливается, я не хочу, чтобы функция DealDamage имела десятки if (hasArmorEffect), if (isImmuneEffect), если (hasDamageReflectionEffect) проверяет, будет ли проверяться весь этот материал.В идеале система должна работать таким образом, чтобы программист мог написать компонент Armor, подключить его куда-нибудь, чтобы сигнализировать о своем интересе к вычислениям DealDamage, и присоединить его к GameObject без изменения кода A и B.

1 Ответ

1 голос
/ 08 марта 2019

Я бы решил это с помощью списка модификаторов. Предположим, у вас есть интерфейсы:

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;
    }
}

Однако этот подход уязвим к порядку модификаторов. Например, если вы сначала поместите модификатор отражения, он будет отражать больше урона, чем последний. Вам может потребоваться расширить систему, например, системой приоритетов / заказов.

Этот модификатор отражения также уязвим для исключения переполнения стека, если и у атакующего, и у цели есть модификатор отражения, так что просто следите за этим.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...