Разработка игр, движкописательство и немножко про путешествия.
проснуться();
расчистить_вход_в_норку_от_песка();
пока ещё_не_перегрелись_на_солнце {
выбрать_точку_куда_идти();
пока не_дошли {
сделать_шаг_по_маршруту()
если встретили_пищу, то
есть();
если встретили_врага, то
сражаться();
}
}
идти_в_тенёк().
Согласитесь, намного читабельнее и удобнее, чем писать штук 10 классов под это дело. Единственная проблемка в том, что каждая операция здесь потенциально может выполняться больше одного игрового тика (то есть длиться дольше одного кадра; например, из-за анимации), а потому простой функцией здесь не обойтись. Как вы уже догадались, нам просто нужна возможность писать функции с несколькими точками входа. Или, проще говоря, возможность нашпиговать этот код yield()-ами через строчку:
проснуться();
yield расчистить_вход_в_норку_от_песка();
пока ещё_не_перегрелись_на_солнце {
выбрать_точку_куда_идти();
пока не_дошли {
yield сделать_шаг_по_маршруту()
если встретили_пищу, то
yield есть();
если встретили_врага, то
yield сражаться();
}
}
yield идти_в_тенёк().
Такие фаршированные функции называют генераторами. Они могут быть знакомы многим по C#. И по большому счёту это и есть машина состояний, только записанная более удобным синтаксисом; поскольку внутренне это всё разбивается всё равно на несколько маленьких функций от одного yield до другого, а общие переменные выносятся в небольшую структуру, которая передаётся дальше (на манер того, как это происходит с замыканиями). Каждая такая сгенерированная структурка и соответствующая ей функция по сути и образуют наши олдскульные классы состояний конечного автомата.public struct EndlessSpring
{
private float TimeScale;
// time is amount of time that needeed to pass
// threshold-share of the distance (never pass 100%)
public EndlessSpring(float time, float threashold = 0.98f)
{
var expectedValue = 1 / (1 - threashold);
this.TimeScale = Mathf.Log(expectedValue, 2) / time;
}
public float GetLerpK(float deltaTime)
{
return 1 - 1 / Mathf.Pow(2, deltaTime * TimeScale);
}
}
1 / 2^tА значит пройденное расстояние от времени вычисляется по формуле:
1 - 1 / 2^tДвойка здесь всего лишь указатель на то, что в качестве одинаковых промежутков мы выбрали половину расстояния. Мы можем подставить туда 3, чтобы получить треть, или любое другое число больше 1.
1 - 1 / base^t = 0,98Ну вот и всё. Теперь мы можем инициализировать этими параметрами нашу бесконечную резинку-пружинку, после чего ей можно будет скармливать deltaTime, а в ответ получать LerpK. Таким образом получилось простое FPS-независимое сглаживание для всего, что можно лерпать. Финальный класс можно видеть на скриншоте.
base^t = 1 / (1 - 0,98)
t = log(1 / (1 - 0,98), base)