BuildingSystem.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.InputSystem;
  4. public enum BuildingCategory
  5. {
  6. Living,
  7. Working,
  8. Decorative,
  9. Infrastructure
  10. }
  11. [System.Serializable]
  12. public class BuildingTemplate
  13. {
  14. public string name;
  15. public BuildingCategory category;
  16. public GameObject prefab;
  17. public ResourceCost[] costs;
  18. public string description;
  19. public Vector2Int size = new Vector2Int(1, 1); // Grid size
  20. }
  21. [System.Serializable]
  22. public class ResourceCost
  23. {
  24. public ResourceType resourceType;
  25. public int amount;
  26. }
  27. public class BuildingSystem : MonoBehaviour
  28. {
  29. [Header("Building Templates")]
  30. public List<BuildingTemplate> buildingTemplates = new List<BuildingTemplate>();
  31. [Header("Building Settings")]
  32. public LayerMask buildableLayer = 1; // Ground layer
  33. public Material previewMaterial;
  34. private BuildingTemplate selectedBuildingTemplate;
  35. private GameObject previewObject;
  36. private Camera playerCamera;
  37. private bool isBuildModeActive = false;
  38. // Events
  39. public static System.Action<BuildingTemplate> OnBuildingSelected;
  40. public static System.Action OnBuildModeEntered;
  41. public static System.Action OnBuildModeExited;
  42. public static System.Action<GameObject> OnBuildingPlaced;
  43. void Start()
  44. {
  45. playerCamera = Camera.main;
  46. InitializeBuildingTemplates();
  47. }
  48. void InitializeBuildingTemplates()
  49. {
  50. // Clear existing templates
  51. buildingTemplates.Clear();
  52. // Add House template
  53. buildingTemplates.Add(new BuildingTemplate
  54. {
  55. name = "House",
  56. category = BuildingCategory.Living,
  57. costs = new ResourceCost[]
  58. {
  59. new ResourceCost { resourceType = ResourceType.Wood, amount = 20 },
  60. new ResourceCost { resourceType = ResourceType.Stone, amount = 10 }
  61. },
  62. description = "Provides housing for villagers. Increases happiness and allows population growth.",
  63. size = new Vector2Int(2, 2)
  64. });
  65. // Add Builder's Workshop template
  66. buildingTemplates.Add(new BuildingTemplate
  67. {
  68. name = "Builder's Workshop",
  69. category = BuildingCategory.Working,
  70. costs = new ResourceCost[]
  71. {
  72. new ResourceCost { resourceType = ResourceType.Wood, amount = 30 },
  73. new ResourceCost { resourceType = ResourceType.Stone, amount = 15 }
  74. },
  75. description = "Allows villagers to work as builders. Required for construction projects.",
  76. size = new Vector2Int(2, 1)
  77. });
  78. Debug.Log($"Initialized {buildingTemplates.Count} building templates");
  79. }
  80. void Update()
  81. {
  82. if (isBuildModeActive)
  83. {
  84. HandleBuildMode();
  85. }
  86. }
  87. public void EnterBuildMode(BuildingTemplate template)
  88. {
  89. selectedBuildingTemplate = template;
  90. isBuildModeActive = true;
  91. CreatePreviewObject();
  92. OnBuildModeEntered?.Invoke();
  93. Debug.Log($"Entered build mode for: {template.name}");
  94. }
  95. public void ExitBuildMode()
  96. {
  97. isBuildModeActive = false;
  98. selectedBuildingTemplate = null;
  99. if (previewObject != null)
  100. {
  101. DestroyImmediate(previewObject);
  102. }
  103. OnBuildModeExited?.Invoke();
  104. Debug.Log("Exited build mode");
  105. }
  106. void CreatePreviewObject()
  107. {
  108. if (selectedBuildingTemplate?.prefab != null)
  109. {
  110. previewObject = Instantiate(selectedBuildingTemplate.prefab);
  111. // Make it a preview (transparent, non-interactive)
  112. MakePreviewObject(previewObject);
  113. }
  114. else
  115. {
  116. // Create a simple cube preview if no prefab
  117. previewObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
  118. previewObject.name = $"{selectedBuildingTemplate.name}_Preview";
  119. // Scale based on building size
  120. previewObject.transform.localScale = new Vector3(
  121. selectedBuildingTemplate.size.x,
  122. 1f,
  123. selectedBuildingTemplate.size.y
  124. );
  125. MakePreviewObject(previewObject);
  126. }
  127. }
  128. void MakePreviewObject(GameObject obj)
  129. {
  130. // Make transparent
  131. Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
  132. foreach (var renderer in renderers)
  133. {
  134. if (previewMaterial != null)
  135. {
  136. renderer.material = previewMaterial;
  137. }
  138. else
  139. {
  140. // Create basic transparent material
  141. Material transparentMat = new Material(Shader.Find("Standard"));
  142. transparentMat.SetFloat("_Mode", 3); // Transparent
  143. transparentMat.color = new Color(0, 1, 0, 0.5f);
  144. renderer.material = transparentMat;
  145. }
  146. }
  147. // Remove colliders to prevent interference
  148. Collider[] colliders = obj.GetComponentsInChildren<Collider>();
  149. foreach (var collider in colliders)
  150. {
  151. collider.enabled = false;
  152. }
  153. }
  154. void HandleBuildMode()
  155. {
  156. if (previewObject == null) return;
  157. // Update preview position based on mouse
  158. if (Mouse.current == null) return;
  159. Vector2 mousePosition = Mouse.current.position.ReadValue();
  160. Ray ray = playerCamera.ScreenPointToRay(mousePosition);
  161. if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, buildableLayer))
  162. {
  163. Vector3 buildPosition = hit.point;
  164. // Snap to grid (optional)
  165. buildPosition.x = Mathf.Round(buildPosition.x);
  166. buildPosition.z = Mathf.Round(buildPosition.z);
  167. buildPosition.y = hit.point.y;
  168. previewObject.transform.position = buildPosition;
  169. // Check if position is valid
  170. bool canBuild = CanBuildAtPosition(buildPosition);
  171. // Change preview color based on validity
  172. ChangePreviewColor(canBuild);
  173. // Handle click to place building
  174. if (Mouse.current.leftButton.wasPressedThisFrame && canBuild)
  175. {
  176. PlaceBuilding(buildPosition);
  177. }
  178. }
  179. // Cancel build mode
  180. if (Keyboard.current.escapeKey.wasPressedThisFrame || Mouse.current.rightButton.wasPressedThisFrame)
  181. {
  182. ExitBuildMode();
  183. }
  184. }
  185. bool CanBuildAtPosition(Vector3 position)
  186. {
  187. // Check if we have enough resources
  188. if (!HasEnoughResources(selectedBuildingTemplate))
  189. {
  190. return false;
  191. }
  192. // Check for overlapping objects using a box that matches building size
  193. Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y);
  194. Collider[] overlapping = Physics.OverlapBox(position, boxSize * 0.5f, Quaternion.identity);
  195. foreach (var col in overlapping)
  196. {
  197. // Skip the preview object itself
  198. if (col.gameObject == previewObject)
  199. continue;
  200. // Skip ground/terrain objects
  201. string objName = col.gameObject.name.ToLower();
  202. if (objName.Contains("ground") || objName.Contains("terrain") || objName.Contains("plane"))
  203. continue;
  204. // Check for buildings
  205. if (col.GetComponent<Building>() != null)
  206. {
  207. Debug.Log($"Building blocked by existing building: {col.gameObject.name}");
  208. return false;
  209. }
  210. // Check for resource objects and other blocking objects by tag
  211. string tag = col.gameObject.tag;
  212. if (tag == "Tree" || tag == "Stone" || tag == "Farm" || tag == "TownHall" || tag == "Villager")
  213. {
  214. Debug.Log($"Building blocked by {tag}: {col.gameObject.name}");
  215. return false;
  216. }
  217. // Check for any other objects with colliders (except ground layer)
  218. if (col.gameObject.layer != 0) // Layer 0 is Default, usually ground
  219. {
  220. Debug.Log($"Building blocked by object: {col.gameObject.name} (layer: {col.gameObject.layer})");
  221. return false;
  222. }
  223. }
  224. return true;
  225. }
  226. bool HasEnoughResources(BuildingTemplate template)
  227. {
  228. if (GameManager.Instance?.resourceManager == null) return false;
  229. foreach (var cost in template.costs)
  230. {
  231. if (!GameManager.Instance.resourceManager.HasResource(cost.resourceType, cost.amount))
  232. {
  233. return false;
  234. }
  235. }
  236. return true;
  237. }
  238. void ChangePreviewColor(bool canBuild)
  239. {
  240. Color previewColor = canBuild ? Color.green : Color.red;
  241. previewColor.a = 0.5f;
  242. Renderer[] renderers = previewObject.GetComponentsInChildren<Renderer>();
  243. foreach (var renderer in renderers)
  244. {
  245. renderer.material.color = previewColor;
  246. }
  247. }
  248. void PlaceBuilding(Vector3 position)
  249. {
  250. // Consume resources
  251. foreach (var cost in selectedBuildingTemplate.costs)
  252. {
  253. GameManager.Instance.resourceManager.RemoveResource(cost.resourceType, cost.amount);
  254. }
  255. // Create the actual building
  256. GameObject newBuilding;
  257. if (selectedBuildingTemplate.prefab != null)
  258. {
  259. newBuilding = Instantiate(selectedBuildingTemplate.prefab, position, Quaternion.identity);
  260. }
  261. else
  262. {
  263. // Create basic building
  264. newBuilding = CreateBasicBuilding(selectedBuildingTemplate, position);
  265. }
  266. // Add building component if it doesn't exist
  267. Building buildingComponent = newBuilding.GetComponent<Building>();
  268. if (buildingComponent == null)
  269. {
  270. buildingComponent = newBuilding.AddComponent<Building>();
  271. }
  272. buildingComponent.buildingName = selectedBuildingTemplate.name;
  273. buildingComponent.category = selectedBuildingTemplate.category;
  274. OnBuildingPlaced?.Invoke(newBuilding);
  275. Debug.Log($"Placed {selectedBuildingTemplate.name} at {position}");
  276. // Exit build mode after placing
  277. ExitBuildMode();
  278. }
  279. GameObject CreateBasicBuilding(BuildingTemplate template, Vector3 position)
  280. {
  281. GameObject building = new GameObject(template.name);
  282. building.transform.position = position;
  283. // Create visual representation
  284. GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
  285. visual.transform.SetParent(building.transform);
  286. visual.transform.localPosition = Vector3.zero;
  287. visual.transform.localScale = new Vector3(template.size.x, 1f, template.size.y);
  288. // Color based on category
  289. Renderer renderer = visual.GetComponent<Renderer>();
  290. renderer.material.color = GetCategoryColor(template.category);
  291. // Add collider for selection
  292. if (building.GetComponent<Collider>() == null)
  293. {
  294. BoxCollider collider = building.AddComponent<BoxCollider>();
  295. collider.size = new Vector3(template.size.x, 2f, template.size.y);
  296. collider.center = new Vector3(0, 1f, 0);
  297. }
  298. return building;
  299. }
  300. Color GetCategoryColor(BuildingCategory category)
  301. {
  302. return category switch
  303. {
  304. BuildingCategory.Living => new Color(0.8f, 0.6f, 0.4f), // Brown
  305. BuildingCategory.Working => new Color(0.6f, 0.6f, 0.8f), // Blue
  306. BuildingCategory.Decorative => new Color(0.8f, 0.4f, 0.8f), // Purple
  307. BuildingCategory.Infrastructure => new Color(0.6f, 0.8f, 0.6f), // Green
  308. _ => Color.gray
  309. };
  310. }
  311. public List<BuildingTemplate> GetBuildingsByCategory(BuildingCategory category)
  312. {
  313. List<BuildingTemplate> buildings = new List<BuildingTemplate>();
  314. foreach (var template in buildingTemplates)
  315. {
  316. if (template.category == category)
  317. {
  318. buildings.Add(template);
  319. }
  320. }
  321. return buildings;
  322. }
  323. public string GetResourceCostString(BuildingTemplate template)
  324. {
  325. if (template.costs == null || template.costs.Length == 0)
  326. return "Free";
  327. List<string> costStrings = new List<string>();
  328. foreach (var cost in template.costs)
  329. {
  330. costStrings.Add($"{cost.amount} {cost.resourceType}");
  331. }
  332. return string.Join(", ", costStrings);
  333. }
  334. // Debug visualization
  335. void OnDrawGizmos()
  336. {
  337. if (isBuildModeActive && selectedBuildingTemplate != null && previewObject != null)
  338. {
  339. // Draw building bounds
  340. Gizmos.color = Color.yellow;
  341. Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y);
  342. Gizmos.DrawWireCube(previewObject.transform.position, boxSize);
  343. }
  344. }
  345. }