using UnityEngine;
/// <summary>
/// Third-person controller for the T-Rex player character.
/// Handles movement, rotation, camera follow, and basic animations.
/// </summary>
public class TrexController : MonoBehaviour
{
[Header("Movement")]
[Tooltip("Walking speed in units per second")]
public float walkSpeed = 5f;
[Tooltip("Running speed when holding Shift")]
public float runSpeed = 8f;
[Tooltip("Rotation speed when turning")]
public float rotationSpeed = 10f;
[Tooltip("How much gravity affects the T-Rex")]
public float gravity = -9.81f;
[Header("Jumping")]
[Tooltip("Jump velocity in units per second")]
public float jumpHeight = 2f;
[Tooltip("Ground check distance from feet")]
public float groundCheckDistance = 0.4f;
[Tooltip("Layer mask for what counts as ground")]
public LayerMask groundLayer;
[Header("Camera")]
[Tooltip("Offset of camera behind and above T-Rex")]
public Vector3 cameraOffset = new Vector3(0, 3f, -6f);
[Tooltip("How smoothly camera follows")]
public float cameraSmoothTime = 0.1f;
[Tooltip("Enable mouse look for camera rotation")]
public bool enableMouseLook = true;
[Tooltip("Mouse sensitivity for looking around")]
public float mouseSensitivity = 2f;
[Header("References")]
[Tooltip("Transform representing the T-Rex's feet position")]
public Transform groundCheck;
[Tooltip("The camera that follows the T-Rex")]
public Camera mainCamera;
[Tooltip("Animator component for playing animations")]
public Animator animator;
// Private variables
private Vector3 velocity;
private bool isGrounded;
private float horizontalLook;
private float verticalLook;
private float currentHorizontalLook;
private float currentVerticalLook;
private CharacterController controller;
private Vector3 cameraVelocity = Vector3.zero;
void Start()
{
// Get or add CharacterController
controller = GetComponent<CharacterController>();
if (controller == null)
{
Debug.LogWarning("No CharacterController found! Adding one.", gameObject);
controller = gameObject.AddComponent<CharacterController>();
controller.height = 3f;
controller.radius = 0.8f;
}
// Setup ground check if not assigned
if (groundCheck == null)
{
groundCheck = new GameObject("GroundCheck").transform;
groundCheck.SetParent(transform);
groundCheck.localPosition = new Vector3(0, -controller.height / 2f + 0.1f, 0);
}
// Setup animator parameters if assigned
if (animator != null)
{
animator.SetFloat("Speed", 0f);
animator.SetBool("IsGrounded", true);
}
// Lock cursor on start
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
Debug.Log("🦖 T-Rex Controller initialized!");
}
void Update()
{
HandleGroundCheck();
HandleMovement();
HandleCamera();
UpdateAnimator();
}
void HandleGroundCheck()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundCheckDistance, groundLayer);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f; // Small downward push to keep grounded
}
}
void HandleMovement()
{
// Get input
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
bool isRunning = Input.GetKey(KeyCode.LeftShift);
bool wantsToJump = Input.GetButtonDown("Jump");
// Calculate movement direction relative to camera
Vector3 moveDirection = CameraRelativeDirection(horizontal, vertical);
// Apply speed
float currentSpeed = isRunning ? runSpeed : walkSpeed;
Vector3 movement = moveDirection * currentSpeed;
// Handle jumping
if (isGrounded && wantsToJump)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
// Apply gravity
if (!isGrounded)
{
velocity.y += gravity * Time.deltaTime;
movement.y = velocity.y;
}
else
{
movement.y = velocity.y;
}
// Move the character
controller.Move(movement * Time.deltaTime);
// Rotate T-Rex to face movement direction
if (moveDirection.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
}
Vector3 CameraRelativeDirection(float horizontal, float vertical)
{
if (mainCamera == null)
{
mainCamera = Camera.main;
}
Vector3 forward = mainCamera.transform.forward;
Vector3 right = mainCamera.transform.right;
// Remove vertical component
forward.y = 0f;
right.y = 0f;
forward.Normalize();
right.Normalize();
return (forward * vertical + right * horizontal).normalized;
}
void HandleCamera()
{
if (mainCamera == null || !enableMouseLook) return;
// Mouse look
if (Input.GetMouseButton(1) || Cursor.lockState == CursorLockMode.Locked)
{
horizontalLook += Input.GetAxis("Mouse X") * mouseSensitivity;
verticalLook -= Input.GetAxis("Mouse Y") * mouseSensitivity;
// Clamp vertical look
verticalLook = Mathf.Clamp(verticalLook, -60f, 60f);
}
// Smooth camera rotation
currentHorizontalLook = Mathf.Lerp(currentHorizontalLook, horizontalLook, cameraSmoothTime * 10f);
currentVerticalLook = Mathf.Lerp(currentVerticalLook, verticalLook, cameraSmoothTime * 10f);
// Calculate target camera position
Quaternion rotation = Quaternion.Euler(currentVerticalLook, currentHorizontalLook, 0f);
Vector3 targetPosition = transform.position + transform.up * cameraOffset.y - rotation * Vector3.forward * cameraOffset.z;
targetPosition += rotation * Vector3.right * cameraOffset.x;
// Smooth camera movement
mainCamera.transform.position = Vector3.SmoothDamp(
mainCamera.transform.position,
targetPosition,
ref cameraVelocity,
cameraSmoothTime
);
// Camera always looks at T-Rex
mainCamera.transform.LookAt(transform.position + Vector3.up * 2f);
}
void UpdateAnimator()
{
if (animator == null) return;
float speed = Mathf.Abs(Input.GetAxis("Horizontal")) + Mathf.Abs(Input.GetAxis("Vertical"));
animator.SetFloat("Speed", speed);
animator.SetBool("IsGrounded", isGrounded);
}
// Debug visualization
void OnDrawGizmosSelected()
{
// Draw ground check sphere
if (groundCheck != null)
{
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(groundCheck.position, groundCheckDistance);
}
}
}