Когда вы работаете с динамическими таблицами для игровых механик, например, при создании симулятора урона, часто возникает необходимость связать несколько параметров в цепочку зависимостей. Одна из распространенных проблем – циклические ссылки, которые ломают логику расчетов. Представьте ситуацию: персонаж использует навык, который ускоряет следующую атаку, но сам момент применения этого навыка зависит от предыдущих действий. Как разорвать этот круг без ручных правок или громоздких костылей?
В этой статье разберем решение на примере таблицы с двумя типами атак («базовая» и «навык»), где скорость выполнения действий зависит от активации временного баффа. Основная задача – автоматически рассчитывать интервалы между событиями, избегая ссылок на «самих себя».

Как работает механика расчета времени
Персонаж может выполнять:
- Базовую атаку за 10 или 20 кадров (зависит от наличия скоростного баффа).
- Навык за 60 кадров, который активирует бафф на следующую атаку.
Интервалы между действиями должны рассчитываться так, чтобы при активации баффа следующий ускорялся. Проблема возникает, когда формула в столбце «Скоростной бафф» ссылается на интервалы навыков, а те, в свою очередь, зависят от статуса баффа. Это создает циклическую зависимость, которую нельзя игнорировать.
Шаг 1: Отказ от прямых ссылок через функции высшего порядка
Ключевая идея – использовать функции LET, LAMBDA и REDUCE, которые позволяют организовать последовательные вычисления без перекрестных ссылок между ячейками. Вот готовая формула для столбца «Интервал»:
=let(
previous_, lambda(c, offset(c, -1, 0)),
n_, lambda(outcome, match(outcome, B4:B11, 0) - 1),
inc.1, H4 / n_("skill1"),
inc.2, 60 + (0 * H5 / (n_("skill2") - n_("skill1")),
time, reduce(tocol(æ, 2), C4:C11, lambda(a, c, let(
prev, previous_(c),
prevPrev, previous_(prev),
increment, ifs(
and(prev "basic", prev "skill"), 0,
and(prev = "basic", prevPrev = "skill"), inc.1 / 2,
prev = "basic", inc.1,
prev = "skill", inc.2,
true, 0
),
vstack(a, iferror(chooserows(a, -1)) + increment)
)),
time
)
Разбор формулы по компонентам:
- previous_ – лямбда-функция для получения предыдущего значения в столбце (аналог OFFSET(-1)).
- n_ – определяет количество повторений навыка до текущей строки через MATCH().
- inc.1 и inc.2 – переменные для расчета приращения времени после базовой атаки и навыка. Здесь inc.2 зафиксирован на 60 кадрах, как указано в условии.
- REDUCE – последовательно обрабатывает каждое действие, накапливая результат в массиве time.
Шаг 2: Настройка параметров под ваши условия
Если в вашей таблице больше двух навыков или другие значения длительности атак, потребуется модифицировать части формулы:
- Для добавления третьего навыка создайте переменную inc.3 по аналогии с inc.2 и расширьте условие в IFS.
- Чтобы изменить длительность базовой атаки с 10/20 на другие значения, скорректируйте inc.1. Например, если бафф сокращает время не вдвое, а на 30%, формула примет вид:
inc.1, H4 * 0.7 / n_("skill1").
Шаг 3: Устранение частых ошибок
Даже с такой формулой можно столкнуться с проблемами:
- #N/A в столбце интервалов – возникает, если MATCH() не находит навык в диапазоне B4:B11. Проверьте, что в столбце «Действие» нет опечаток.
- Некорректное время после навыка – если inc.2 рассчитан неправильно. Убедитесь, что для навыков установлено фиксированное значение (в примере – 60).
- Циклическая ссылка все еще появляется – такое возможно, если вы случайно ссылаетесь на ячейку с самой формулой в других частях таблицы. Используйте меню Файл → Настройки → Вычисления и проверьте, нет ли предупреждений о циклах.
Дополнительные рекомендации
- Тестовые данные – перед внедрением формулы в основной лист проверьте ее на упрощенной копии таблицы (документ с ручным вводом данных).
- Именованные диапазоны – если навыков становится слишком много, присвойте диапазонам имена (например, «Навык1», «Навык2»). Это упростит чтение формулы: n_(“skill1”) заменится на n_(Навык1).
- Визуализация – добавьте условное форматирование для столбца «Скоростной бафф», чтобы быстро отслеживать активные периоды ускорения.
Альтернативный подход: скрипты Google Apps Script
Если формулы кажутся слишком сложными, можно автоматизировать расчеты через скрипт. Пример функции, которая пересчитывает интервалы:
function calculateIntervals() {
const sheet = SpreadsheetApp.getActive().getSheetByName("Лист1");
const data = sheet.getRange("B4:C11").getValues();
let time = 0;
let prevAction = "";
let buffActive = false;
const intervals = data.map((row, index) => {
const [action] = row;
let increment = 0;
if (index === 0) {
return [time];
}
if (prevAction === "skill") {
increment = 60;
buffActive = true;
} else if (prevAction === "basic") {
increment = buffActive ? 10 : 20;
buffActive = false;
}
time += increment;
prevAction = action;
return [time];
});
sheet.getRange("D4:D11").setValues(intervals);
}
Этот скрипт можно запускать по кнопке или через триггеры. Однако учтите, что для больших таблиц производительность может снижаться.
Итог: Использование функций LET и REDUCE позволяет создать «самодостаточную» формулу, которая не ссылается на другие ячейки таблицы в реальном времени, тем самым избегая циклических зависимостей. Для новичков такой подход может показаться запутанным, но после нескольких экспериментов с лямбда-функциями вы заметите, насколько это удобно для динамических моделей.
Если остались вопросы по адаптации формулы под ваши условия – смело задавайте их в комментариях. Часто нюансы вроде разного времени атак или дополнительных баффов требуют точечной настройки, и здесь пригодится коллективный опыт.