// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using Animancer.Units; using UnityEngine; namespace Animancer.Samples.Mixers { /// /// Controls Animancer parameters to make the character move /// towards the mouse position using Root Motion in a 2D Mixer. /// /// /// /// Sample: /// /// Directional Mixers /// /// /// https://kybernetik.com.au/animancer/api/Animancer.Samples.Mixers/FollowMousePosition /// [AddComponentMenu(Strings.SamplesMenuPrefix + "Mixers - Follow Mouse Position")] [AnimancerHelpUrl(typeof(FollowMousePosition))] public class FollowMousePosition : MonoBehaviour { /************************************************************************************************************************/ #if UNITY_PHYSICS_3D /************************************************************************************************************************/ // We could hard code the parameter names like this: // public static readonly StringReference ParameterX = "Movement X"; // public static readonly StringReference ParameterY = "Movement Y"; // But that would make this script less flexible // and you wouldn't be able to see what it's using in the Inspector. [SerializeField] private AnimancerComponent _Animancer; [SerializeField] private StringAsset _ParameterX; [SerializeField] private StringAsset _ParameterY; [SerializeField, Seconds] private float _ParameterSmoothTime = 0.15f; [SerializeField, Meters] private float _StopProximity = 0.1f; /************************************************************************************************************************/ private SmoothedVector2Parameter _SmoothedParameters; /************************************************************************************************************************/ protected virtual void Awake() { _SmoothedParameters = new SmoothedVector2Parameter( _Animancer, _ParameterX, _ParameterY, _ParameterSmoothTime); } /************************************************************************************************************************/ protected virtual void Update() { // Calculate the movement direction. Vector3 movementDirection = GetMovementDirection(); // The movement direction is in world space, // so we need to convert it to the character's local space // to be appropriate for their current rotation. Vector3 localDirection = transform.InverseTransformDirection(movementDirection); // Then set the target value for the parameters to move towards: // - Parameter X towards Direction X (right/left). // - Parameter Y towards Direction Z (forwards/backwards). // - Ignore Direction Y because the Mixer is only 2D. _SmoothedParameters.TargetValue = new Vector2(localDirection.x, localDirection.z); } /************************************************************************************************************************/ private Vector3 GetMovementDirection() { // Get a ray from the main camera in the direction of the mouse cursor. Ray ray = Camera.main.ScreenPointToRay(SampleInput.MousePosition); // Raycast with it and stop trying to move it it doesn't hit anything. if (!Physics.Raycast(ray, out RaycastHit raycastHit))// Note the exclamation mark ! return Vector3.zero; // If the ray hit something, calculate the direction from this object to that point. Vector3 direction = raycastHit.point - transform.position; // If we are close to the destination, stop moving. float squaredDistance = direction.sqrMagnitude; if (squaredDistance <= _StopProximity * _StopProximity) { return Vector3.zero; } else { // Otherwise normalize the direction so that we don't change speed based on distance. // Calling direction.Normalize() would do the same thing, but would calculate the magnitude again. return direction / Mathf.Sqrt(squaredDistance); } } /************************************************************************************************************************/ protected virtual void OnDestroy() { // It's not necessary for this sample, // but if this component could be destroyed before the rest of the character // then we need to Dispose the smoother to remove it from the target parameters. _SmoothedParameters.Dispose(); } /************************************************************************************************************************/ #else /************************************************************************************************************************/ protected virtual void Awake() { SampleReadMe.LogMissingPhysics3DModuleError(this); } /************************************************************************************************************************/ #endif /************************************************************************************************************************/ } }