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

Creado el: 2 de junio 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; } public void InstanceBuilding() { _InstanceBuilding(); } public void CancelBuilding() { _CancelBuildingMode(); } protected override void _PrepareBuilding() { base._PrepareBuilding(); _EnableGridVisual(true); scriptGlobalUI.CloseGlobalPanels(); script0UI.MostrarIndicaciones();

#if UNITY_ANDROID
script0UI.BotonesConsOn();
#endif
IsPlacingBuilding = true;
}

text
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); foreach (var res in results) { Transform t = res.gameObject.transform; bool isBuildUI = false; while (t != null) { if (t.CompareTag("BuildUI")) { isBuildUI = true; break; } t = t.parent; } if (isBuildUI) continue; return true; } return false; }

}


ahora mismo, el script cancela el modo construcción si el usuario hace interactua con la UI, excepto si lo hace sobre la BuildUI (botones de construccion). Pero tambien requiero que si el usuario interactua con la BuildUI, el preview del edificio no se coloque donde el usuario hizo tap (osea, donde esta la BuildUI), sino que se quede en la ultima posicion donde el usuario hizo tap (para que el usuario pueda interactuar con los botones de construccion sin mover el preview de donde lo dejo)

Pensamiento

Analyzing user issue

The user’s Unity C# script cancels building mode when interacting with the UI unless the UI element is tagged "BuildUI." The issue arises when interacting with BuildUI—it updates the preview building's position even though the user wants the preview to remain in the last tapped spot.

The current script checks if touch is over BuildUI, but on mobile, it doesn't consider this behavior correctly in _HandleMobileInput, leading to the preview being updated improperly. I’ll think through a fix for that.

Approaching mobile input handling

The user wants BuildUI taps to not update or move the building preview while preventing building mode from being canceled in that case. The current method cancels building when any UI is touched, but this excludes BuildUI.

I’m thinking of adding logic to check if the pointer is over BuildUI and skipping the preview update without canceling the building. I might implement an early return for BuildUI taps to achieve this, while keeping the method signature unchanged.

Modifying touch handling for UI interactions

The user wants to prevent the building preview from updating when touching BuildUI, while not canceling building or moving the preview. I’m considering adding a function to detect whether the touch is over BuildUI, and if so, ignore the input to prevent updates.

When the touch phase is Began, I’ll check if the touch is over BuildUI and then stop dragging without canceling building. For Move and Ended, I’ll adjust the logic so that building behavior and preview updates are controlled appropriately.

Refining UI input logic for mobile and PC

I am refining the logic to prevent the building preview from moving when interacting with BuildUI, whether on mobile or PC. On mobile, I’ll implement a method to detect if the pointer is over BuildUI, and ignore it, so the preview remains in place. For the PC version, I’ll ensure similar behavior by preventing the preview from updating if touching over BuildUI, ensuring the script doesn't interfere with building mode until the user interacts outside of it. This modification should help achieve the desired user interaction on both platforms.

Thought for 59 seconds

Respuesta

Para que los botones BuildUI no desplaz-en el preview del edificio (pero sigan dejando al jugador usar la interfaz) basta con:

  1. Detectar explícitamente si el toque / clic está sobre BuildUI.
  2. Ignorar ese evento de entrada (no mover el preview ni cancelar el modo).

A continuación se muestra un parche minimalista; todo lo demás del script permanece igual.

csharp
// 1) ───────── helpers nuevos ─────────────────────────────── private bool _IsInBuildUIHierarchy(Transform t) { while (t != null) { if (t.CompareTag("BuildUI")) return true; t = t.parent; } return false; } private bool IsPointerOverBuildUI(Vector2 screenPos) { PointerEventData data = new PointerEventData(EventSystem.current) { position = screenPos }; List<RaycastResult> results = new List<RaycastResult>(); EventSystem.current.RaycastAll(data, results); foreach (var res in results) if (_IsInBuildUIHierarchy(res.gameObject.transform)) return true; return false; }

2) Móvil _HandleMobileInput

csharp
private void _HandleMobileInput() { if (Input.touchCount == 0) return; Touch touch = Input.GetTouch(0); bool overOtherUI = IsPointerOverUI(touch.position); // cualquier UI ≠ BuildUI bool overBuildUI = IsPointerOverBuildUI(touch.position); // solo botones de construcción //-- toca UI normal → cancelar construcción if (touch.phase == TouchPhase.Began && overOtherUI) { _CancelBuildingMode(); return; } //-- toca BuildUI → NO mover el preview ni cancelar if (overBuildUI) return; //----- resto del método SIN CAMBIOS (mostrar preview, drag, etc.) ----- }

3) PC _HandlePCInput

csharp
private void _HandlePCInput() { if (_buildingPrefab == null) return; if (EventSystem.current.IsPointerOverGameObject()) { // sobre BuildUI → ignorar entrada pero no cancelar ni ocultar preview var go = EventSystem.current.currentSelectedGameObject; if (go != null && _IsInBuildUIHierarchy(go.transform)) return; // sobre otra UI → lógica existente if (Input.GetMouseButtonDown(0)) { _CancelBuildingMode(); return; } if (_toBuild.activeSelf) _toBuild.SetActive(false); return; } //----- resto del método SIN CAMBIOS (snap, sortOrder, colocar, etc.) ----- }

Qué logra el cambio

  • Tocar / hacer clic en cualquier elemento fuera de BuildUI sigue funcionando como antes:
    · clic izquierdo sobre UI normal cancela construcción.
    · el preview se oculta si el cursor está sobre UI normal.

  • Tocar / hacer clic en BuildUI:
    · la construcción no se cancela.
    · el preview se queda en la última posición válida (no salta debajo del botón).
    · puedes pulsar varios botones de BuildUI sin que el edificio se mueva.

Con esto el jugador puede colocar el edificio, abrir un panel de mejoras, alternar categorías, etc., sin que cada pulsación en la interfaz le desplace el preview.

Compartir este Q&A