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

Creado el: 26 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)) { 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(); IsPlacingBuilding = false; } } } } 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 (EventSystem.current.IsPointerOverGameObject(touch.fingerId)) { _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 _CancelBuildingMode() { if (_toBuild != null) Destroy(_toBuild); _toBuild = null; _buildingPrefab = null; _EnableGridVisual(false); script0UI.OcultarIndicaciones(); IsPlacingBuilding = false; } protected override void _PrepareBuilding() { base._PrepareBuilding(); _EnableGridVisual(true); scriptGlobalUI.CloseGlobalPanels(); script0UI.MostrarIndicaciones(); 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); }

}


porque en android, al interactuar con la UI, no se cancela el modo construccion??

Pensamiento

The user is facing an issue where construction mode isn't canceled on Android when interacting with the UI. This could be due to how they're using EventSystem.current.IsPointerOverGameObject(touch.fingerId) in _HandleMobileInput. On mobile, this check only works when there's a UI element with a raycast target. They may need to adjust their call or ensure that a GraphicRaycaster and EventSystem are in place. On PC, it's simpler, as it uses IsPointerOverGameObject() without the fingerId.

The user is dealing with an issue where building mode isn't canceled when interacting with the UI on Android. It seems that IsPointerOverGameObject(touch.fingerId) only works when the touch is at the start of an interaction, and the check may need to happen during the TouchPhase.Began. The problem might also stem from the UI Canvas using Screen Space - Camera, or the Raycast Target not being enabled. Another factor could be the use of the new Input System or conflicts with multiple touches. I think calling IsPointerOverGameObject after a touch begins could help.

The issue seems to be with how touch input is handled in Unity. On Android, EventSystem.current.IsPointerOverGameObject(touch.fingerId) isn't detecting UI touches properly. It could be due to missing Raycasters or relying on StandaloneInputModule without a correctly set pointerId. A potential fix is to ensure both StandaloneInputModule and TouchInputModule are used together, or switch the input mode. Also, UI elements must have RaycastTarget enabled. Additionally, using a custom method to detect UI interaction during touch (using PointerEventData) might help.

Thought for 1m 6s

Respuesta

En Android el método que empleas para detectar que el dedo está sobre la UI nunca llega a devolver true, de modo que _CancelBuildingMode() no se llama.
La causa más habitual es esta:

Qué ocurrePor qué
EventSystem.current.IsPointerOverGameObject(touch.fingerId) devuelve falseCon el módulo Standalone Input Module (el que Unity crea por defecto) todas las pulsaciones táctiles se tratan como un único puntero con pointerId = -1 (el “ratón”). No se crean datos de puntero individuales para cada fingerId, así que buscar el puntero 0, 1, 2… siempre falla en el dispositivo.

Soluciones

  1. Usar la sobrecarga sin parámetro (o con -1) para móviles

    csharp
    bool overUI = EventSystem.current.IsPointerOverGameObject(); // ó …(-1)

    Esto funciona tanto en PC como en Android con el StandaloneInputModule.

  2. Añadir un módulo táctil real

    • Sustituye el Standalone Input Module por un Touch Input Module (o Input System UI Input Module si usas el nuevo sistema de entrada).
    • Entonces sí podrás llamar a IsPointerOverGameObject(touch.fingerId).
  3. Crear tu propio helper multiplataforma (más robusto, no depende del módulo):

    csharp
    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; }

    Uso en tu código móvil:

    csharp
    Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began && IsPointerOverUI(touch.position)) { _CancelBuildingMode(); return; }

Cambios mínimos en tu script

csharp
private void _HandleMobileInput() { if (Input.touchCount == 0) return; Touch touch = Input.GetTouch(0); // Comprueba la UI SOLO en TouchPhase.Began y sin fingerId if (touch.phase == TouchPhase.Began && EventSystem.current.IsPointerOverGameObject()) // <- cambio { _CancelBuildingMode(); return; } }

Con cualquiera de estas opciones el toque sobre un botón / panel en Android se detectará correctamente y el modo de construcción se cancelará igual que en PC.

Compartir este Q&A