55 lines
1.9 KiB
C#
55 lines
1.9 KiB
C#
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;
|
|
}
|
|
}
|