public void Solve_Animated(IKSolverVR solver, float scale, float deltaTime) { if (animator == null) { Debug.LogError("VRIK cannot find Animator on the VRIK root gameobject.", solver.root); return; } if (deltaTime <= 0f) { return; } if (!animator.enabled) { Debug.LogWarning("Trying to use VRIK animated locomotion with a disabled animator!", solver.root); return; } // Root up vector Vector3 rootUp = solver.rootBone.solverRotation * Vector3.up; // Substract any motion from parent transforms Vector3 externalDelta = solver.rootBone.solverPosition - lastEndRootPos; externalDelta -= animator.deltaPosition; // Head target position Vector3 headTargetPos = solver.spine.headPosition; Vector3 standOffsetWorld = solver.rootBone.solverRotation * new Vector3(standOffset.x, 0f, standOffset.y) * scale; headTargetPos += standOffsetWorld; if (firstFrame) { lastHeadTargetPos = headTargetPos; firstFrame = false; } // Head target velocity Vector3 headTargetVelocity = (headTargetPos - lastHeadTargetPos) / deltaTime; lastHeadTargetPos = headTargetPos; headTargetVelocity = V3Tools.Flatten(headTargetVelocity, rootUp); // Head target offset Vector3 offset = headTargetPos - solver.rootBone.solverPosition; offset -= externalDelta; offset -= lastCorrection; offset = V3Tools.Flatten(offset, rootUp); // Turning Vector3 headForward = (solver.spine.IKRotationHead * solver.spine.anchorRelativeToHead) * Vector3.forward; headForward.y = 0f; Vector3 headForwardLocal = Quaternion.Inverse(solver.rootBone.solverRotation) * headForward; float angle = Mathf.Atan2(headForwardLocal.x, headForwardLocal.z) * Mathf.Rad2Deg; angle += solver.spine.rootHeadingOffset; float turnTarget = angle / 90f; bool isTurning = true; if (Mathf.Abs(turnTarget) < 0.2f) { turnTarget = 0f; isTurning = false; } turn = Mathf.Lerp(turn, turnTarget, Time.deltaTime * 3f); animator.SetFloat(VRIK_Turn, turn * 2f); // Local Velocity, animation smoothing Vector3 velocityLocalTarget = Quaternion.Inverse(solver.readRotations[0]) * (headTargetVelocity + offset); velocityLocalTarget *= weight * stepLengthMlp; float animationSmoothTimeTarget = isTurning && !isMoving ? 0.2f : animationSmoothTime; currentAnimationSmoothTime = Mathf.Lerp(currentAnimationSmoothTime, animationSmoothTimeTarget, deltaTime * 20f); velocityLocal = Vector3.SmoothDamp(velocityLocal, velocityLocalTarget, ref velocityLocalV, currentAnimationSmoothTime, Mathf.Infinity, deltaTime); float velLocalMag = velocityLocal.magnitude / stepLengthMlp; //animator.SetBool("VRIK_StartWithRightFoot", velocityLocal.x >= 0f); animator.SetFloat(VRIK_Horizontal, velocityLocal.x / scale); animator.SetFloat(VRIK_Vertical, velocityLocal.z / scale); // Is Moving float m = moveThreshold * scale; if (isMoving) { m *= 0.9f; } bool isMovingRaw = velocityLocal.sqrMagnitude > m * m; if (isMovingRaw) { stopMoveTimer = 0f; } else { stopMoveTimer += deltaTime; } isMoving = stopMoveTimer < 0.05f; // Max root angle float maxRootAngleTarget = isMoving ? maxRootAngleMoving : maxRootAngleStanding; solver.spine.maxRootAngle = Mathf.SmoothDamp(solver.spine.maxRootAngle, maxRootAngleTarget, ref maxRootAngleV, 0.2f, Mathf.Infinity, deltaTime); animator.SetBool(VRIK_IsMoving, isMoving); // Animation speed Vector3 currentRootPos = solver.rootBone.solverPosition; currentRootPos -= externalDelta; currentRootPos -= lastCorrection; Vector3 rootVelocity = (currentRootPos - lastSpeedRootPos) / deltaTime; lastSpeedRootPos = solver.rootBone.solverPosition; float rootVelocityMag = rootVelocity.magnitude; float animSpeedTarget = minAnimationSpeed; if (rootVelocityMag > 0f && isMovingRaw) { animSpeedTarget = animSpeed * (velLocalMag / rootVelocityMag); } animSpeedTarget = Mathf.Clamp(animSpeedTarget, minAnimationSpeed, maxAnimationSpeed); animSpeed = Mathf.SmoothDamp(animSpeed, animSpeedTarget, ref animSpeedV, 0.05f, Mathf.Infinity, deltaTime); animSpeed = Mathf.Lerp(1f, animSpeed, weight); animator.SetFloat(VRIK_Speed, animSpeed); // Is Stopping AnimatorTransitionInfo transInfo = animator.GetAnimatorTransitionInfo(0); bool isStopping = transInfo.IsUserName("VRIK_Stop"); // Root lerp speed float rootLerpSpeedTarget = 0; if (isMoving) { rootLerpSpeedTarget = rootLerpSpeedWhileMoving; } if (isStopping) { rootLerpSpeedTarget = rootLerpSpeedWhileStopping; } if (isTurning) { rootLerpSpeedTarget = rootLerpSpeedWhileTurning; } rootLerpSpeedTarget *= Mathf.Max(headTargetVelocity.magnitude, 0.2f); rootLerpSpeed = Mathf.Lerp(rootLerpSpeed, rootLerpSpeedTarget, deltaTime * 20f); // Root lerp and limits headTargetPos += V3Tools.ExtractVertical(solver.rootBone.solverPosition - headTargetPos, rootUp, 1f); if (maxRootOffset > 0f) { // Lerp towards head target position Vector3 p = solver.rootBone.solverPosition; if (rootLerpSpeed > 0f) { solver.rootBone.solverPosition = Vector3.Lerp(solver.rootBone.solverPosition, headTargetPos, rootLerpSpeed * deltaTime * weight); } lastCorrection = solver.rootBone.solverPosition - p; // Max offset offset = headTargetPos - solver.rootBone.solverPosition; offset = V3Tools.Flatten(offset, rootUp); float offsetMag = offset.magnitude; if (offsetMag > maxRootOffset) { lastCorrection += (offset - (offset / offsetMag) * maxRootOffset) * weight; solver.rootBone.solverPosition += lastCorrection; } } else { // Snap to head target position lastCorrection = (headTargetPos - solver.rootBone.solverPosition) * weight; solver.rootBone.solverPosition += lastCorrection; } lastEndRootPos = solver.rootBone.solverPosition; }