290 lines
9.3 KiB
C#
290 lines
9.3 KiB
C#
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.UI;
|
|
using Unheimlich.Gameplay.DetectiveBoard;
|
|
|
|
namespace Unheimlich.UI.DetectiveBoard
|
|
{
|
|
/// <summary>
|
|
/// Handles mouse interaction for the detective board World Space canvas.
|
|
/// Manages hover, selection, dragging, and click forwarding for Post-It nodes.
|
|
/// Interactions are only active when the board is in inspection mode.
|
|
/// </summary>
|
|
public class DetectiveBoardInteraction : MonoBehaviour
|
|
{
|
|
[Header("References")]
|
|
[SerializeField] private Camera _camera;
|
|
[SerializeField] private Canvas _boardCanvas;
|
|
[SerializeField] private RectTransform _boardArea;
|
|
[SerializeField] private LayerMask _boardLayer;
|
|
[SerializeField] private DetectiveBoardController _boardController;
|
|
|
|
[Header("Settings")]
|
|
[SerializeField] private float _maxInteractionDistance = 5f;
|
|
|
|
private DetectiveBoardCanvas _canvasManager;
|
|
private PostItNode _hoveredNode;
|
|
private PostItNode _selectedNode;
|
|
private bool _isDragging;
|
|
private Plane _canvasPlane;
|
|
private Vector3 _dragOffset;
|
|
|
|
private void Start()
|
|
{
|
|
if (_camera == null)
|
|
_camera = Camera.main;
|
|
|
|
if (_boardController == null)
|
|
_boardController = FindFirstObjectByType<DetectiveBoardController>();
|
|
|
|
if (_canvasManager == null)
|
|
_canvasManager = GetComponent<DetectiveBoardCanvas>();
|
|
|
|
UpdateCanvasPlane();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!IsInBoardInteractionMode())
|
|
{
|
|
ClearInteractionState();
|
|
return;
|
|
}
|
|
|
|
UpdateCanvasPlane();
|
|
|
|
if (_isDragging)
|
|
{
|
|
HandleDrag();
|
|
}
|
|
else
|
|
{
|
|
HandleHover();
|
|
}
|
|
|
|
HandleClick();
|
|
}
|
|
|
|
private bool IsInBoardInteractionMode()
|
|
{
|
|
return _boardController != null && _boardController.IsBoardActive;
|
|
}
|
|
|
|
private void ClearInteractionState()
|
|
{
|
|
if (_hoveredNode != null)
|
|
{
|
|
_hoveredNode.SetHovered(false);
|
|
_hoveredNode = null;
|
|
}
|
|
|
|
if (_selectedNode != null && _isDragging)
|
|
{
|
|
_selectedNode.SetSelected(false);
|
|
_selectedNode = null;
|
|
_isDragging = false;
|
|
}
|
|
}
|
|
|
|
private void UpdateCanvasPlane()
|
|
{
|
|
if (_boardCanvas != null)
|
|
{
|
|
Vector3 planeNormal = -_boardCanvas.transform.forward;
|
|
Vector3 planePosition = _boardCanvas.transform.position;
|
|
_canvasPlane = new Plane(planeNormal, planePosition);
|
|
}
|
|
}
|
|
|
|
private void HandleHover()
|
|
{
|
|
PostItNode hitNode = RaycastForNode();
|
|
|
|
if (hitNode != _hoveredNode)
|
|
{
|
|
if (_hoveredNode != null)
|
|
{
|
|
_hoveredNode.SetHovered(false);
|
|
}
|
|
|
|
_hoveredNode = hitNode;
|
|
|
|
if (_hoveredNode != null)
|
|
{
|
|
_hoveredNode.SetHovered(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HandleClick()
|
|
{
|
|
if (Mouse.current == null)
|
|
return;
|
|
|
|
PostItNode hitNode = RaycastForNode();
|
|
|
|
if (Mouse.current.leftButton.wasPressedThisFrame)
|
|
{
|
|
HandleLeftClick(hitNode);
|
|
}
|
|
else if (Mouse.current.rightButton.wasPressedThisFrame && hitNode != null && _canvasManager != null)
|
|
{
|
|
_canvasManager.OnNodeRightClicked(hitNode);
|
|
}
|
|
}
|
|
|
|
private void HandleLeftClick(PostItNode hitNode)
|
|
{
|
|
if (_canvasManager != null && _canvasManager.IsConnectMode)
|
|
{
|
|
if (hitNode != null)
|
|
{
|
|
_canvasManager.OnNodeClicked(hitNode);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (_isDragging)
|
|
{
|
|
_isDragging = false;
|
|
|
|
if (_selectedNode != null && hitNode == _selectedNode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_selectedNode != null)
|
|
{
|
|
_selectedNode.SetSelected(false);
|
|
}
|
|
|
|
if (hitNode != null)
|
|
{
|
|
_selectedNode = hitNode;
|
|
_selectedNode.SetSelected(true);
|
|
_isDragging = true;
|
|
CalculateDragOffset();
|
|
}
|
|
else
|
|
{
|
|
_selectedNode = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hitNode != null)
|
|
{
|
|
if (_selectedNode != null && _selectedNode != hitNode)
|
|
{
|
|
_selectedNode.SetSelected(false);
|
|
}
|
|
|
|
_selectedNode = hitNode;
|
|
_selectedNode.SetSelected(true);
|
|
_isDragging = true;
|
|
CalculateDragOffset();
|
|
}
|
|
else
|
|
{
|
|
if (_selectedNode != null)
|
|
{
|
|
_selectedNode.SetSelected(false);
|
|
_selectedNode = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HandleDrag()
|
|
{
|
|
if (_selectedNode == null || _camera == null)
|
|
return;
|
|
|
|
Vector2 mousePos = Mouse.current.position.ReadValue();
|
|
Ray ray = _camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0));
|
|
|
|
if (_canvasPlane.Raycast(ray, out float enter))
|
|
{
|
|
Vector3 worldHitPoint = ray.GetPoint(enter);
|
|
Vector3 newWorldPos = worldHitPoint + _dragOffset;
|
|
|
|
Vector3 localPos = _boardCanvas.transform.InverseTransformPoint(newWorldPos);
|
|
Vector2 clampedPos = ClampPositionToBoard(new Vector2(localPos.x, localPos.y));
|
|
|
|
_selectedNode.SetPosition(clampedPos);
|
|
|
|
if (_canvasManager != null)
|
|
{
|
|
_canvasManager.UpdateConnectionsForNode(_selectedNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
private Vector2 ClampPositionToBoard(Vector2 position)
|
|
{
|
|
if (_boardArea == null)
|
|
return position;
|
|
|
|
Rect boardRect = _boardArea.rect;
|
|
float halfWidth = boardRect.width * 0.5f;
|
|
float halfHeight = boardRect.height * 0.5f;
|
|
|
|
float clampedX = Mathf.Clamp(position.x, -halfWidth, halfWidth);
|
|
float clampedY = Mathf.Clamp(position.y, -halfHeight, halfHeight);
|
|
|
|
return new Vector2(clampedX, clampedY);
|
|
}
|
|
|
|
private void CalculateDragOffset()
|
|
{
|
|
if (_selectedNode == null || _camera == null)
|
|
return;
|
|
|
|
Vector2 mousePos = Mouse.current.position.ReadValue();
|
|
Ray ray = _camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0));
|
|
|
|
if (_canvasPlane.Raycast(ray, out float enter))
|
|
{
|
|
Vector3 worldHitPoint = ray.GetPoint(enter);
|
|
Vector3 currentWorldPos = _selectedNode.transform.position;
|
|
_dragOffset = currentWorldPos - worldHitPoint;
|
|
}
|
|
}
|
|
|
|
private PostItNode RaycastForNode()
|
|
{
|
|
if (_camera == null)
|
|
return null;
|
|
|
|
Vector2 mousePos = Mouse.current.position.ReadValue();
|
|
Ray ray = _camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0));
|
|
|
|
if (Physics.Raycast(ray, out RaycastHit hit, _maxInteractionDistance, _boardLayer))
|
|
{
|
|
return hit.collider.GetComponentInParent<PostItNode>();
|
|
}
|
|
|
|
GraphicRaycaster raycaster = _boardCanvas.GetComponent<GraphicRaycaster>();
|
|
if (raycaster != null)
|
|
{
|
|
var pointerEventData = new UnityEngine.EventSystems.PointerEventData(UnityEngine.EventSystems.EventSystem.current)
|
|
{
|
|
position = mousePos
|
|
};
|
|
|
|
var results = new System.Collections.Generic.List<UnityEngine.EventSystems.RaycastResult>();
|
|
raycaster.Raycast(pointerEventData, results);
|
|
|
|
foreach (var result in results)
|
|
{
|
|
PostItNode node = result.gameObject.GetComponentInParent<PostItNode>();
|
|
if (node != null)
|
|
return node;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|