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

The user is working on Unity C# code and encountering an issue on Android. They want to prevent building mode from being canceled when interacting with some UI elements like the construction panel, but still cancel it when tapping other UI components.

The key challenge involves modifying the IsPointerOverUI function or possibly using layers, Canvas groups, or tags to distinguish UI elements that should cancel building mode versus those that should be interactive, like the construction buttons.

To solve the issue, the user could modify the UI interaction logic. One approach is to use EventSystem.current.RaycastAll to check for UI elements, differentiating between those that should cancel building mode and those that shouldn't, like the construction buttons. They could add tags, layers, or a SelectableUI component to identify UI elements allowing building mode. Allocating exceptions through either tagged elements or raycasting adjustments can keep building interacting with the UI without cancelling.

The user is working on adapting their logic to keep the construction mode active when interacting with a specific UI panel (like the "ConstructionPanel"), without triggering a cancellation. A good solution would be to modify the IsPointerOverUI function to check if the touched UI element is part of the allowed panel. They could use a tag or layer to identify UI elements that should cancel the mode. Additionally, defining specific cancel behavior, like on-click functions for cancel buttons, would solve the issue.

Thought for 1m 28s

Respuesta

La idea es seguir usando el ray-cast sobre la UI para decidir cuándo hay que cancelar, pero excluir de esa decisión al panel de controles de construcción (y, si quieres, a cualquier otro widget que forme parte del flujo normal de “construir / confirmar / cancelar”).
Basta con filtrar los objetos que devuelve el raycast:

csharp
// -------- BuildingGridPlacer.cs -------- #region EXCLUSIÓN DE UI QUE NO DEBE CANCELAR // Asigna en el inspector la raíz del panel que NO debe provocar la salida del modo construcción [SerializeField] private RectTransform panelConstruccion; // &larr; “consB” /// <summary> /// Devuelve true cuando el toque/mouse está sobre una UI que *debe* cancelar el modo construcción. /// </summary> private bool IsPointerOverCancelUI(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 del panel de construcción? &rarr; entonces lo ignoramos if (panelConstruccion != null && r.gameObject.transform.IsChildOf(panelConstruccion)) return false; // toca una UI “permitida”; no cancelamos } return results.Count > 0; // toca otra UI cualquiera &rarr; sí cancelamos } #endregion

1. Cambios en la entrada móvil

Reemplaza la comprobación actual del principio de _HandleMobileInput():

csharp
//   ANTES if (touch.phase == TouchPhase.Began && IsPointerOverUI(touch.position)) { _CancelBuildingMode(); return; } //   DESPUÉS if (touch.phase == TouchPhase.Began && IsPointerOverCancelUI(touch.position)) { _CancelBuildingMode(); return; }

2. Cambios en la entrada PC

En _HandlePCInput() es el mismo concepto; sustituye:

csharp
if (EventSystem.current.IsPointerOverGameObject()) { if (Input.GetMouseButtonDown(0)) { _CancelBuildingMode(); return; } }

por

csharp
if (EventSystem.current.IsPointerOverGameObject()) { // Raycast explícito para saber si es una UI que cancela if (Input.GetMouseButtonDown(0) && IsPointerOverCancelUI(Input.mousePosition)) { _CancelBuildingMode(); return; } if (_toBuild.activeSelf) _toBuild.SetActive(false); return; }

3. Cómo configurarlo en el Inspector

  1. Pon tu panel consB (el contenedor raíz de los botones “Confirmar” y “Cancelar”) en el campo Panel Construccion del script.
  2. No necesitas dar tags ni layers especiales a cada botón; el filtro IsChildOf() detecta cualquier hijo de ese panel.

4. Resultado

  • Tocar cualquier parte del panel de construcción no sale del modo construcción; los botones continúan recibiendo sus clics o toques y disparan sus OnClick() normalmente.
  • Tocar cualquier otro elemento de UI (menús globales, HUD, etc.) sigue llamando a _CancelBuildingMode() como antes.
  • El comportamiento en escritorio y en Android queda unificado para que sea más fácil de mantener.

Con este pequeño filtro mantienes la funcionalidad original y, al mismo tiempo, permites que el usuario interactúe con los controles propios del flujo de construcción.

Compartir este Q&A