243 lines
6.4 KiB
C#
243 lines
6.4 KiB
C#
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<GameObject> placedBuildings = new List<GameObject>();
|
|
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 = 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<BuildingHealth>() == null)
|
|
{
|
|
building.AddComponent<BuildingHealth>();
|
|
}
|
|
|
|
if (currentCharacterData != null)
|
|
{
|
|
Barracks barracks = building.GetComponent<Barracks>();
|
|
if (barracks == null)
|
|
{
|
|
barracks = building.AddComponent<Barracks>();
|
|
}
|
|
|
|
barracks.characterData = currentCharacterData;
|
|
}
|
|
else if (building.GetComponent<GoldMine>() == null)
|
|
{
|
|
building.AddComponent<GoldMine>();
|
|
}
|
|
|
|
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>())
|
|
{
|
|
renderer.sharedMaterial = material;
|
|
}
|
|
}
|
|
|
|
private void SetCollidersEnabled(GameObject target, bool enabled)
|
|
{
|
|
foreach (Collider collider in target.GetComponentsInChildren<Collider>())
|
|
{
|
|
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;
|
|
}
|
|
}
|