using UnityEngine; namespace TriLibCore.Samples { /// /// Manages avatar movement and camera positioning in TriLib samples. /// This class extends to handle user input, /// allowing the character to move and rotate smoothly, as well as updating the /// camera’s position and orientation relative to the avatar. /// public class AvatarController : AbstractInputSystem { /// /// A singleton reference to the active in the scene. /// Set in to ensure only one active instance is present. /// public static AvatarController Instance { get; private set; } /// /// Maximum movement speed (in Unity units per second) that the avatar can reach. /// private const float MaxSpeed = 2f; /// /// Avatar acceleration (in Unity units per second) applied each frame /// when the user provides movement input. /// private const float Acceleration = 5f; /// /// Rate at which the avatar slows down (in Unity units per second) /// when there is no user input (applies friction to movement). /// private const float Friction = 2f; /// /// The rotation speed factor used to smoothly rotate the avatar toward the movement direction. /// private const float RotationSpeed = 60f; /// /// The responsible for handling collisions /// and movement for the avatar. /// public CharacterController CharacterController; /// /// The that handles animation playback and blending for this avatar. /// public Animator Animator; /// /// The root representing the loaded avatar model. /// This can be replaced at runtime if a new avatar is loaded. /// public GameObject InnerAvatar; /// /// The camera’s offset from the avatar’s position, used to maintain a consistent view. /// Calculated during and updated in to reflect rotation. /// private Vector3 _cameraOffset; /// /// The avatar’s current movement speed, dynamically updated each frame /// based on , , and user input. /// private float _speed; /// /// A vertical offset to position the camera at the avatar’s head level, /// derived from the 's height. /// private Vector3 _cameraHeightOffset; /// /// Tracks the instantaneous velocity for smooth avatar rotation /// in Mathf.SmoothDampAngle. /// private float _currentVelocity; /// /// Initializes the singleton instance and calculates the camera offsets /// based on the avatar’s height. Called automatically when the script instance awakens. /// private void Awake() { Instance = this; // Determine where the camera should pivot from the avatar’s head level _cameraHeightOffset = new Vector3(0f, CharacterController.height * 0.8f, 0f); // Store the initial offset between the camera and the avatar _cameraOffset = Camera.main.transform.position - transform.position; } /// /// Handles user input and updates the avatar’s position, rotation, speed, and animator parameters. /// Also updates the camera position and orientation to stay behind the avatar. /// private void Update() { // Gather input along the horizontal and vertical axes var input = new Vector3(GetAxis("Horizontal"), 0f, GetAxis("Vertical")); // Transform input according to the camera’s orientation, ignoring vertical component var direction = Camera.main.transform.TransformDirection(input); direction.y = 0f; direction.Normalize(); // Determine the avatar’s target orientation based on input direction var targetEulerAngles = direction.magnitude > 0 ? Quaternion.LookRotation(direction).eulerAngles : transform.rotation.eulerAngles; // Smoothly rotate the avatar toward the movement direction var eulerAngles = transform.rotation.eulerAngles; eulerAngles.y = Mathf.SmoothDampAngle( eulerAngles.y, targetEulerAngles.y, ref _currentVelocity, Time.deltaTime * RotationSpeed * input.magnitude ); transform.rotation = Quaternion.Euler(eulerAngles); // Update speed based on acceleration or friction _speed += input.magnitude * (Acceleration * MaxSpeed) * Time.deltaTime; _speed -= Friction * MaxSpeed * Time.deltaTime; _speed = Mathf.Clamp(_speed, 0f, MaxSpeed); // Move the avatar using the CharacterController CharacterController.SimpleMove(transform.forward * _speed); // Update the animator with the normalized speed Animator.SetFloat("SpeedFactor", _speed / MaxSpeed); // Adjust the camera based on the user’s camera angle (from AssetViewerBase) and stored offsets var pivotedPosition = Quaternion.AngleAxis(AvatarLoader.Instance.CameraAngle.x, Vector3.up) * Quaternion.AngleAxis(-AvatarLoader.Instance.CameraAngle.y, Vector3.right) * _cameraOffset; Camera.main.transform.position = transform.position + _cameraHeightOffset + pivotedPosition; Camera.main.transform.LookAt(transform.position + _cameraHeightOffset); } } }