Метод, который вычисляет примерное количество атак


Этот метод предполагает, что оба блока и целевого начала в том же месте, что и как будет работать непрерывно по прямой линии в одном направлении. Каждый раз, когда враг попадает в радиус атаки юнита, юнит будет атаковать, но он должен стоять на месте и выполнит его анимация атаки перед запуском или повторной атаки. Удар как только он завершает первый план качания даже если спинка качелей и время простоя не могли быть завершены из-за длительности окончание. Даже если противник имеет возможность выйти из блока дальность атаки, а блок делает его анимация атаки, атака будет по-прежнему хит и анимация по-прежнему будет завершена.

Есть все, что я могу сделать, чтобы сделать его более оптимизированным? Мне это нужно, чтобы иметь лучшее представление, он может получить.

    public static float EstimatedNumberOfAttacks(this Unit unit, Unit target, float duration, float overrideMoveSpeed = 0)
    {
        var immobileDuration = target.GetImmobileDuration();
        var attackAnimation = unit.ForeSwing + unit.BackSwing + unit.AttackIdleTime;

        //target is not able to move for whole duration
        if (!target.IsMoving() || immobileDuration >= duration)
        {
            return (float) Math.Floor(duration / attackAnimation);
        }

        var durationLeft = duration;
        float endDuration;

        var enemySpeed = target.MovementSpeed;
        var unitSpeed = overrideMoveSpeed <= 0 ? unit.MovementSpeed : overrideMoveSpeed;
        var speedDifference = unitSpeed - enemySpeed;

        var numberOfAttacksWhileImmobile = (float) Math.Ceiling(immobileDuration / attackAnimation);
        var immobileTimeElapsed = numberOfAttacksWhileImmobile * attackAnimation;
        var distanceTraveledAfterImmobile = (immobileTimeElapsed - immobileDuration) * enemySpeed;

        //duration ends in the middle of the attack animation of your last attack before immobility ends
        if ((endDuration = durationLeft - immobileTimeElapsed + attackAnimation) < 0)
        {
            while (endDuration < unit.ForeSwing)
            {
                numberOfAttacksWhileImmobile--;
                endDuration += attackAnimation;
            }

            return numberOfAttacksWhileImmobile;
        }

        durationLeft -= immobileTimeElapsed;

        var startNumberOfAttacks = 0f;
        var startTimeElapsed = 0f;
        float startCatchUpTime;

        var distanceTraveledDuringAttack = attackAnimation * enemySpeed;
        var unitAttackRange = unit.GetAttackRange();

        //enemy was not able to get out of attack range within the last attack animation before immobility ended
        if (unitAttackRange >= distanceTraveledAfterImmobile)
        {
            float startDistance;
            //enemy was not able to get out of attack range after a single attack
            if (distanceTraveledDuringAttack <= unitAttackRange - distanceTraveledAfterImmobile)
            {
                startNumberOfAttacks = (float) Math.Ceiling((unitAttackRange - distanceTraveledAfterImmobile) / distanceTraveledDuringAttack);
                startDistance = startNumberOfAttacks * distanceTraveledDuringAttack;
                startTimeElapsed = startDistance / enemySpeed;
            }
            else
            {
                startNumberOfAttacks = 1;
                startDistance = distanceTraveledDuringAttack + distanceTraveledAfterImmobile;
                startTimeElapsed = attackAnimation;
            }

            //duration ends in the middle of the attack animation of your last attack before enemy is able to get out of your attack range
            if ((endDuration = durationLeft - startTimeElapsed + attackAnimation) < 0)
            {
                while (endDuration < unit.ForeSwing)
                {
                    startNumberOfAttacks--;
                    endDuration += attackAnimation;
                }

                return numberOfAttacksWhileImmobile + startNumberOfAttacks;
            }

            startCatchUpTime = startDistance / speedDifference;
        }
        else
        {
            startCatchUpTime = distanceTraveledAfterImmobile / speedDifference;
        }

        //target is uncatchable
        if (speedDifference <= 0)
        {
            return numberOfAttacksWhileImmobile + startNumberOfAttacks;
        }

        durationLeft -= startTimeElapsed + startCatchUpTime;

        durationLeft -= attackAnimation;
        startNumberOfAttacks++;

        //see if last attack was able to hit
        if (durationLeft < 0)
        {
            if (durationLeft + attackAnimation > unit.ForeSwing)
            {
                startNumberOfAttacks--;
            }

            return numberOfAttacksWhileImmobile + startNumberOfAttacks;
        }

        //when both are now running its just a repeating pattern of catch up-attack-catch up-attack until duration ends
        var catchUpTime = distanceTraveledDuringAttack / speedDifference;
        var additionalAttacks = (float) Math.Floor(durationLeft / (catchUpTime + attackAnimation));
        var additionalAttackTimeTaken = (catchUpTime + attackAnimation) * additionalAttacks;

        durationLeft -= additionalAttackTimeTaken;

        durationLeft -= catchUpTime;

        //see if last attack was able to hit
        if (durationLeft > unit.ForeSwing) additionalAttacks++;

        return numberOfAttacksWhileImmobile + startNumberOfAttacks + additionalAttacks;
    }


