| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.InputSystem;
- public enum BuildingCategory
- {
- Living,
- Working,
- Decorative,
- Infrastructure
- }
- [System.Serializable]
- public class BuildingTemplate
- {
- public string name;
- public BuildingCategory category;
- public GameObject prefab;
- public ResourceCost[] costs;
- public string description;
- public Vector2Int size = new Vector2Int(1, 1); // Grid size
- }
- [System.Serializable]
- public class ResourceCost
- {
- public ResourceType resourceType;
- public int amount;
- }
- public class BuildingSystem : MonoBehaviour
- {
- [Header("Building Templates")]
- public List<BuildingTemplate> buildingTemplates = new List<BuildingTemplate>();
- [Header("Building Settings")]
- public LayerMask buildableLayer = 1; // Ground layer
- public Material previewMaterial;
- private BuildingTemplate selectedBuildingTemplate;
- private GameObject previewObject;
- private Camera playerCamera;
- private bool isBuildModeActive = false;
- // Events
- public static System.Action<BuildingTemplate> OnBuildingSelected;
- public static System.Action OnBuildModeEntered;
- public static System.Action OnBuildModeExited;
- public static System.Action<GameObject> OnBuildingPlaced;
- void Start()
- {
- playerCamera = Camera.main;
- InitializeBuildingTemplates();
- }
- void InitializeBuildingTemplates()
- {
- // Clear existing templates
- buildingTemplates.Clear();
- // Add House template
- buildingTemplates.Add(new BuildingTemplate
- {
- name = "House",
- category = BuildingCategory.Living,
- costs = new ResourceCost[]
- {
- new ResourceCost { resourceType = ResourceType.Wood, amount = 20 },
- new ResourceCost { resourceType = ResourceType.Stone, amount = 10 }
- },
- description = "Provides housing for villagers. Increases happiness and allows population growth.",
- size = new Vector2Int(2, 2)
- });
- // Add Builder's Workshop template
- buildingTemplates.Add(new BuildingTemplate
- {
- name = "Builder's Workshop",
- category = BuildingCategory.Working,
- costs = new ResourceCost[]
- {
- new ResourceCost { resourceType = ResourceType.Wood, amount = 30 },
- new ResourceCost { resourceType = ResourceType.Stone, amount = 15 }
- },
- description = "Allows villagers to work as builders. Required for construction projects.",
- size = new Vector2Int(2, 1)
- });
- Debug.Log($"Initialized {buildingTemplates.Count} building templates");
- }
- void Update()
- {
- if (isBuildModeActive)
- {
- HandleBuildMode();
- }
- }
- public void EnterBuildMode(BuildingTemplate template)
- {
- selectedBuildingTemplate = template;
- isBuildModeActive = true;
- CreatePreviewObject();
- OnBuildModeEntered?.Invoke();
- Debug.Log($"Entered build mode for: {template.name}");
- }
- public void ExitBuildMode()
- {
- isBuildModeActive = false;
- selectedBuildingTemplate = null;
- if (previewObject != null)
- {
- DestroyImmediate(previewObject);
- }
- OnBuildModeExited?.Invoke();
- Debug.Log("Exited build mode");
- }
- void CreatePreviewObject()
- {
- if (selectedBuildingTemplate?.prefab != null)
- {
- previewObject = Instantiate(selectedBuildingTemplate.prefab);
- // Make it a preview (transparent, non-interactive)
- MakePreviewObject(previewObject);
- }
- else
- {
- // Create a simple cube preview if no prefab
- previewObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
- previewObject.name = $"{selectedBuildingTemplate.name}_Preview";
- // Scale based on building size
- previewObject.transform.localScale = new Vector3(
- selectedBuildingTemplate.size.x,
- 1f,
- selectedBuildingTemplate.size.y
- );
- MakePreviewObject(previewObject);
- }
- }
- void MakePreviewObject(GameObject obj)
- {
- // Make transparent
- Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
- foreach (var renderer in renderers)
- {
- if (previewMaterial != null)
- {
- renderer.material = previewMaterial;
- }
- else
- {
- // Create basic transparent material
- Material transparentMat = new Material(Shader.Find("Standard"));
- transparentMat.SetFloat("_Mode", 3); // Transparent
- transparentMat.color = new Color(0, 1, 0, 0.5f);
- renderer.material = transparentMat;
- }
- }
- // Remove colliders to prevent interference
- Collider[] colliders = obj.GetComponentsInChildren<Collider>();
- foreach (var collider in colliders)
- {
- collider.enabled = false;
- }
- }
- void HandleBuildMode()
- {
- if (previewObject == null) return;
- // Update preview position based on mouse
- if (Mouse.current == null) return;
- Vector2 mousePosition = Mouse.current.position.ReadValue();
- Ray ray = playerCamera.ScreenPointToRay(mousePosition);
- if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, buildableLayer))
- {
- Vector3 buildPosition = hit.point;
- // Snap to grid (optional)
- buildPosition.x = Mathf.Round(buildPosition.x);
- buildPosition.z = Mathf.Round(buildPosition.z);
- buildPosition.y = hit.point.y;
- previewObject.transform.position = buildPosition;
- // Check if position is valid
- bool canBuild = CanBuildAtPosition(buildPosition);
- // Change preview color based on validity
- ChangePreviewColor(canBuild);
- // Handle click to place building
- if (Mouse.current.leftButton.wasPressedThisFrame && canBuild)
- {
- PlaceBuilding(buildPosition);
- }
- }
- // Cancel build mode
- if (Keyboard.current.escapeKey.wasPressedThisFrame || Mouse.current.rightButton.wasPressedThisFrame)
- {
- ExitBuildMode();
- }
- }
- bool CanBuildAtPosition(Vector3 position)
- {
- // Check if we have enough resources
- if (!HasEnoughResources(selectedBuildingTemplate))
- {
- return false;
- }
- // Check for overlapping objects using a box that matches building size
- Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y);
- Collider[] overlapping = Physics.OverlapBox(position, boxSize * 0.5f, Quaternion.identity);
- foreach (var col in overlapping)
- {
- // Skip the preview object itself
- if (col.gameObject == previewObject)
- continue;
- // Skip ground/terrain objects
- string objName = col.gameObject.name.ToLower();
- if (objName.Contains("ground") || objName.Contains("terrain") || objName.Contains("plane"))
- continue;
- // Check for buildings
- if (col.GetComponent<Building>() != null)
- {
- Debug.Log($"Building blocked by existing building: {col.gameObject.name}");
- return false;
- }
- // Check for resource objects and other blocking objects by tag
- string tag = col.gameObject.tag;
- if (tag == "Tree" || tag == "Stone" || tag == "Farm" || tag == "TownHall" || tag == "Villager")
- {
- Debug.Log($"Building blocked by {tag}: {col.gameObject.name}");
- return false;
- }
- // Check for any other objects with colliders (except ground layer)
- if (col.gameObject.layer != 0) // Layer 0 is Default, usually ground
- {
- Debug.Log($"Building blocked by object: {col.gameObject.name} (layer: {col.gameObject.layer})");
- return false;
- }
- }
- return true;
- }
- bool HasEnoughResources(BuildingTemplate template)
- {
- if (GameManager.Instance?.resourceManager == null) return false;
- foreach (var cost in template.costs)
- {
- if (!GameManager.Instance.resourceManager.HasResource(cost.resourceType, cost.amount))
- {
- return false;
- }
- }
- return true;
- }
- void ChangePreviewColor(bool canBuild)
- {
- Color previewColor = canBuild ? Color.green : Color.red;
- previewColor.a = 0.5f;
- Renderer[] renderers = previewObject.GetComponentsInChildren<Renderer>();
- foreach (var renderer in renderers)
- {
- renderer.material.color = previewColor;
- }
- }
- void PlaceBuilding(Vector3 position)
- {
- // Consume resources
- foreach (var cost in selectedBuildingTemplate.costs)
- {
- GameManager.Instance.resourceManager.RemoveResource(cost.resourceType, cost.amount);
- }
- // Create the actual building
- GameObject newBuilding;
- if (selectedBuildingTemplate.prefab != null)
- {
- newBuilding = Instantiate(selectedBuildingTemplate.prefab, position, Quaternion.identity);
- }
- else
- {
- // Create basic building
- newBuilding = CreateBasicBuilding(selectedBuildingTemplate, position);
- }
- // Add building component if it doesn't exist
- Building buildingComponent = newBuilding.GetComponent<Building>();
- if (buildingComponent == null)
- {
- buildingComponent = newBuilding.AddComponent<Building>();
- }
- buildingComponent.buildingName = selectedBuildingTemplate.name;
- buildingComponent.category = selectedBuildingTemplate.category;
- OnBuildingPlaced?.Invoke(newBuilding);
- Debug.Log($"Placed {selectedBuildingTemplate.name} at {position}");
- // Exit build mode after placing
- ExitBuildMode();
- }
- GameObject CreateBasicBuilding(BuildingTemplate template, Vector3 position)
- {
- GameObject building = new GameObject(template.name);
- building.transform.position = position;
- // Create visual representation
- GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
- visual.transform.SetParent(building.transform);
- visual.transform.localPosition = Vector3.zero;
- visual.transform.localScale = new Vector3(template.size.x, 1f, template.size.y);
- // Color based on category
- Renderer renderer = visual.GetComponent<Renderer>();
- renderer.material.color = GetCategoryColor(template.category);
- // Add collider for selection
- if (building.GetComponent<Collider>() == null)
- {
- BoxCollider collider = building.AddComponent<BoxCollider>();
- collider.size = new Vector3(template.size.x, 2f, template.size.y);
- collider.center = new Vector3(0, 1f, 0);
- }
- return building;
- }
- Color GetCategoryColor(BuildingCategory category)
- {
- return category switch
- {
- BuildingCategory.Living => new Color(0.8f, 0.6f, 0.4f), // Brown
- BuildingCategory.Working => new Color(0.6f, 0.6f, 0.8f), // Blue
- BuildingCategory.Decorative => new Color(0.8f, 0.4f, 0.8f), // Purple
- BuildingCategory.Infrastructure => new Color(0.6f, 0.8f, 0.6f), // Green
- _ => Color.gray
- };
- }
- public List<BuildingTemplate> GetBuildingsByCategory(BuildingCategory category)
- {
- List<BuildingTemplate> buildings = new List<BuildingTemplate>();
- foreach (var template in buildingTemplates)
- {
- if (template.category == category)
- {
- buildings.Add(template);
- }
- }
- return buildings;
- }
- public string GetResourceCostString(BuildingTemplate template)
- {
- if (template.costs == null || template.costs.Length == 0)
- return "Free";
- List<string> costStrings = new List<string>();
- foreach (var cost in template.costs)
- {
- costStrings.Add($"{cost.amount} {cost.resourceType}");
- }
- return string.Join(", ", costStrings);
- }
- // Debug visualization
- void OnDrawGizmos()
- {
- if (isBuildModeActive && selectedBuildingTemplate != null && previewObject != null)
- {
- // Draw building bounds
- Gizmos.color = Color.yellow;
- Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y);
- Gizmos.DrawWireCube(previewObject.transform.position, boxSize);
- }
- }
- }
|