using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class Draw2DNavigation : MonoBehaviour { [Header("Cible à zoomer / déplacer (UI root)")] public Transform Draw2D; [Header("Limites de zoom")] public float minScale = 0.3f; public float maxScale = 8f; [Header("Vitesses de zoom")] [SerializeField] private float mouseZoomSpeed = 0.3f; [SerializeField] private float pinchZoomSpeed = 0.002f; [Header("Vitesses de pan")] [SerializeField] private float mousePanSpeed = 1.0f; [SerializeField] private float touchPanSpeed = 1.0f; [Header("Zone FitToView (% du display : 1.0 = 100%)")] public float fitWidthPct = 0.9f; public float fitHeightPct = 0.75f; public static Draw2DNavigation _instance; private bool _isPanning; private Vector3 _lastPanPosition; private readonly List _raycastResults = new List(); void Awake() { _instance = this; } void Update() { HandleZoom(); HandlePan(); HandlePinchZoom(); } // ---------- ZOOM molette (PC) ---------- void HandleZoom() { float scroll = Input.GetAxis("Mouse ScrollWheel"); if (Mathf.Abs(scroll) > 0.0001f) ApplyZoomFactor(1f + scroll * mouseZoomSpeed); } // ---------- PAN : bouton gauche sur le fond, bouton droit partout, touch 1 doigt sur le fond ---------- // Sur WebGL, le touch est mappé en mouse button 0 par Unity → même code pour tout. void HandlePan() { bool pressing = Input.GetMouseButton(0) || Input.GetMouseButton(1); // Démarrage du pan if (!_isPanning && pressing) { bool rightClick = Input.GetMouseButton(1); bool onBackground = rightClick || IsPointerOnBackground(); if (onBackground && !WallCreationManager._isAddingWall) { _isPanning = true; _lastPanPosition = Input.mousePosition; } } // Pan en cours if (_isPanning && pressing) { // Annuler si une autre interaction a pris la main if (WallCreationManager._isAddingWall || WallPoint.AnyPointDragging || WallSelection.IsDragging) { _isPanning = false; return; } Vector3 delta = Input.mousePosition - _lastPanPosition; _lastPanPosition = Input.mousePosition; Pan(delta * mousePanSpeed); } // Fin du pan if (!pressing) _isPanning = false; } // ---------- PINCH ZOOM 2 DOIGTS (mobile natif uniquement) ---------- void HandlePinchZoom() { if (Input.touchCount != 2) return; Touch t0 = Input.GetTouch(0); Touch t1 = Input.GetTouch(1); float prevMag = ((t0.position - t0.deltaPosition) - (t1.position - t1.deltaPosition)).magnitude; float curMag = (t0.position - t1.position).magnitude; ApplyZoomFactor(1f + (curMag - prevMag) * pinchZoomSpeed); } // ---------- Détection du fond via RaycastAll (pas de timing EventSystem) ---------- bool IsPointerOnBackground() { // Sans EventSystem actif dans la scene, EventSystem.current est null : on ne peut pas // savoir ce qui est sous le curseur. On considere qu'on est sur le fond plutot que de // lancer une exception a chaque image. if (EventSystem.current == null) return true; _raycastResults.Clear(); PointerEventData ped = new PointerEventData(EventSystem.current) { position = Input.mousePosition }; EventSystem.current.RaycastAll(ped, _raycastResults); if (_raycastResults.Count == 0) return true; // hors de tout élément UI = fond return _raycastResults[0].gameObject.GetComponent() != null; } // ---------- ZOOM ---------- void ApplyZoomFactor(float factor) { if (Draw2D == null) return; float s = Mathf.Clamp(Draw2D.localScale.x * factor, minScale, maxScale); Draw2D.localScale = new Vector3(s, s, 1f); } // ---------- PAN ---------- public void Pan(Vector2 screenDelta) { if (Draw2D == null) return; Draw2D.position += new Vector3(screenDelta.x, screenDelta.y, 0f); } // ---------- FIT TO VIEW (bouton "Recentrer") ---------- public void FitToViewDelayed() { StartCoroutine(FitToViewNextFrame()); } private System.Collections.IEnumerator FitToViewNextFrame() { yield return null; yield return null; yield return null; FitToView(); } public void FitToView() { if (Draw2D == null) return; // Reset to screen-pixel baseline: position=(0,0)=bottom-left, scale=1 // In Screen Space Overlay, pt.transform.position == screen pixel coords at this state Draw2D.position = Vector3.zero; Draw2D.localScale = Vector3.one; UnityEngine.Canvas.ForceUpdateCanvases(); WallPoint[] points = Draw2D.GetComponentsInChildren(false); if (points.Length < 2) { Draw2D.localScale = new Vector3(1f, 1f, 1f); Draw2D.position = new Vector3(Screen.width * 0.5f, Screen.height * 0.5f, 0f); return; } Vector2 min = new Vector2(float.MaxValue, float.MaxValue); Vector2 max = new Vector2(float.MinValue, float.MinValue); foreach (WallPoint pt in points) { Vector2 pos = pt.transform.position; if (pos.x < min.x) min.x = pos.x; if (pos.y < min.y) min.y = pos.y; if (pos.x > max.x) max.x = pos.x; if (pos.y > max.y) max.y = pos.y; } Vector2 center = (min + max) * 0.5f; Vector2 boundsSize = max - min; if (boundsSize.x < 1f || boundsSize.y < 1f) return; float vpW = Screen.width * fitWidthPct; float vpH = Screen.height * fitHeightPct; const float padding = 0.9f; float s = Mathf.Clamp( Mathf.Min(vpW * padding / boundsSize.x, vpH * padding / boundsSize.y), minScale, maxScale); Draw2D.localScale = new Vector3(s, s, 1f); Draw2D.position = new Vector3( Screen.width * 0.5f - center.x * s, Screen.height * 0.5f - center.y * s, 0f); } }