using System.Collections; using System.Collections.Generic; using UnityEngine; public class CharacterLookatController : MonoBehaviour { [SerializeField] private Transform headLookat; [SerializeField] private Transform bodyLookat; [SerializeField] private Transform headBone; [Header("Head Rotation Limits")] [SerializeField] private float maxYaw = 60f; // Left/right [SerializeField] private float maxPitch = 30f; // Up/down private void LateUpdate() { // Rotate body on XZ plane Vector3 bodyLookatPlanarXZ = bodyLookat.position; bodyLookatPlanarXZ.y = transform.position.y; Vector3 bodyDirection = (bodyLookatPlanarXZ - transform.position).normalized; if (bodyDirection != Vector3.zero) transform.forward = bodyDirection; // Head direction clamping Vector3 headDirectionWorld = headLookat.position - headBone.position; Quaternion lookRotation = Quaternion.LookRotation(headDirectionWorld, Vector3.up); // Convert world rotation to local rotation relative to body Quaternion localRotation = Quaternion.Inverse(transform.rotation) * lookRotation; Vector3 localEuler = localRotation.eulerAngles; // Convert angles from 0-360 to -180 to 180 localEuler.x = NormalizeAngle(localEuler.x); localEuler.y = NormalizeAngle(localEuler.y); // Clamp pitch (x) and yaw (y) localEuler.x = Mathf.Clamp(localEuler.x, -maxPitch, maxPitch); localEuler.y = Mathf.Clamp(localEuler.y, -maxYaw, maxYaw); // Reconstruct the clamped rotation Quaternion clampedLocalRotation = Quaternion.Euler(localEuler); Quaternion clampedWorldRotation = transform.rotation * clampedLocalRotation; headBone.rotation = clampedWorldRotation; } private float NormalizeAngle(float angle) { if (angle > 180f) angle -= 360f; return angle; } }