Skip to content

Instantly share code, notes, and snippets.

@emilianavt
Last active Mar 11, 2021
Embed
What would you like to do?
This component uses the UnityEngine.LowLevel API to run Final IK things before Unity constraints and particle systems are evaluated.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.LowLevel;
using RootMotion.FinalIK;
// Disable IK and TwistRelaxers and set them on this to have them run before constraints and particle systems are evaluated.
// Only one instance of IKRunner is possible, but it should be easy enough to extend.
// Remember to call Start() on the TwistRelaxers once if they are disabled from the beginning. The IK may also need manual initialization.
public class IKRunner : MonoBehaviour
{
public IK ik;
public List<TwistRelaxer> twistRelaxers = new List<TwistRelaxer>();
private bool found = false;
bool FindInjectionPoint(ref PlayerLoopSystem system, string target) {
if (system.type != null && system.type.ToString() == target) {
return true;
}
if (system.subSystemList == null)
return false;
found = false;
for (int i = 0; i < system.subSystemList.Length; i++) {
found = found || FindInjectionPoint(ref system.subSystemList[i], target);
}
if (found) {
PlayerLoopSystem[] newLoop = new PlayerLoopSystem[system.subSystemList.Length + 1];
int j = 0;
for (int i = 0; i < system.subSystemList.Length; i++) {
if (system.subSystemList[i].type.ToString() == target) {
newLoop[j] = PreConstraintIKRunner.GetNewSystem();
j++;
}
newLoop[j++] = system.subSystemList[i];
}
system.subSystemList = newLoop;
}
return false;
}
public struct PreConstraintIKRunner {
public static bool ready = false;
public static IKRunner ikRunner;
public static PlayerLoopSystem GetNewSystem() {
return new PlayerLoopSystem() {
type = typeof(PreConstraintIKRunner),
updateDelegate = UpdateFunction
};
}
public static void UpdateFunction() {
ikRunner.IKUpdate();
}
}
void Start() {
PreConstraintIKRunner.ikRunner = this;
if (!PreConstraintIKRunner.ready) {
var loop = PlayerLoop.GetCurrentPlayerLoop();
try {
FindInjectionPoint(ref loop, "UnityEngine.PlayerLoop.PreLateUpdate+ConstraintManagerUpdate");
} catch (Exception e) {
Debug.LogError("Failed to insert IKRunner into PlayerLoop: " + e);
}
PlayerLoop.SetPlayerLoop(loop);
PreConstraintIKRunner.ready = found;
}
}
void Update() {
if (ik != null)
ik.GetIKSolver().FixTransforms();
}
void IKUpdate() {
if (ik != null) {
ik.GetIKSolver().Update();
foreach (var relaxer in twistRelaxers)
if (relaxer.ik == null) {
foreach (var solver in relaxer.twistSolvers)
solver.Relax();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment