Mira, en unity tengo un edificio que al colocarse ...

تم الإنشاء في: ١٠ يونيو ٢٠٢٥

تمت الإجابة باستخدام O3 بواسطة Chat01

سؤال

Mira, en unity tengo un edificio que al colocarse tiene que activar el GameObject ciclos:


using UnityEngine;

public class ed0 : MonoBehaviour
{
public GameObject ciclos;

text
void Start() { ciclos.SetActive(true); }

}


el problema es que el edificio se coloca con el siguiente script:


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; } if (touch.phase == TouchPhase.Began && IsPointerOverBuildUI(touch.position)) { // Mostrar preview sin moverlo if (!_toBuild.activeSelf) _toBuild.SetActive(true); isDragging = false; 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; } } if (_toBuild != null) { foreach (SpriteRenderer sr in _toBuild.GetComponent<BuildingManager>().spriteRenderers) { sr.sortingOrder = _CalculateSortingOrder(sr.transform.position) + 1000; } } } 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();
if (_toBuild != null && Camera.main != null)
{
// centro de la pantalla en píxeles
Vector3 screenCenter = new Vector3(Screen.width * 0.5f,
Screen.height * 0.5f,
Mathf.Abs(Camera.main.transform.position.z));

text
// pasar a coordenadas mundo Vector2 worldCenter = Camera.main.ScreenToWorldPoint(screenCenter); // ajustar a la rejilla (cartesiana o isométrica) Vector2 snapped = isIsometric ? _ClampToNearestIsometric(worldCenter, cellSize) : _ClampToNearest(worldCenter, cellSize); // posicionar el preview _toBuild.transform.position = new Vector3(snapped.x, snapped.y, _toBuild.transform.position.z); }

#endif

text
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); 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; } 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; t = t.parent; } } return false; }

}


por lo que ciclos se activa cuando se entra en el modo construccion y el preview aparece, no cuando se coloca en si. Como puedo solucionarlo?

الفكرة

The user has a Unity code issue where a function (ciclos) activates during the preview phase, but they want it activated only when a building is placed. The problem occurs because the Start() method is called as soon as the preview object is instantiated. To fix this, I suggest deferring the activation. This can be done by moving the activation call to a method triggered upon placement confirmation, like using an OnPlaced() interface or activating it within the building management system after placement.

The user is working with Unity and wants to control when a ciclos object is activated; it currently happens too early during the preview phase. There are a few options: they can disable ciclos by default, then activate it once placement is confirmed. One solution is to call OnPlaced() after the building's placement, or control activation with the PlacementMode. Another option is to track placement mode in an update function or use UnityEvents. A simpler approach would be to manage activation in the building script after placement confirmation.

Thought for 17 seconds

الإجابة

Start() se ejecuta en el mismo frame en que el prefab se instancia, y tu prefab se instancia cuando entras en modo construcción para mostrar el “preview”.
Por eso ciclos.SetActive(true) se dispara demasiado pronto.

La solución es “avisar” al edificio solo cuando ya quedó fijado (PlacementMode.Fixed).
Hay varias maneras; la más limpia es exponer un método (o un UnityEvent) en el propio edificio y llamarlo desde _InstanceBuilding():

csharp
// ---------- ed0.cs ---------- using UnityEngine; public class ed0 : MonoBehaviour { [SerializeField] private GameObject ciclos; // Nada en Start: el prefab se mostrará con ciclos apagado /// <summary>Se llama cuando el edificio queda colocado.</summary> public void ActivarCiclos() { ciclos.SetActive(true); } }
csharp
// ---------- BuildingGridPlacer.cs ---------- private void _InstanceBuilding() { if (_toBuild == null) return; BuildingManager m = _toBuild.GetComponent<BuildingManager>(); if (!m.hasValidPlacement) return; m.SetPlacementMode(PlacementMode.Fixed); // Cambiar el sorting… foreach (SpriteRenderer sr in m.spriteRenderers) sr.sortingOrder = _CalculateSortingOrder(sr.transform.position); // 👉 Avisar al edificio que ya está colocado ed0 ed = _toBuild.GetComponent<ed0>(); if (ed != null) ed.ActivarCiclos(); // ­--- resto del método igual --- _buildingPrefab = null; _toBuild = null; _EnableGridVisual(false); script0UI.OcultarIndicaciones(); script0UI.BotonesConsOff(); IsPlacingBuilding = false; }

Alternativas rápidas

  • UnityEvent
    En BuildingManager agrega un UnityEvent OnPlaced; invócalo cuando pases a Fixed y, desde el Inspector, enlaza ese evento al SetActive(true) de ciclos.

  • Comprobación en Update dentro de ed0

    csharp
    void Update() { if (!ciclos.activeSelf && GetComponent<BuildingManager>().placementMode == PlacementMode.Fixed) { ciclos.SetActive(true); enabled = false; // ya no hace falta seguir actualizando } }

    (Es menos limpio, pero no requiere tocar BuildingGridPlacer).


Con cualquiera de esas opciones ciclos permanecerá apagado mientras el objeto sea solo un preview y se encenderá únicamente cuando el jugador confirme la colocación.

مشاركة هذا السؤال والجواب