using System.Collections.Generic; using UnityEngine; public class BuildingPlacementManager : MonoBehaviour { public static BuildingPlacementManager Instance { get; private set; } [Header("建筑预制体")] public GameObject barracksBasePrefab; public GameObject goldMinePrefab; [Header("虚拟网格")] [Min(0.1f)] public float gridSize = 1f; [Min(0f)] public float placementHeight; public Vector2 xLimits = new Vector2(-7f, 7f); public Vector2 zLimits = new Vector2(-10f, 20f); [Header("放置检测")] public LayerMask groundLayer; [Min(0.1f)] public float buildingRadius = 1.25f; public Material previewMaterialValid; public Material previewMaterialInvalid; private readonly List placedBuildings = new List(); private CharacterData currentCharacterData; private GameObject currentPrefab; private GameObject previewObject; private Camera mainCamera; private bool isPlacing; private bool previewPositionValid; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; } private void Start() { mainCamera = Camera.main; RegisterExistingBuilding(GameObject.Find("主要基地")); } private void Update() { if (!isPlacing) { return; } UpdatePreview(); if (Input.GetMouseButtonDown(0)) { TryPlaceBuilding(); } else if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.Escape)) { CancelPlacement(); } } public void StartPlacingBuilding(CharacterData data) { if (data == null) { return; } StartPlacingBarracks(data); } public void StartPlacingBarracks(CharacterData data) { if (data == null || barracksBasePrefab == null) { Debug.LogError("兵营预制体或角色数据为空"); return; } BeginPlacement(barracksBasePrefab, data); } public void StartPlacingGoldMine() { if (goldMinePrefab == null) { Debug.LogError("金矿预制体为空"); return; } BeginPlacement(goldMinePrefab, null); } private void BeginPlacement(GameObject prefab, CharacterData data) { CancelPlacement(); currentPrefab = prefab; currentCharacterData = data; previewObject = Instantiate(prefab); previewObject.name = "建筑预览"; SetCollidersEnabled(previewObject, false); isPlacing = true; mainCamera = Camera.main; } private void UpdatePreview() { if (previewObject == null || mainCamera == null) { return; } Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); int mask = groundLayer.value == 0 ? Physics.DefaultRaycastLayers : groundLayer.value; if (!Physics.Raycast(ray, out RaycastHit hit, 1000f, mask)) { previewPositionValid = false; return; } Vector3 position = hit.point; position.x = Mathf.Round(position.x / gridSize) * gridSize; position.y = placementHeight; position.z = Mathf.Round(position.z / gridSize) * gridSize; previewObject.transform.position = position; previewPositionValid = IsValidPosition(position); UpdatePreviewMaterial(previewPositionValid); } private bool IsValidPosition(Vector3 position) { if (position.x < xLimits.x || position.x > xLimits.y || position.z < zLimits.x || position.z > zLimits.y) { return false; } foreach (GameObject building in placedBuildings) { if (building != null && Vector3.Distance(position, building.transform.position) < buildingRadius * 2f) { return false; } } return true; } private void TryPlaceBuilding() { if (!previewPositionValid || previewObject == null || currentPrefab == null) { return; } int cost = currentCharacterData != null ? currentCharacterData.buildingCost : 100; if (GoldSystem.Instance == null || !GoldSystem.Instance.TrySpend(cost)) { Debug.Log($"金币不足,需要{cost}"); return; } Vector3 position = previewObject.transform.position; GameObject building = Instantiate(currentPrefab, position, Quaternion.identity); building.name = currentCharacterData != null ? "兵营_" + currentCharacterData.characterName : "金矿"; building.tag = "Friend"; if (building.GetComponent() == null) { building.AddComponent(); } if (currentCharacterData != null) { Barracks barracks = building.GetComponent(); if (barracks == null) { barracks = building.AddComponent(); } barracks.characterData = currentCharacterData; } else if (building.GetComponent() == null) { building.AddComponent(); } placedBuildings.Add(building); CancelPlacement(); } private void UpdatePreviewMaterial(bool valid) { Material material = valid ? previewMaterialValid : previewMaterialInvalid; if (material == null || previewObject == null) { return; } foreach (Renderer renderer in previewObject.GetComponentsInChildren()) { renderer.sharedMaterial = material; } } private void SetCollidersEnabled(GameObject target, bool enabled) { foreach (Collider collider in target.GetComponentsInChildren()) { collider.enabled = enabled; } } public void RegisterExistingBuilding(GameObject building) { if (building != null && !placedBuildings.Contains(building)) { placedBuildings.Add(building); } } public void CancelPlacement() { if (previewObject != null) { Destroy(previewObject); } previewObject = null; currentPrefab = null; currentCharacterData = null; isPlacing = false; previewPositionValid = false; } }