Files
test/Assets/Plugins/Animancer/Examples/06 State Machines/05 Brain Transplants/MouseBrain.cs
T
2025-09-16 18:07:13 +07:00

125 lines
5.1 KiB
C#

// Animancer // https://kybernetik.com.au/animancer // Copyright 2021 Kybernetik //
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
using Animancer.FSM;
using Animancer.Units;
using UnityEngine;
namespace Animancer.Examples.StateMachines.Brains
{
/// <summary>A <see cref="CharacterBrain"/> which controls the character using mouse input.</summary>
/// <example><see href="https://kybernetik.com.au/animancer/docs/examples/fsm/brain-transplants">Brain Transplants</see></example>
/// https://kybernetik.com.au/animancer/api/Animancer.Examples.StateMachines.Brains/MouseBrain
///
[AddComponentMenu(Strings.ExamplesMenuPrefix + "Brains - Mouse Brain")]
[HelpURL(Strings.DocsURLs.ExampleAPIDocumentation + nameof(StateMachines) + "." + nameof(Brains) + "/" + nameof(MouseBrain))]
public sealed class MouseBrain : CharacterBrain
{
/************************************************************************************************************************/
[SerializeField] private CharacterState _Locomotion;
[SerializeField, Meters] private float _StopDistance = 0.2f;
[SerializeField, Meters] private float _MinRunDistance = 1;
private Vector3? _Destination;
/************************************************************************************************************************/
private void OnEnable()
{
_Destination = null;
}
/************************************************************************************************************************/
private void Update()
{
UpdateInput();
UpdateMovement();
}
/************************************************************************************************************************/
private void UpdateInput()
{
if (Input.GetMouseButton(0))
{
var characterPosition = Character.Rigidbody.position;
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out var raycastHit))
{
_Destination = raycastHit.point;
Debug.DrawLine(ray.origin, raycastHit.point, Color.green);
}
else
{
// If the ray does not hit anything, just use the point where it intersects
// the XZ plane at the height the character is currently at.
_Destination = CalculateRayTargetXZ(ray, characterPosition.y);
if (_Destination != null)
Debug.DrawLine(ray.origin, _Destination.Value, Color.red);
}
IsRunning =
_Destination != null &&
Vector3.Distance(characterPosition, _Destination.Value) >= _MinRunDistance;
}
else
{
IsRunning = false;
}
}
/************************************************************************************************************************/
public static Vector3? CalculateRayTargetXZ(Ray ray, float y = 0)
{
y = ray.origin.y - y;
// If the ray starts above the target and is pointing up then it will never intersect.
// Same if it is below and pointing down or if it is perfectly horizontal.
if (ray.direction.y == 0 || SameSign(y, ray.direction.y))
return null;
return ray.origin - ray.direction * (y / ray.direction.y);
}
public static bool SameSign(float x, float y)
{
if (x > 0) return y > 0;
else if (x < 0) return y < 0;
else return y == 0;
}
/************************************************************************************************************************/
private void UpdateMovement()
{
if (_Destination != null)
{
var fromCurrentToDestination = _Destination.Value - Character.Rigidbody.position;
// Vector magnitudes are calculated using Pythagoras' Theorem which involves a square root.
// Square roots are a much slower operation than simple arithmetic operations.
// Since we only need to see which is greater, we can compare the squared magnitude and stop distance.
if (fromCurrentToDestination.sqrMagnitude > _StopDistance * _StopDistance)
{
MovementDirection = fromCurrentToDestination;
_Locomotion.TryEnterState();
Debug.DrawLine(Character.Rigidbody.position, _Destination.Value, Color.cyan);
return;
}
}
_Destination = null;
MovementDirection = default;
Character.Idle.TryEnterState();
}
/************************************************************************************************************************/
}
}