Иногда встроенного в Unity IK недостаточно. Я собираюсь показать вам, как создать свой собственный IK-скрипт для Unity. Вы можете применить этот IK к любому сочлененному телу - пальцам, рукам или ногам. IK, который я собираюсь рассмотреть, в настоящее время используется Unity, Unreal, Panda и несколькими другими игровыми движками. Это называется FABRIK.
FABRIK - это итерационный метод. Итерационный метод - это метод, который не сразу решает проблему. Итерационный метод становится все ближе и ближе к правильному решению с большим количеством итераций метода - иными словами, итераций цикла.
Любая проблема IK имеет заданное сочлененное тело (набор связанных конечностей с длинами и углами). Конечный эффектор (позиция конечной точки конечности) имеет начальную позицию с позицией цели. Это может иметь другие цели, такие как углы.
Каждая итерация FABRIK состоит из двух основных частей.
void FABRIK() {
while( abs(endEffectorPosition — goalPosition) > EPS ) {
FinalToRoot(); // PartOne
RootToFinal(); // PartTwo
}
}
Первая часть переходит от конечной конечности к корневой. Для конечной конечности вам нужно изменить угол / вращение конечности так, чтобы он указывал на положение цели (сохраняя привязанную внутреннюю позицию и позволяя внешней позиции переводиться путем изменения угла). Затем вы переводите конечную конечность по обновленному углу в направлении цели, пока внешнее положение конечности не станет равным целевому положению (сохраняя угол, но позволяя внутреннему положению конечности измениться). Это в значительной степени так и для последней конечности, за исключением того, что теперь вам нужно обновить текущую позицию цели. Текущая позиция цели теперь установлена на обновленную внутреннюю позицию конечности.
Для каждой последующей внутренней конечности вы делаете то же самое. Чтобы было понятно, я обрисую это. Для текущей конечности вам нужно изменить угол / поворот конечности, чтобы он указывал на текущую позицию цели. Затем вы переводите текущую конечность вдоль обновленного угла в направлении текущей позиции цели, пока внешняя позиция конечности не станет равной текущей позиции цели. Наконец, вы обновляете текущую позицию цели, чтобы она была равна обновленной внутренней позиции текущей конечности.
Повторяйте эти операции до тех пор, пока вы не завершите эти операции на корневой ветви. После этого первая часть была завершена.
/* Part One */
void FinalToRoot() {
currentGoal = goalPosition;
currentLimb = finalLimb;
while (currentLimb != NULL) {
currentLimb.rotation = RotFromTo(Vector.UP,
currentGoal — currentLimb.inboardPosition);
currentGoal = currentLimb.inboardPosition;
currentLimb = currentLimb->inboardLimb;
}
}
Вторая часть повторяется в обратном направлении: от корневой конечности до конечной. Для корневой конечности вам необходимо обновить ее внутреннюю позицию до корневой позиции. Это должно переводить всю конечность (без растяжения). Это в значительной степени относится к корневой конечности, за исключением того, что вам теперь нужно обновить текущую внутреннюю позицию. Текущая внутренняя позиция теперь установлена на обновленную внешнюю позицию корневой конечности. Для каждой последовательной подвесной конечности вы делаете то же самое. Чтобы было понятно, я обрисую это. Для текущей конечности вам необходимо обновить ее внутреннюю позицию до текущей внутренней позиции. Это должно перевести всю конечность. Это в значительной степени так и для текущей конечности, за исключением того, что теперь вам нужно обновить текущую внутреннюю позицию. Текущая внутренняя позиция теперь установлена на обновленную внешнюю позицию текущей конечности.
Повторяйте эти операции до тех пор, пока вы не завершите эти операции на конечной конечности. После этого вторая часть была завершена.
/* Part Two */
void RootToFinal() {
currentInboardPosition = rootLimb.inboardPosition;
currentLimb = rootLimb;
while (currentLimb != NULL) {
currentLimb.inboardPosition = currentInboardPosition;
currentInboardPosition = currentLimb.outboardPosition;
currentLimb = currentLimb->inboardLimb;
}
}
Как только первая часть и вторая часть были завершены, вы завершили первую итерацию метода FABRIK. Продолжайте итерации по первой и второй частям до тех пор, пока конечный эффектор не окажется настолько близким, насколько вам нужно, чтобы он был к целевой позиции, определенной некоторым EPS (значение epsilon).
Это оно! Это метод FABRIK. Часть первая повторяется в обратном направлении от конечной конечности. Часть вторая переходит вперед от корневой конечности. Метод FABRIK выполняет итерации первой и второй частей до тех пор, пока конечный эффектор не окажется достаточно близко к целевой позиции.