162
5
задан 9 апреля 2018 в 01:04 Источник Поделиться
Комментарии
1 ответ

Расплывчатые наименования

var attackAnimation = unit.ForeSwing + unit.BackSwing + unit.AttackIdleTime

Если "время бездействия" не было в названии третье свойство; это было бы невозможно знать, что переменная выражает:

var attackAnimation = unit.ForeSwing + unit.BackSwing + unit.Attack;

Это количество? Строки? Эти фактические Animation объекты, которые вы сможете объединить?

Как AttackIdleTimeвы должны быть более ясным о другие названия: BackSwingDuration, ForeSwingDuration, attackAnimationDuration, AttackIdleDuration.

Обратите внимание, что я использую Duration вместо Time. Семантически, "время" является абсолютной величиной (13:00:00) в то время как "длительность" (а.к.а. в промежуток времени)- это продолжительность времени (13 часов).


Расплывчатые комментарии

//duration ends in the middle of the attack animation of your last attack before enemy is able to get out of your attack range 

//enemy was not able to get out of attack range within the last attack animation before immobility ended

//duration ends in the middle of the attack animation of your last attack before immobility ends

Я уверена, что ты знал, о чем вы говорите на момент написания комментария, но эти комментарии невероятно трудно точно расшифровать.
Я сомневаюсь, что вы поймете замечаний в течение года без необходимости повторного считывания кода; таким образом побеждая цель комментария в первую очередь.

Для меня, как для "нового застройщика", глядя на этот код, этот комментарий не более чем намек на то, что у вас есть отдельные логические алгоритмы. Точное различие между этими алгоритмами не все, что ясно.


Тело метода

Ваш код проверяет все возможные комбинации факторов в отдельности, а не объединить общей части логики.

Чтобы продемонстрировать проблему:

public void MyMethod(User user, bool userWinks, bool userTalks, bool userWalks)
{
if (userWinks)
{
if (userTalks)
{
if (userWalks)
{
user.Wink();
user.Talk();
user.Walk();
}
else
{
user.Wink();
user.Talk();
}
}
else
{
if (userWalks)
{
user.Wink();
user.Walk();
}
else
{
user.Wink();
}
}
}
else
{
//omitted for brevity's sake
}
}

Это по сути то, что ты делаешь. Вы пытаетесь обеспечить каждый взаимоисключающих варианта использования в одном методе тела. Это не есть хорошо. Нет никакой выгоды, имея взаимоисключающие логики сгруппированы вместе, поскольку они по своей сути никогда не будет полезным одновременно.

Вы должны отделять эти проверки, не помещая их. Исправить пример:

public void MyMethod(User user, bool userWinks, bool userTalks, bool userWalks)
{
if (userWinks)
{
user.Wink();
}

if (userTalks)
{
user.Talk();
}

if (userWalks)
{
user.Walk();
}
}

Примечание: поскольку вы используете return заявления, вы технически не вложенности (т. е. выделить) их, а полагаясь на неявный "этот код будет достигнута лишь в том случае, если мы не уже возвращает значение" подход. Пока это технически не гнездование, это все-таки БАД код по той же причине.


Расчет

Я думаю, что есть номер для улучшения здесь. Вы отдельной ситуации до точки, где они не пересекаются, хотя этот метод используется для расчета детерминированного поведения. Это странно.

