using System.Collections; using System.Collections...

Creado el: 28 de mayo de 2025

Respondido usando O3 por Chat01

Pregunta

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class BuildingGridPlacer : BuildingPlacer
{
public static bool IsPlacingBuilding = false;

text
public globalControl scriptGlobalUI; public ap0Control script0UI; public Vector2 cellSize = new Vector2(1, 1); public Vector2 gridOffset; public Renderer gridRenderer; public bool isIsometric = false; private bool isMobile; private bool isDragging = false; private Vector2 lastTouchPosition;

#if UNITY_EDITOR
private void OnValidate()
{
_UpdateGridVisual();
}
#endif

text
private void Start() { isMobile = Application.isMobilePlatform; _UpdateGridVisual(); _EnableGridVisual(false); } private void Update() { if (_buildingPrefab != null) { if (isMobile) { _HandleMobileInput(); } else { _HandlePCInput(); } } } private void _HandlePCInput() { if (_buildingPrefab != null) { if (Input.GetMouseButtonDown(1)) { _CancelBuildingMode(); return; } if (EventSystem.current.IsPointerOverGameObject()) { if (Input.GetMouseButtonDown(0)) { _CancelBuildingMode(); return; } if (_toBuild.activeSelf) _toBuild.SetActive(false); return; } else if (!_toBuild.activeSelf) _toBuild.SetActive(true); if (Input.GetKeyDown(KeyCode.Space)) { // esta madre no hace nada } Vector3 mouseWorldPos3D = Camera.main.ScreenToWorldPoint(Input.mousePosition); Vector2 mouseWorldPos = new Vector2(mouseWorldPos3D.x, mouseWorldPos3D.y); Vector2 snappedPos = isIsometric ? _ClampToNearestIsometric(mouseWorldPos, cellSize) : _ClampToNearest(mouseWorldPos, cellSize); _toBuild.transform.position = new Vector3(snappedPos.x, snappedPos.y, _toBuild.transform.position.z); foreach (SpriteRenderer sr in _toBuild.GetComponent<BuildingManager>().spriteRenderers) { sr.sortingOrder = _CalculateSortingOrder(sr.transform.position) + 1000; } if (Input.GetMouseButtonDown(0)) { _InstanceBuilding(); return; } } } private void _HandleMobileInput() { if (Input.touchCount > 0) { Touch touch = Input.GetTouch(0); // Si el usuario toca (tap) sobre la UI, cancelar modo construcción if (touch.phase == TouchPhase.Began && IsPointerOverUI(touch.position)) { _CancelBuildingMode(); return; } // Siempre mostrar el preview mientras esté en modo construcción if (!_toBuild.activeSelf) _toBuild.SetActive(true); Vector3 touchWorldPos3D = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, Camera.main.nearClipPlane)); Vector2 touchWorldPos = new Vector2(touchWorldPos3D.x, touchWorldPos3D.y); switch (touch.phase) { case TouchPhase.Began: isDragging = true; lastTouchPosition = touchWorldPos; Vector2 snappedStartPos = isIsometric ? _ClampToNearestIsometric(touchWorldPos, cellSize) : _ClampToNearest(touchWorldPos, cellSize); _toBuild.transform.position = new Vector3(snappedStartPos.x, snappedStartPos.y, _toBuild.transform.position.z); break; case TouchPhase.Moved: if (isDragging) { Vector2 snappedPos = isIsometric ? _ClampToNearestIsometric(touchWorldPos, cellSize) : _ClampToNearest(touchWorldPos, cellSize); _toBuild.transform.position = new Vector3(snappedPos.x, snappedPos.y, _toBuild.transform.position.z); } break; case TouchPhase.Ended: case TouchPhase.Canceled: isDragging = false; break; } } } private void _InstanceBuilding() { if (_toBuild != null) { BuildingManager m = _toBuild.GetComponent<BuildingManager>(); if (m.hasValidPlacement) { m.SetPlacementMode(PlacementMode.Fixed); foreach (SpriteRenderer sr in m.spriteRenderers) { sr.sortingOrder = _CalculateSortingOrder(sr.transform.position); } _buildingPrefab = null; _toBuild = null; _EnableGridVisual(false); script0UI.OcultarIndicaciones(); script0UI.BotonesConsOff(); IsPlacingBuilding = false; } } } private void _CancelBuildingMode() { if (_toBuild != null) Destroy(_toBuild); _toBuild = null; _buildingPrefab = null; _EnableGridVisual(false); script0UI.OcultarIndicaciones(); script0UI.BotonesConsOff(); IsPlacingBuilding = false; } protected override void _PrepareBuilding() { base._PrepareBuilding(); _EnableGridVisual(true); scriptGlobalUI.CloseGlobalPanels(); script0UI.MostrarIndicaciones(); #if UNITY_ANDROID script0UI.BotonesConsOn(); #endif IsPlacingBuilding = true; } private Vector2 _ClampToNearest(Vector2 pos, Vector2 cell) { Vector2 snapped = new Vector2( Mathf.Floor(pos.x / cell.x) * cell.x + (cell.x * 0.5f) + gridOffset.x, Mathf.Floor(pos.y / cell.y) * cell.y + (cell.y * 0.5f) + gridOffset.y ); return snapped; } private Vector2 _ClampToNearestIsometric(Vector2 pos, Vector2 cell) { // Medidas de medio tile float halfW = cell.x * 0.5f; float halfH = cell.y * 0.5f; // Convertir posición mundo a "coordenadas de rejilla" float isoX = (pos.x / halfW + pos.y / halfH) * 0.5f; float isoY = (pos.y / halfH - pos.x / halfW) * 0.5f; // Snap a entero int gridX = Mathf.RoundToInt(isoX); int gridY = Mathf.RoundToInt(isoY); // Volver a convertir a coordenadas mundo float worldX = (gridX - gridY) * halfW; float worldY = (gridX + gridY) * halfH; return new Vector2(worldX + gridOffset.x, worldY + gridOffset.y); } private void _EnableGridVisual(bool on) { if (gridRenderer == null) return; gridRenderer.gameObject.SetActive(on); } private void _UpdateGridVisual() { if (gridRenderer == null) return; gridRenderer.sharedMaterial.SetVector("_Cell_Size", new Vector4(cellSize.x, cellSize.y, 0, 0)); } private int _CalculateSortingOrder(Vector3 position) { return Mathf.RoundToInt(-position.y * 100); } private bool IsPointerOverUI(Vector2 screenPos) { PointerEventData data = new PointerEventData(EventSystem.current) { position = screenPos }; List<RaycastResult> results = new List<RaycastResult>(); EventSystem.current.RaycastAll(data, results); return results.Count > 0; }

}


En android, cuando el jugador interactua con el panel que contiene los botones para colocar y cancelar (consB, activado con la linea script0UI.BotonesConsON(); al entrar en el modo construccion) el modo construccion se cancela automaticamente porque esta programado que se cancele al interactuar con un elemento de la UI.
Como hago para mantener esta funcion, pero permitiendo que el panel se pueda interactuar sin cancelar el modo construccion?

Pensamiento

Analyzing construction mode issue

The user wants to keep the existing functionality where construction mode is canceled when interacting with UI components, but not when interacting with specific elements (like the "place" and "cancel" buttons). In Android, they're using _HandleMobileInput to check for touches over UI, triggering cancellation. Similarly, on PC, _HandlePCInput uses EventSystem to cancel when the mouse clicks UI. The goal is to find a way to exempt certain buttons (such as consB) from this check to maintain the UI's interactivity without canceling the mode.

Finding a solution for panel interaction

The user wants to prevent the building mode from cancelling when interacting with certain panels. One approach could be to modify the pointer check, allowing interactions with specific UI elements (like the building panel) without triggering the cancellation. Options include using CanvasGroup with blocksRaycasts set to false, layering event detection, or filtering using EventSystem to check if the pointer is over an allowed UI. They want to maintain interaction with UI buttons while preventing other menus from canceling building mode.

Thought for 25 seconds

Respuesta

Para conservar la regla general de “si toco la UI se cancela el modo construcción” pero hacer una excepción con el panel BotonesCons (consB), basta con filtrar los resultados del raycast y solo cancelar cuando el toque/clic NO esté sobre ese panel (o alguno de sus hijos).

1. Expón en el inspector qué parte de la UI quieres “permitir”

csharp
public RectTransform panelConstruccion; // arrástralo en el Inspector (el RectTransform raíz de consB)

Si lo prefieres puedes usar un tag o layer en vez de un RectTransform; el principio es el mismo: identificar la UI que no debe cancelar el modo construcción.


2. Sustituye tu método genérico por una versión que sepa “distinguir”

csharp
// Devuelve true solo si se está sobre UI *que no sea* el panel de construcción private bool IsPointerOverForCancel(Vector2 screenPos) { PointerEventData data = new PointerEventData(EventSystem.current) { position = screenPos }; List<RaycastResult> results = new List<RaycastResult>(); EventSystem.current.RaycastAll(data, results); foreach (var r in results) { // ¿Es hijo (o el mismo) del panel que queremos permitir? if (panelConstruccion != null && r.gameObject.transform.IsChildOf(panelConstruccion)) { // Está sobre la UI "permitida" &rarr; NO cancelar return false; } } // Si llegó aquí hay UI debajo, pero no es la “permitida” return results.Count > 0; }

3. Usa este nuevo filtro tanto en PC como en móvil

csharp
// PC if (Input.GetMouseButtonDown(0) && IsPointerOverForCancel(Input.mousePosition)) { _CancelBuildingMode(); return; } // MÓVIL if (touch.phase == TouchPhase.Began && IsPointerOverForCancel(touch.position)) { _CancelBuildingMode(); return; }

4. Qué ganas con esto

  • La regla global se mantiene: cualquier toque/clic sobre otro elemento de la UI sigue cerrando el modo construcción.
  • El panel consB es interactuable: puedes pulsar sus botones (confirmar, rotar, cancelar, etc.) sin que el sistema se autodestruya antes de que el botón reciba el OnClick.
  • No necesitas cambiar layers, raycastTarget, ni duplicar canvases; solo filtras el resultado del raycast con una condición clara.

Consejo
Si acabas teniendo varios paneles “permitidos”, guarda todos en una List<RectTransform> y en el foreach comprueba si el RaycastResult pertenece a cualquiera de ellos (results[i].gameObject.transform.IsChildOf(panel)).

Con este pequeño filtro tu flujo se mantiene limpio y controlado, tanto en Android como en el editor. ¡Listo!

Compartir este Q&A