private void LateUpdate() { // Aim adjustments. if (controller.Aiming) { // Calculate desired rotation. Quaternion targetRotation = Quaternion.LookRotation(controller.personalTarget - spine.position); // Apply parent bones initial rotation offsets targetRotation *= Quaternion.Euler(initialRootRotation); targetRotation *= Quaternion.Euler(initialHipsRotation); targetRotation *= Quaternion.Euler(initialSpineRotation); // Apply extra rotation offsets (depends on the NPC avatar). targetRotation *= Quaternion.Euler(controller.classStats.aimOffset); // Set rotation for the frame. Quaternion frameRotation = Quaternion.Slerp(lastRotation, targetRotation, timeCountAim); // Simulate a simple bone constraint on upper body rotation. // Is spine rotation relative to the hips is less than 60 degrees? if (Quaternion.Angle(frameRotation, hips.rotation) <= 60f) { // Set desired rotation. spine.rotation = frameRotation; timeCountAim += Time.deltaTime; } // Avoid unrealistic rotation, related to hips and spine. else { // Deal with over twist stuck situation, due to async rotation of spine and hips. if (timeCountAim == 0 && Quaternion.Angle(frameRotation, hips.rotation) > 70f) { // Stop aiming and aim again after body realignment (1 second interval). StartCoroutine(controller.UnstuckAim(1f)); } // No over twist, freeze spine rotation until the desired one is 60 degrees or less, relative to hips. spine.rotation = lastRotation; timeCountAim = 0; } // Grab rotation for next frame. lastRotation = spine.rotation; // Measure remain angle gap to desired aim orientation. Vector3 target = controller.personalTarget - gunMuzzle.position; Vector3 fwd = -gunMuzzle.right; currentAimAngleGap = Vector3.Angle(target, fwd); timeCountGuard = 0; } // Guard position adjustments. else { lastRotation = spine.rotation; // Slowly reduce aim offset when exiting aim position. spine.rotation *= Quaternion.Slerp(Quaternion.Euler(controller.classStats.aimOffset), Quaternion.identity, timeCountGuard); timeCountGuard += Time.deltaTime; } }