Created
July 29, 2022 02:08
-
-
Save hiroshi-nishiura/9a696e2f5ba28fc82e410f8c16dc09c9 to your computer and use it in GitHub Desktop.
Hand IK Applier for Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UnityEngine; | |
using UnityEngine.Animations; | |
using UnityEngine.Playables; | |
public struct IKTarget | |
{ | |
public Vector3 pos; | |
public Quaternion rot; | |
public Vector3 hint; | |
} | |
public struct HandIKJob : IAnimationJob | |
{ | |
public IKTarget left; | |
public IKTarget right; | |
public PropertySceneHandle leftWeightHandle; | |
public PropertySceneHandle rightWeightHandle; | |
public void ProcessRootMotion(AnimationStream stream){} | |
public void ProcessAnimation(AnimationStream stream) | |
{ | |
AnimationHumanStream humanStream = stream.AsHuman(); | |
left.pos = humanStream.GetGoalPosition(AvatarIKGoal.LeftHand); | |
left.rot = humanStream.GetGoalRotation(AvatarIKGoal.LeftHand); | |
left.hint = humanStream.GetHintPosition(AvatarIKHint.LeftElbow); | |
right.pos = humanStream.GetGoalPosition(AvatarIKGoal.RightHand); | |
right.rot = humanStream.GetGoalRotation(AvatarIKGoal.RightHand); | |
right.hint = humanStream.GetHintPosition(AvatarIKHint.RightElbow); | |
float weightL = leftWeightHandle.GetFloat(stream); | |
float weightR = rightWeightHandle.GetFloat(stream); | |
if(weightL>0) | |
{ | |
humanStream.SetGoalPosition(AvatarIKGoal.LeftHand, left.pos); | |
humanStream.SetGoalRotation(AvatarIKGoal.LeftHand, left.rot); | |
humanStream.SetHintPosition(AvatarIKHint.LeftElbow, left.hint); | |
humanStream.SetGoalWeightPosition(AvatarIKGoal.LeftHand, weightL); | |
humanStream.SetGoalWeightRotation(AvatarIKGoal.LeftHand, weightL); | |
humanStream.SetHintWeightPosition(AvatarIKHint.LeftElbow, weightL); | |
} | |
if(weightR>0) | |
{ | |
humanStream.SetGoalPosition(AvatarIKGoal.RightHand, right.pos); | |
humanStream.SetGoalRotation(AvatarIKGoal.RightHand, right.rot); | |
humanStream.SetHintPosition(AvatarIKHint.RightElbow, right.hint); | |
humanStream.SetGoalWeightPosition(AvatarIKGoal.RightHand, weightR); | |
humanStream.SetGoalWeightRotation(AvatarIKGoal.RightHand, weightR); | |
humanStream.SetHintWeightPosition(AvatarIKHint.RightElbow, weightR); | |
} | |
if(weightL>0 || weightR>0) humanStream.SolveIK(); | |
} | |
} | |
public class HandIKApplier : MonoBehaviour | |
{ | |
public bool _drawGizmo = false; | |
public PlayableDirector _director; | |
[Range(0,1)] | |
public float _leftWeight; | |
[Range(0,1)] | |
public float _rightWeight; | |
Animator _animator; | |
AnimationScriptPlayable _playable; | |
IKTarget _left; | |
IKTarget _right; | |
void OnEnable() | |
{ | |
for(Transform current = this.transform; _animator==null && current!=null; current = current.parent) | |
_animator = current.GetComponent<Animator>(); | |
if(_animator==null) throw new System.Exception("Animator has not in the parent"); | |
if(_director && _director.playableGraph.IsValid()) CreateJob(_director.playableGraph); | |
} | |
void OnDisable() | |
{ | |
if (_playable.IsValid()) _playable.Destroy(); | |
} | |
void CreateJob(PlayableGraph graph) // PlayableDirector's graph | |
{ | |
for(int i=0; i<graph.GetOutputCountByType<AnimationPlayableOutput>(); i++) | |
{ | |
AnimationPlayableOutput output = (AnimationPlayableOutput) graph.GetOutputByType<AnimationPlayableOutput>(i); | |
if(output.GetTarget() == _animator && output.IsOutputValid()) | |
{ | |
var job = new HandIKJob() | |
{ | |
leftWeightHandle = _animator.BindSceneProperty(transform, typeof(HandIKApplier), "_leftWeight"), | |
rightWeightHandle = _animator.BindSceneProperty(transform, typeof(HandIKApplier), "_rightWeight") | |
}; | |
_playable = AnimationScriptPlayable.Create(graph, job, 1); | |
_playable.SetInputWeight(0, 1); | |
var timeline = output.GetSourcePlayable(); | |
var source = timeline.GetInput(i); | |
graph.Disconnect(timeline, i); | |
graph.Connect(source, 0, _playable, 0); | |
graph.Connect(_playable, 0, timeline, i); | |
break; | |
} | |
} | |
//Debug.Log("CreateJob : " + _animator + " // " + _playable); | |
} | |
void OnAnimatorIK()// Animation Controller events | |
{ | |
_animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, _leftWeight); | |
_animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, _leftWeight); | |
_animator.SetIKHintPositionWeight(AvatarIKHint.LeftElbow, _leftWeight); | |
_animator.SetIKPositionWeight(AvatarIKGoal.RightHand, _rightWeight); | |
_animator.SetIKRotationWeight(AvatarIKGoal.RightHand, _rightWeight); | |
_animator.SetIKHintPositionWeight(AvatarIKHint.RightElbow, _rightWeight); | |
} | |
void Update() | |
{ | |
if(_director && _director.playableGraph.IsValid() && !_playable.IsValid()) CreateJob(_director.playableGraph); | |
} | |
void LateUpdate() | |
{ | |
if (_playable.IsValid()) | |
{ | |
var job = _playable.GetJobData<HandIKJob>(); | |
_left = job.left; | |
_right = job.right; | |
} | |
} | |
#if UNITY_EDITOR | |
void OnDrawGizmosSelected() | |
{ | |
if (_drawGizmo && _playable.IsValid()) | |
{ | |
Vector3 scale = new Vector3(1,1,1); | |
Gizmos.color = Color.magenta; | |
Gizmos.matrix = Matrix4x4.TRS(_left.pos, _left.rot, scale); | |
Gizmos.DrawWireSphere(Vector3.zero, 0.05f); | |
Gizmos.matrix = Matrix4x4.TRS(_right.pos, _right.rot, scale); | |
Gizmos.DrawWireSphere(Vector3.zero, 0.05f); | |
Gizmos.matrix = Matrix4x4.identity; | |
Gizmos.DrawWireSphere(_left.hint, 0.05f); | |
Gizmos.DrawWireSphere(_right.hint, 0.05f); | |
} | |
} | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
see http://ossan-engineer.blogspot.com/2022/07/handikappier-for-unity.html