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), pero todavia sin cancelar el modo construccion.

Pensamiento

The user is working with a script in Unity C# used in a construction mode. The script cancels build mode if the user taps any part of the UI, except for the BuildUI (where construction buttons are). However, they're asking for a specific tweak: the building preview should not appear where the user taps within the BuildUI, without actually canceling build mode. I'll need to help modify the script so that it distinguishes between placement taps and UI interaction more effectively.

It seems like the user wants to keep the construction mode running when interacting with the BuildUI, without canceling or moving the preview. Right now, on PCs, the script checks if the pointer is over a UI element and avoids showing the preview. On mobile, there's a custom check: it lets BuildUI slides pass through but prevents the preview from moving. The user now needs to fine-tune it so that if the pointer is over BuildUI, the preview remains fixed, but the mode stays active. Interesting challenge!

The user is refining how the preview behaves when interacting with the BuildUI, particularly for mobile. They need to prevent the preview from moving or updating if over BuildUI but still keep build mode active. Currently, on mobile, the script cancels build mode when the pointer is over UI, but they want to avoid this for BuildUI specifically. The proposed solution is to modify the logic in _HandleMobileInput, adding functions to detect interactions with BuildUI and prevent preview repositioning without canceling the building mode. A similar adjustment is needed for the PC version as well.

Thought for 20 seconds

Respuesta

Para que los taps sobre los botones de construcción no muevan el preview (pero tampoco salgan del modo construcción), solo necesitas distinguir entre:

  1. UI “normal” → cancela el modo construcción.
  2. UI con la etiqueta BuildUI → ignora el toque (no muevas el preview, pero sigue en modo construcción).
  3. Mundo → actualiza posición del preview.

1 . Nuevo helper

csharp
/// Devuelve true si el puntero está sobre algún elemento con tag "BuildUI" 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) { Transform t = res.gameObject.transform; while (t != null) { if (t.CompareTag("BuildUI")) return true; // ← encontrado t = t.parent; } } return false; }

Mantén tu IsPointerOverUI tal cual (sigue detectando cualquier UI excepto BuildUI).

2 . Cambios para móvil

En _HandleMobileInput añade al principio (antes del switch) algo así:

csharp
bool overBuildUI = IsPointerOverBuildUI(touch.position); bool overOtherUI = IsPointerOverUI(touch.position); // sólo UI “normal” // 1. UI “normal” → cancelar if (overOtherUI) { _CancelBuildingMode(); return; } // 2. BuildUI → ocultar preview y salir sin moverlo if (overBuildUI) { if (_toBuild.activeSelf) _toBuild.SetActive(false); return; } // 3. Mundo → asegurarse de que el preview esté visible if (!_toBuild.activeSelf) _toBuild.SetActive(true);

El resto del código (switch con Began / Moved / Ended) permanece igual; al entrar ahí ya sabes que el dedo no está sobre ninguna UI, así que puedes hacer snap y mover con seguridad.

3 . Cambios para PC (opcional pero recomendable)

Sustituye la lógica de UI de _HandlePCInput por:

csharp
bool overBuildUI = IsPointerOverBuildUI(Input.mousePosition); bool overOtherUI = EventSystem.current.IsPointerOverGameObject() && !overBuildUI; // UI “normal” if (overOtherUI) { if (Input.GetMouseButtonDown(0)) { _CancelBuildingMode(); return; } if (_toBuild.activeSelf) _toBuild.SetActive(false); return; } // BuildUI if (overBuildUI) { if (_toBuild.activeSelf) _toBuild.SetActive(false); // esconder/fijar preview return; }

(Después de esto viene tu código actual de snap & colocación).


Con estos cambios:

  • Pulsar en cualquier UI distinta de BuildUI sigue cancelando el modo construcción.
  • Pulsar en botones BuildUI sólo bloquea/oculta el preview mientras el dedo/ratón está sobre ellos —cuando salga de la UI, el preview vuelve a mostrarse en la posición anterior—, y nunca mueve el edificio a la coordenada del botón.

Compartir este Q&A