SpiderBotController.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  3. using Animancer.Examples.FineControl;
  4. using Animancer.Units;
  5. using UnityEngine;
  6. namespace Animancer.Examples.Locomotion
  7. {
  8. /// <summary>
  9. /// Controls a <see cref="SpiderBot"/> with a <see cref="MixerTransition2D"/> and <see cref="Rigidbody"/> to allow
  10. /// the bot to move around the scene in any direction.
  11. /// </summary>
  12. /// <example><see href="https://kybernetik.com.au/animancer/docs/examples/locomotion/directional-blending">Directional Blending</see></example>
  13. /// https://kybernetik.com.au/animancer/api/Animancer.Examples.Locomotion/SpiderBotController
  14. ///
  15. [AddComponentMenu(Strings.ExamplesMenuPrefix + "Locomotion - Spider Bot Controller")]
  16. [HelpURL(Strings.DocsURLs.ExampleAPIDocumentation + nameof(Locomotion) + "/" + nameof(SpiderBotController))]
  17. public sealed class SpiderBotController : MonoBehaviour
  18. {
  19. /************************************************************************************************************************/
  20. [SerializeField] private SpiderBot _SpiderBot;
  21. [SerializeField] private Rigidbody _Body;
  22. [SerializeField, DegreesPerSecond] private float _TurnSpeed = 90;
  23. [SerializeField, MetersPerSecond] private float _MovementSpeed = 1.5f;
  24. [SerializeField, Multiplier] private float _SprintMultiplier = 2;
  25. /************************************************************************************************************************/
  26. private MixerState<Vector2> _MoveState;
  27. private Vector3 _MovementDirection;
  28. /************************************************************************************************************************/
  29. private void Awake()
  30. {
  31. // _SpiderBot.Move is an ITransition which doesn't have a Parameter property for us to control in Update.
  32. // So we need to create its state, type cast it to MixerState<Vector2>, and store it in a field.
  33. // Then we will be able to control that field's Parameter in Update.
  34. var state = _SpiderBot.Animancer.States.GetOrCreate(_SpiderBot.Move);
  35. _MoveState = (MixerState<Vector2>)state;
  36. }
  37. /************************************************************************************************************************/
  38. private void Update()
  39. {
  40. // Calculate the movement direction.
  41. _MovementDirection = GetMovementDirection();
  42. // The bot should be moving whenever the direction isn't zero.
  43. _SpiderBot.IsMoving = _MovementDirection != default;
  44. // If the movement state is playing and not fading out:
  45. if (_MoveState.IsActive)
  46. {
  47. // Rotate towards the same angle around the Y axis as the camera.
  48. var eulerAngles = transform.eulerAngles;
  49. var targetEulerY = Camera.main.transform.eulerAngles.y;
  50. eulerAngles.y = Mathf.MoveTowardsAngle(eulerAngles.y, targetEulerY, _TurnSpeed * Time.deltaTime);
  51. transform.eulerAngles = eulerAngles;
  52. // The movement direction is in world space, so we need to convert it to the bot's local space to be
  53. // appropriate for its current rotation. We do this by using dot-products to determine how much of that
  54. // direction lies along each axis. This would be unnecessary if we did not rotate at all.
  55. _MoveState.Parameter = new Vector2(
  56. Vector3.Dot(transform.right, _MovementDirection),
  57. Vector3.Dot(transform.forward, _MovementDirection));
  58. // Set its speed depending on whether you are sprinting or not.
  59. _MoveState.Speed = ExampleInput.LeftMouseHold ? _SprintMultiplier : 1;
  60. }
  61. else// Otherwise stop it entirely.
  62. {
  63. _MoveState.Parameter = default;
  64. _MoveState.Speed = 0;
  65. }
  66. }
  67. /************************************************************************************************************************/
  68. private Vector3 GetMovementDirection()
  69. {
  70. // Get a ray from the main camera in the direction of the mouse cursor.
  71. var ray = Camera.main.ScreenPointToRay(ExampleInput.MousePosition);
  72. // Do a raycast with it and stop trying to move it it does not hit anything.
  73. // Note that this object is set to the Ignore Raycast layer so that the raycast will not hit it.
  74. if (!Physics.Raycast(ray, out var raycastHit))// Note the exclamation mark !
  75. return default;
  76. // If the ray hit something, calculate the horizontal direction from this object to that point.
  77. var direction = raycastHit.point - transform.position;
  78. direction.y = 0;
  79. // Calculate how far we could move this frame at max speed.
  80. var movementThisFrame = _MovementSpeed * _SprintMultiplier * Time.fixedDeltaTime;
  81. // If we are close to the destination, stop moving.
  82. var distance = direction.magnitude;
  83. if (distance <= movementThisFrame)
  84. {
  85. return default;
  86. }
  87. else
  88. {
  89. // Otherwise normalize the direction so that we do not change speed based on distance.
  90. // Calling direction.Normalize() would do the same thing, but would calculate the magnitude again.
  91. return direction / distance;
  92. }
  93. }
  94. /************************************************************************************************************************/
  95. private void FixedUpdate()
  96. {
  97. // Set the velocity so that Unity will move the Rigidbody in the desired direction.
  98. _Body.velocity = _MoveState.Speed * _MovementSpeed * _MovementDirection;
  99. }
  100. /************************************************************************************************************************/
  101. }
  102. }