public void Init(CCDIKBone child, CCDIKBone parent) { LastIKLocPosition = transform.localPosition; IKParent = parent; if (child != null) { SetChild(child); } IKChild = child; }
/// <summary> Assigning bones for IK processor with CCD IK logics (unlimited bone count) </summary> public FIK_CCDProcessor(Transform[] bonesChain) { IKBones = new CCDIKBone[bonesChain.Length]; Bones = new CCDIKBone[IKBones.Length]; for (int i = 0; i < bonesChain.Length; i++) { IKBones[i] = new CCDIKBone(bonesChain[i]); Bones[i] = IKBones[i]; } IKTargetPosition = EndBone.transform.position; IKTargetRotation = EndBone.transform.rotation; }
public override void Init(Transform root) { if (Initialized) { return; } fullLength = 0f; for (int i = 0; i < Bones.Length; i++) { CCDIKBone b = IKBones[i]; CCDIKBone child = null, parent = null; if (i > 0) { parent = IKBones[i - 1]; } else if (i < Bones.Length - 1) { child = IKBones[i + 1]; } if (i < Bones.Length - 1) { IKBones[i].Init(child, parent); fullLength += b.BoneLength; b.ForwardOrientation = Quaternion.Inverse(b.transform.rotation) * (IKBones[i + 1].transform.position - b.transform.position); } else { IKBones[i].Init(child, parent); b.ForwardOrientation = Quaternion.Inverse(b.transform.rotation) * (IKBones[IKBones.Length - 1].transform.position - IKBones[0].transform.position); } } Initialized = true; }
/// <summary> Updating processor with n-bones oriented inverse kinematics </summary> public override void Update() { if (!Initialized) { return; } if (IKWeight <= 0f) { return; } CCDIKBone wb = IKBones[0]; // Restoring previous IK progress for continous solving if (ContinousSolving) { while (wb != null) { wb.LastKeyLocalRotation = wb.transform.localRotation; wb.transform.localPosition = wb.LastIKLocPosition; wb.transform.localRotation = wb.LastIKLocRotation; wb = wb.IKChild; } } else { if (SyncWithAnimator > 0f) { // Memory for animator syncing while (wb != null) { wb.LastKeyLocalRotation = wb.transform.localRotation; wb = wb.IKChild; } } } if (ReactionQuality < 0) { ReactionQuality = 1; } Vector3 goalPivotOffset = Vector3.zero; if (ReactionQuality > 1) { goalPivotOffset = GetGoalPivotOffset(); } for (int itr = 0; itr < ReactionQuality; itr++) { // Restrictions for multiple interations if (itr >= 1) { if (goalPivotOffset.sqrMagnitude == 0) { if (Smoothing > 0) { if (GetVelocityDifference() < Smoothing * Smoothing) { break; } } } } LastLocalDirection = RefreshLocalDirection(); Vector3 ikGoal = IKTargetPosition + goalPivotOffset; // Going in iterations in reversed way, from pre end child to root parent wb = IKBones[IKBones.Length - 2]; if (!Use2D) // Full 3D space rotations calculations { while (wb != null) { float weight = wb.MotionWeight * IKWeight; if (weight > 0f) { Quaternion targetRotation = Quaternion.FromToRotation(Bones[Bones.Length - 1].transform.position - wb.transform.position /*fromThisToEndChildBone*/, ikGoal - wb.transform.position /*fromThisToIKGoal*/) * wb.transform.rotation; if (weight < 1f) { wb.transform.rotation = Quaternion.Lerp(wb.transform.rotation, targetRotation, weight); } else { wb.transform.rotation = targetRotation; } } wb.AngleLimiting(); wb = wb.IKParent; } } else { // Going in while() loop is 2x faster than for(i;i;i;) when there is more iterations while (wb != null) { float weight = wb.MotionWeight * IKWeight; if (weight > 0f) { Vector3 fromThisToEndChildBone = Bones[Bones.Length - 1].transform.position - wb.transform.position; Vector3 fromThisToIKGoal = ikGoal - wb.transform.position; wb.transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(Mathf.Atan2(fromThisToEndChildBone.x, fromThisToEndChildBone.y) * Mathf.Rad2Deg /* Angle to last bone */, Mathf.Atan2(fromThisToIKGoal.x, fromThisToIKGoal.y) * Mathf.Rad2Deg /* Angle to goal position */) * weight, Vector3.back) * wb.transform.rotation; } wb.AngleLimiting(); wb = wb.IKParent; } } } LastLocalDirection = RefreshLocalDirection(); // Support for stretching if (MaxStretching > 0f) { ActiveLength = Mathf.Epsilon; wb = IKBones[0]; while (wb.IKChild != null) { wb.FrameWorldLength = (wb.transform.position - wb.IKChild.transform.position).magnitude; ActiveLength += wb.FrameWorldLength; wb = wb.IKChild; } Vector3 toGoal = IKTargetPosition - StartBone.transform.position; float stretch = toGoal.magnitude / ActiveLength; if (stretch > 1f) { for (int i = 1; i < IKBones.Length; ++i) { IKBones[i].transform.position += toGoal.normalized * (IKBones[i - 1].BoneLength * MaxStretching) * StretchCurve.Evaluate(-(1f - stretch)); } } } wb = IKBones[0]; while (wb != null) { // Storing final rotations for animator offset wb.LastIKLocRotation = wb.transform.localRotation; wb.LastIKLocPosition = wb.transform.localPosition; // Offset based rotation sync with animator Quaternion ikDiff = wb.LastIKLocRotation * Quaternion.Inverse(wb.InitialLocalRotation); wb.transform.localRotation = Quaternion.Lerp(wb.LastIKLocRotation, ikDiff * wb.LastKeyLocalRotation, SyncWithAnimator); if (IKWeight < 1f) { wb.transform.localRotation = Quaternion.Lerp(wb.LastKeyLocalRotation, wb.transform.localRotation, IKWeight); } wb = wb.IKChild; } }