using UnityEngine;
namespace HQFPSWeapons
{
///
/// Sends a ray from the center of the camera, in the game world.
/// Gathers data about what is in front of the player, and stores it in a variable.
///
public class PlayerInteraction : PlayerComponent
{
[SerializeField]
private Camera m_WorldCamera = null;
[SerializeField]
[Tooltip("The maximum distance at which you can interact with objects.")]
private float m_InteractionDistance = 2f;
[SerializeField]
[Range(0f, 60f)]
private float m_MaxInteractionAngle = 30f;
[SerializeField]
private LayerMask m_LayerMask = new LayerMask();
private InteractiveObject m_InteractedObject;
private void Awake()
{
Player.WantsToInteract.AddChangeListener(OnChanged_WantsToInteract);
}
private void OnChanged_WantsToInteract(bool wantsToInteract)
{
var raycastData = Player.RaycastData.Get();
var wantedToInteractPreviously = Player.WantsToInteract.GetPreviousValue();
var wantsToInteractNow = wantsToInteract;
if(raycastData != null && raycastData.IsInteractive)
{
if(!wantedToInteractPreviously && wantsToInteractNow)
{
raycastData.InteractiveObject.OnInteractionStart(Player);
m_InteractedObject = raycastData.InteractiveObject;
}
}
if(m_InteractedObject != null && wantedToInteractPreviously && !wantsToInteractNow)
{
m_InteractedObject.OnInteractionEnd(Player);
m_InteractedObject = null;
}
}
private void Update()
{
var ray = m_WorldCamera.ViewportPointToRay(Vector2.one * 0.5f);
var lastRaycastData = Player.RaycastData.Get();
var collidersInRange = Physics.OverlapSphere(m_WorldCamera.transform.position, m_InteractionDistance, m_LayerMask, QueryTriggerInteraction.Collide);
float smallestAngle = 1000f;
InteractiveObject closestObject = null;
int closestObjectIndex = -1;
Vector3 cameraPosition = m_WorldCamera.transform.position;
Vector3 cameraDirection = m_WorldCamera.transform.forward;
for(int i = 0;i < collidersInRange.Length; i ++)
{
InteractiveObject interactiveObject = collidersInRange[i].GetComponent();
RaycastHit hitInfo;
if(interactiveObject != null && Physics.Linecast(cameraPosition, interactiveObject.transform.position + (interactiveObject.transform.position - cameraPosition).normalized * 0.05f, out hitInfo, m_LayerMask))
{
if(hitInfo.collider == null || hitInfo.collider == collidersInRange[i])
{
float angle = Vector3.Angle(cameraDirection, interactiveObject.transform.position - cameraPosition);
if(angle < smallestAngle)
{
smallestAngle = angle;
closestObject = interactiveObject;
closestObjectIndex = i;
}
}
}
}
if(smallestAngle < m_MaxInteractionAngle && ((lastRaycastData != null && lastRaycastData.Collider != collidersInRange[closestObjectIndex]) || lastRaycastData == null))
{
var raycastData = new RaycastData(collidersInRange[closestObjectIndex], closestObject);
Player.RaycastData.Set(raycastData);
bool startedRaycastingOnObject =
lastRaycastData != null &&
raycastData.IsInteractive &&
raycastData.InteractiveObject != lastRaycastData.InteractiveObject;
if(startedRaycastingOnObject)
raycastData.InteractiveObject.OnRaycastStart(Player);
else if(raycastData.IsInteractive)
raycastData.InteractiveObject.OnRaycastUpdate(Player);
else if(lastRaycastData != null && lastRaycastData.InteractiveObject != null)
lastRaycastData.InteractiveObject.OnRaycastEnd(Player);
}
else if(smallestAngle > m_MaxInteractionAngle)
{
Player.RaycastData.Set(null);
// Let the object know the ray it's not on it anymore.
if(lastRaycastData != null && lastRaycastData.IsInteractive)
{
if(lastRaycastData.IsInteractive)
lastRaycastData.InteractiveObject.OnRaycastEnd(Player);
}
}
// if(Physics.Raycast(ray, out hitInfo, m_InteractionDistance, m_LayerMask, QueryTriggerInteraction.Collide) && ((lastRaycastData != null && lastRaycastData.HitInfo.collider != hitInfo.collider) || lastRaycastData == null))
// {
// var raycastData = new RaycastData(hitInfo);
// Player.RaycastData.Set(raycastData);
//
// bool startedRaycastingOnObject =
// lastRaycastData != null &&
// raycastData.IsInteractive &&
// raycastData.InteractiveObject != lastRaycastData.InteractiveObject;
//
// if(startedRaycastingOnObject)
// raycastData.InteractiveObject.OnRaycastStart(Player);
// else if(raycastData.IsInteractive)
// raycastData.InteractiveObject.OnRaycastUpdate(Player);
// else if(lastRaycastData != null && lastRaycastData.InteractiveObject != null)
// lastRaycastData.InteractiveObject.OnRaycastEnd(Player);
// }
// else if(hitInfo.collider == null)
// {
// Player.RaycastData.Set(null);
//
// // Let the object know the ray it's not on it anymore.
// if(lastRaycastData != null && lastRaycastData.IsInteractive)
// {
// if(lastRaycastData.IsInteractive)
// lastRaycastData.InteractiveObject.OnRaycastEnd(Player);
// }
// }
// bool objectInProximity = Physics.CheckSphere(transform.position + transform.TransformVector(m_ProximityDetection.Offset), m_ProximityDetection.Radius, m_ProximityDetection.Mask);
//
// Player.ObjectInProximity.Set(objectInProximity);
if(m_InteractedObject != null)
m_InteractedObject.OnInteractionUpdate(Player);
}
// private void OnDrawGizmosSelected()
// {
// Gizmos.color = Color.red;
// Gizmos.DrawWireSphere(transform.position + transform.TransformVector(m_ProximityDetection.Offset), m_ProximityDetection.Radius);
// }
// // -------------------- Internal --------------------
// [Serializable]
// public struct ProximityDetection
// {
// public Vector3 Offset;
//
// [Range(0f, 2f)]
// public float Radius;
//
// public LayerMask Mask;
// }
}
}