#if GRAPH_DESIGNER /// --------------------------------------------- /// Behavior Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.TransformTasks { using Opsive.GraphDesigner.Runtime.Variables; using UnityEngine; [Opsive.Shared.Utility.Category("Transform")] [Opsive.Shared.Utility.Description("Follows a target GameObject with configurable distance, offset, and smoothing. Can optionally look at the target.")] public class FollowTarget : TargetGameObjectAction { [Tooltip("The GameObject to follow.")] [SerializeField] protected SharedVariable m_Target; [Tooltip("The desired distance to maintain from the target. Set to 0 to follow directly.")] [SerializeField] protected SharedVariable m_FollowDistance = 0f; [Tooltip("The offset from the target position.")] [SerializeField] protected SharedVariable m_Offset = Vector3.zero; [Tooltip("The follow speed.")] [SerializeField] protected SharedVariable m_FollowSpeed = 5f; [Tooltip("The smooth damping time. Lower values = faster response.")] [SerializeField] protected SharedVariable m_SmoothTime = 0.3f; [Tooltip("The height offset from the target.")] [SerializeField] protected SharedVariable m_HeightOffset = 0f; [Tooltip("Should the Transform look at the target?")] [SerializeField] protected SharedVariable m_LookAtTarget = false; [Tooltip("The rotation speed when looking at target. Only used if Look At Target is enabled.")] [SerializeField] protected SharedVariable m_RotationSpeed = 5f; [Tooltip("The up vector for look at rotation. Only used if Look At Target is enabled.")] [SerializeField] protected SharedVariable m_UpVector = Vector3.up; [Tooltip("The task will complete when the position is within this distance of the desired follow position.")] [SerializeField] protected SharedVariable m_PositionArrivedDistance = 0.1f; [Tooltip("The task will complete when the rotation is within this angle of the desired look rotation.")] [SerializeField] protected SharedVariable m_RotationArrivedAngle = 1f; private Vector3 m_CurrentVelocity; /// /// Called when the state machine starts. /// public override void OnStart() { base.OnStart(); m_CurrentVelocity = Vector3.zero; } /// /// Follows the target. /// /// The status of the action. public override TaskStatus OnUpdate() { if (m_Target.Value == null) { return TaskStatus.Failure; } var targetPosition = m_Target.Value.transform.position; var offset = m_Offset.Value; offset.y += m_HeightOffset.Value; // Calculate desired position. Vector3 desiredPosition; if (m_FollowDistance.Value > 0f) { var direction = (transform.position - targetPosition).normalized; desiredPosition = targetPosition + direction * m_FollowDistance.Value + offset; } else { desiredPosition = targetPosition + offset; } // Smoothly move towards desired position. transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref m_CurrentVelocity, m_SmoothTime.Value, m_FollowSpeed.Value, Time.deltaTime); var positionArrived = Vector3.Distance(transform.position, desiredPosition) <= m_PositionArrivedDistance.Value; if (positionArrived) { transform.position = desiredPosition; } // Optionally look at target. var rotationArrived = true; if (m_LookAtTarget.Value) { var targetPos = m_Target.Value.transform.position; var direction = (targetPos - transform.position).normalized; if (direction != Vector3.zero) { var targetRotation = Quaternion.LookRotation(direction, m_UpVector.Value); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, m_RotationSpeed.Value * Time.deltaTime); rotationArrived = Quaternion.Angle(transform.rotation, targetRotation) <= m_RotationArrivedAngle.Value; if (rotationArrived) { transform.rotation = targetRotation; } } } return positionArrived && rotationArrived ? TaskStatus.Success : TaskStatus.Running; } /// /// Resets the action values back to their default. /// public override void Reset() { base.Reset(); m_Target = null; m_FollowDistance = 0f; m_Offset = Vector3.zero; m_FollowSpeed = 5f; m_SmoothTime = 0.3f; m_HeightOffset = 0f; m_LookAtTarget = false; m_RotationSpeed = 5f; m_UpVector = Vector3.up; m_PositionArrivedDistance = 0.1f; m_RotationArrivedAngle = 1f; } } } #endif