Skip to content

Instantly share code, notes, and snippets.

@emilianavt
Last active February 8, 2024 10:42
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emilianavt/3fdc79a8766ff781b86b0eb06aed5084 to your computer and use it in GitHub Desktop.
Save emilianavt/3fdc79a8766ff781b86b0eb06aed5084 to your computer and use it in GitHub Desktop.
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