// Animancer // Copyright 2020 Kybernetik // #pragma warning disable CS0618 // Type or member is obsolete (for MixerState in Animancer Lite). #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using Animancer.Examples.FineControl; using UnityEngine; namespace Animancer.Examples.Locomotion { /// /// A with a and to allow the /// bot to move in any direction. /// [AddComponentMenu(Strings.MenuPrefix + "Examples/Locomotion - Spider Bot Advanced")] [HelpURL(Strings.APIDocumentationURL + ".Examples.Locomotion/SpiderBotAdvanced")] public sealed class SpiderBotAdvanced : SpiderBot { /************************************************************************************************************************/ [SerializeField] private Rigidbody _Body; [SerializeField] private float _TurnSpeed = 90; [SerializeField] private float _MovementSpeed = 1.5f; [SerializeField] private float _SprintMultiplier = 2; /************************************************************************************************************************/ [SerializeField] private MixerState.Transition2D _Move; protected override ITransition MovementAnimation { get { return _Move; } } /************************************************************************************************************************/ private Vector3 _MovementDirection; protected override bool IsMoving { get { return _MovementDirection != Vector3.zero; } } /************************************************************************************************************************/ protected override void Awake() { base.Awake(); // Create the movement state but don't play it yet. // This ensures that we can access _MovementAnimation.State in other methods before actually playing it. Animancer.States.GetOrCreate(_Move); } /************************************************************************************************************************/ protected override void Update() { // Calculate the movement direction and call the base method to wake up or go to sleep if necessary. _MovementDirection = GetMovementDirection(); base.Update(); // If the movement state is playing and not fading out: if (_Move.State.IsActive) { // Rotate towards the same Y angle as the camera. var eulerAngles = transform.eulerAngles; var targetEulerY = Camera.main.transform.eulerAngles.y; eulerAngles.y = Mathf.MoveTowardsAngle(eulerAngles.y, targetEulerY, _TurnSpeed * Time.deltaTime); transform.eulerAngles = eulerAngles; // The movement direction is in world space, so we need to convert it to local space to be appropriate // for the current rotation by using dot-products to determine how much of that direction lies along // each axis. This would be unnecessary if we did not rotate at all. _Move.State.Parameter = new Vector2( Vector3.Dot(transform.right, _MovementDirection), Vector3.Dot(transform.forward, _MovementDirection)); // Set its speed depending on whether you are sprinting or not. var isSprinting = Input.GetMouseButton(0); _Move.State.Speed = isSprinting ? _SprintMultiplier : 1; } else// Otherwise stop it entirely. { _Move.State.Parameter = Vector2.zero; _Move.State.Speed = 0; } } /************************************************************************************************************************/ private Vector3 GetMovementDirection() { // Get a ray from the main camera in the direction of the mouse cursor. var ray = Camera.main.ScreenPointToRay(Input.mousePosition); // Do a raycast with it and stop trying to move it it does not hit anything. // Note that this object is set to the Ignore Raycast layer so that the raycast will not hit it. RaycastHit raycastHit; if (!Physics.Raycast(ray, out raycastHit))// Note the exclamation mark ! return Vector3.zero; // If the ray hit something, calculate the horizontal direction from this object to that point. var direction = raycastHit.point - transform.position; direction.y = 0; // If we are close to the destination, stop moving. // We could use an arbitrary small value like 0.1, but that would not work if the speed is too high. // Instead, we can calculate the distance it will actually move in a single frame at max speed to determine // if it would arrive or pass the destination next frame. var distance = direction.magnitude; if (distance < _MovementSpeed * _SprintMultiplier * Time.fixedDeltaTime) { return Vector3.zero; } else { // Otherwise normalize the direction so that we do not change speed based on distance. // Calling direction.Normalize() would do the same thing, but would calculate the magnitude again. return direction / distance; } } /************************************************************************************************************************/ private void FixedUpdate() { // Move the rigidbody in the desired direction. _Body.velocity = _MovementDirection * _Move.State.Speed * _MovementSpeed; } /************************************************************************************************************************/ } }