Files
ft/Client/Packages/io.github.hatayama.uloopmcp/Runtime/SimulateMouseInput/SimulateMouseInputOverlay.cs

150 lines
4.8 KiB
C#

#if UNITY_EDITOR
#nullable enable
using UnityEngine;
using UnityEngine.UI;
namespace io.github.hatayama.uLoopMCP
{
// Overlay that renders a mouse device icon on Game View and highlights
// pressed buttons / scroll direction during simulate-mouse-input tool calls.
public class SimulateMouseInputOverlay : MonoBehaviour
{
private const float DISPLAY_DURATION = 1.0f;
private static readonly Color BUTTON_PRESSED_COLOR = new Color(1f, 1f, 1f, 0.95f);
[SerializeField] private Image? _leftButton;
[SerializeField] private Image? _rightButton;
[SerializeField] private Image? _scrollWheel;
[SerializeField] private Image? _scrollArrowTop;
[SerializeField] private Image? _scrollArrowBottom;
[SerializeField] private RectTransform? _moveDirectionGroup;
private CanvasGroup _canvasGroup = null!;
private bool _isVisible;
private Color _leftButtonIdleColor;
private Color _rightButtonIdleColor;
private Color _scrollWheelIdleColor;
private void Awake()
{
_canvasGroup = GetComponent<CanvasGroup>();
if (_canvasGroup == null)
{
_canvasGroup = gameObject.AddComponent<CanvasGroup>();
}
_canvasGroup.interactable = false;
_canvasGroup.blocksRaycasts = false;
SetVisible(false);
CaptureIdleColors();
}
private void LateUpdate()
{
if (SimulateMouseInputOverlayState.HasAnyActivity)
{
if (!_isVisible)
{
SetVisible(true);
}
UpdateButtonColors();
UpdateScrollIndicator();
UpdateMoveDirection();
return;
}
float elapsed = Time.realtimeSinceStartup - SimulateMouseInputOverlayState.LastActivityTime;
if (SimulateMouseInputOverlayState.LastActivityTime <= 0f || elapsed > DISPLAY_DURATION)
{
if (_isVisible)
{
SetVisible(false);
}
return;
}
UpdateButtonColors();
UpdateScrollIndicator();
UpdateMoveDirection();
}
private void SetVisible(bool visible)
{
_isVisible = visible;
_canvasGroup.alpha = visible ? 1f : 0f;
}
private void CaptureIdleColors()
{
Debug.Assert(_leftButton != null, "_leftButton must be assigned before CaptureIdleColors");
Debug.Assert(_rightButton != null, "_rightButton must be assigned before CaptureIdleColors");
Debug.Assert(_scrollWheel != null, "_scrollWheel must be assigned before CaptureIdleColors");
_leftButtonIdleColor = _leftButton!.color;
_rightButtonIdleColor = _rightButton!.color;
_scrollWheelIdleColor = _scrollWheel!.color;
}
private void UpdateButtonColors()
{
_leftButton!.color = SimulateMouseInputOverlayState.IsLeftButtonHeld
? BUTTON_PRESSED_COLOR
: _leftButtonIdleColor;
_rightButton!.color = SimulateMouseInputOverlayState.IsRightButtonHeld
? BUTTON_PRESSED_COLOR
: _rightButtonIdleColor;
bool wheelActive = SimulateMouseInputOverlayState.IsMiddleButtonHeld
|| SimulateMouseInputOverlayState.ScrollDirection != 0;
_scrollWheel!.color = wheelActive ? BUTTON_PRESSED_COLOR : _scrollWheelIdleColor;
}
private void UpdateScrollIndicator()
{
int dir = SimulateMouseInputOverlayState.ScrollDirection;
if (dir == 0)
{
_scrollArrowTop!.enabled = false;
_scrollArrowBottom!.enabled = false;
return;
}
_scrollArrowTop!.enabled = true;
_scrollArrowBottom!.enabled = true;
float rotation = dir > 0 ? 0f : 180f;
_scrollArrowTop.rectTransform.localRotation = Quaternion.Euler(0f, 0f, rotation);
_scrollArrowBottom.rectTransform.localRotation = Quaternion.Euler(0f, 0f, rotation);
}
private void UpdateMoveDirection()
{
if (_moveDirectionGroup == null)
{
return;
}
Vector2 delta = SimulateMouseInputOverlayState.MoveDelta;
if (delta == Vector2.zero)
{
_moveDirectionGroup.gameObject.SetActive(false);
return;
}
_moveDirectionGroup.gameObject.SetActive(true);
float angle = Mathf.Atan2(delta.y, delta.x) * Mathf.Rad2Deg;
_moveDirectionGroup.localRotation = Quaternion.Euler(0f, 0f, angle);
}
}
}
#endif