Насколько я понимаю, действия злоумышленника являются логическим итерации:


  1. Бегите до тех пор, пока цель находится в радиусе атаки.

  2. Атаковать цель (foreswing)

  3. Опыт "атаки кулдаун" (замахе + время простоя)

  4. Повторить

Обратите внимание, что это логически не верно для каждого уникальный случай , которые вы перечислили:


  • //target is not able to move for whole duration если объект не двигается, Шаг 1 будет иметь длительность 0.

  • //target is uncatchable если цель движется быстрее, чем у атакующего, Шаг 1 никогда не будет завершена

  • //duration ends in the middle of the attack animation of your last attack before immobility ends мне понадобилось некоторое время, чтобы понять, что вы имеете в виду (не самый простой комментарий, если честно). Но это не исключительный случай, ограничивая себя до истечения заданного времени присуще алгоритму. Поскольку возвращаемое значение-это количество атак, достаточно выполнить эту проверку как раз перед тем, как увеличить количество.

  • //enemy was not able to get out of attack range after a single attack похожие на холостых цель, это просто означает, что Шаг 1 будет иметь длительность 0.

Посмотрите на простоту 3 шага. Они истинны для каждого случая, вне зависимости от:


  • Какой персонаж быстрее бегает

  • Стартовые позиции персонажей

  • Как долго это берет, чтобы выполнить нападение

  • Как долго настраивается длительность

Хотя это правда, что в некоторых случаях, некоторые действия не имеют никакого отношения (например, мишень неподвижна => злоумышленник не нужно догонять), это не неправильно, чтобы все-таки выполнить этот шаг (с ожидаемой продолжительностью 0).
Нет смысла предотвращая ненужный шаг, если шаг не негативно повлиять на результат.


Упрощение алгоритма

Это просто реализация peusodocode из 3 шагов, которые я перечислил. Обратите внимание, что я слежу за собой currentTimer, который работает от 0 до duration (при этом моделирование заканчивается)

1. Бегите до тех пор, пока цель находится в радиусе атаки.

Обратите внимание, что только важность этого метода , чтобы выяснить, как долго это берет, чтобы догнать.

var distanceToCatchUp = distanceBetweenCharacters - attackRange;

if(distanceToCatchUp > 0) //target is out of range
{
var attackerCatchUpSpeed = attackerMoveSpeed - targetMoveSpeed;

if(attackerCatchUpSpeed <= 0)
{
//attacker can never catch up, time needed is infinite (we cap it at the max duration)
currentTimer += duration;
}
else
{
//calculate the needed duration
var neededTime = Math.Ceiling((float)distanceToCatchUp/attackerCatchUpSpeed);

currentTimer += neededTime;
}
}

2. Атаковать цель (foreswing)

Обратите внимание на обязанности этого фрагмента:


  • Проанализировать продолжительность foreswing

  • Проверьте, если currentTimer превысил duration


    • Если нет, увеличить numberOfAttacks к 1

    • Если это так, возвращает текущее numberOfAttacks


currentTimer += unit.ForeSwingDuration;

if(currentTimer >= duration)
return numberOfAttacks;
else
numberOfAttacks++;

Это только строка, return значение! Никакой другой оператор return.

3. Опыт "атаки кулдаун" (замахе + время простоя)

currentTimer += unit.BackSwingDuration + unit.AttackIdleDuration;

4. Повторить

Завернуть все выше в while(true). Идея заключается в том, что моделирование выполняется до достижения заданного времени, после чего return заявление будет называться.

Сноски

Я опустил несколько вещей для краткости, так как они не являются основным фокусом на вопрос здесь:


  • Установка начальной позиции символов

  • Расчет расстояния ездил на цель во время "атаки перезарядка", которая просто totalAttackDuration * target.MoveSpeed.

  • (не указано) есть грань, когда атака не попадает, если цель выходит за радиус атаки до самой foreswing завершается?


    • Если это так, если злоумышленник, то убедитесь, что подходить ближе, чем максимальная дальность атаки, поэтому он может обеспечить попадания в цель?


2
ответ дан 12 апреля 2018 в 01:04 Источник Поделиться