Добавлены префабы построек, BuildManager, BuildMenuUI и скрипт камеры

This commit is contained in:
2026-09-20 22:01:09 +03:00
parent 00cec2b99b
commit 54ea024850
47 changed files with 23834 additions and 5683 deletions
@@ -0,0 +1,123 @@
using UnityEngine;
using System.Collections.Generic;
// Управляет режимом строительства: показывает "призрак" здания под курсором,
// проверяет ресурсы, спавнит здание в подтверждённой позиции.
public class BuildManager : MonoBehaviour
{
public static BuildManager Instance { get; private set; }
[Header("Все типы зданий в игре")]
public List<BuildingData> allBuildings;
[Header("Визуал 'призрака' здания при размещении")]
public Color validPlacementColor = new Color(1, 1, 1, 0.6f);
public Color invalidPlacementColor = new Color(1, 0.3f, 0.3f, 0.6f);
private BuildingData currentlyPlacing;
private GameObject ghostObject;
private Camera mainCam;
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
mainCam = Camera.main;
}
void Update()
{
if (currentlyPlacing == null || ghostObject == null)
return;
Vector3 worldPos = GetPointerWorldPosition();
worldPos.z = 0;
ghostObject.transform.position = worldPos;
bool canAfford = currentlyPlacing.CanAfford();
SpriteRenderer sr = ghostObject.GetComponent<SpriteRenderer>();
if (sr != null)
sr.color = canAfford ? validPlacementColor : invalidPlacementColor;
if (Input.GetMouseButtonDown(0))
{
TryConfirmPlacement(worldPos);
}
if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.Escape))
{
CancelPlacement();
}
}
// Список зданий, доступных для стройки ПРЯМО СЕЙЧАС (по уровню Ратуши)
public List<BuildingData> GetAvailableBuildings()
{
List<BuildingData> available = new List<BuildingData>();
int townHallLevel = TownHall.Instance != null ? TownHall.Instance.currentLevel : 1;
foreach (var b in allBuildings)
{
if (b.requiredTownHallLevel <= townHallLevel)
available.Add(b);
}
return available;
}
// Вызывается из UI-кнопки конкретного здания в меню стройки
public void StartPlacing(BuildingData data)
{
if (currentlyPlacing != null)
{
CancelPlacement();
}
currentlyPlacing = data;
ghostObject = Instantiate(data.prefab);
// Отключаем на "призраке" всё, что мешает (коллайдер, скрипты сбора ресурсов и т.д.)
Collider2D col = ghostObject.GetComponent<Collider2D>();
if (col != null) col.enabled = false;
MonoBehaviour[] scripts = ghostObject.GetComponents<MonoBehaviour>();
foreach (var s in scripts)
{
s.enabled = false;
}
}
void TryConfirmPlacement(Vector3 position)
{
if (!currentlyPlacing.CanAfford())
{
Debug.Log("[СТРОЙКА] Не хватает ресурсов!");
return;
}
currentlyPlacing.PayCost();
GameObject built = Instantiate(currentlyPlacing.prefab, position, Quaternion.identity);
Debug.Log($"[СТРОЙКА] Построено: {currentlyPlacing.buildingName} в {position}");
Destroy(ghostObject);
currentlyPlacing = null;
}
void CancelPlacement()
{
if (ghostObject != null) Destroy(ghostObject);
currentlyPlacing = null;
Debug.Log("[СТРОЙКА] Размещение отменено.");
}
Vector3 GetPointerWorldPosition()
{
Vector3 screenPos = Input.mousePosition;
return mainCam.ScreenToWorldPoint(screenPos);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9de6545e2f73cd04f9d95334879c4fc4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 100
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections.Generic;
// Заполняет меню стройки кнопками для каждого доступного здания.
// Вешается на пустой объект-контейнер меню (например BuildMenuPanel).
public class BuildMenuUi : MonoBehaviour
{
[Header("Ссылки")]
public Transform buttonContainer; //Куда савить снопки
public GameObject buildingButtonPrefab; //Преваб одной кнопки здания
void Start()
{
RefreshMenu();
}
void OnEnable()
{
if (BuildManager.Instance == null) return;
RefreshMenu();
}
public void RefreshMenu()
{
//Чистка старых кнопок перед сборкой списка
foreach (Transform child in buttonContainer)
{
Destroy(child.gameObject);
}
List<BuildingData> available = BuildManager.Instance.GetAvailableBuildings();
foreach (var building in available)
{
GameObject btnGO = Instantiate(buildingButtonPrefab, buttonContainer);
Transform iconT = btnGO.transform.Find("Icon");
Transform nameT = btnGO.transform.Find("NameText");
Transform costT = btnGO.transform.Find("CostText");
if (iconT != null) iconT.GetComponent<Image>().sprite = building.icon;
if (nameT != null) nameT.GetComponent<TMP_Text>().text = building.buildingName;
if (costT != null) costT.GetComponent<TMP_Text>().text = building.GetCostText();
Button btn = btnGO.GetComponent<Button>();
BuildingData capturedBuilding = building; // важно для замыкания в цикле
btn.onClick.AddListener(() =>
{
BuildManager.Instance.StartPlacing(capturedBuilding);
gameObject.SetActive(false); // закрываем меню после выбора, идём размещать здание
});
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e52ef64e91883de4c94450d91e119b48
@@ -15,7 +15,7 @@ public class BuildingData : ScriptableObject
[Header("Требования")]
[Tooltip("Необходимый уровень ратуши")]
public int requiredTowmHallLevel = 1;
public int requiredTownHallLevel = 1;
[Header("Стоимость")]
public ResourceCost[] cost;
@@ -0,0 +1,80 @@
using UnityEngine;
// Вешается на объект Ратуши, который уже стоит на карте.
// Хранит текущий уровень -- от него зависит, какие здания разблокированы в BuildManager.
public class TownHall : MonoBehaviour
{
public static TownHall Instance { get; private set; }
[Header("Уровень")]
public int currentLevel = 1;
public int maxLevel = 10;
[Header("Стоимость апгрейда (растёт с каждым уровнем)")]
public ResourceCost[] baseUpgradeCost;
[Tooltip("Во сколько раз дорожает апгрейд за каждый следующий уровень")]
public float costMultiplierPerLevel = 1.5f;
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
// Стоимость апгрейда на текущем уровне (растёт по экспоненте)
public ResourceCost[] GetUpgradeCost()
{
ResourceCost[] scaled = new ResourceCost[baseUpgradeCost.Length];
float multiplier = Mathf.Pow(costMultiplierPerLevel, currentLevel - 1);
for (int i = 0; i < baseUpgradeCost.Length; i++)
{
scaled[i] = new ResourceCost
{
type = baseUpgradeCost[i].type,
amount = baseUpgradeCost[i].amount * multiplier
};
}
return scaled;
}
public bool CanUpgrade()
{
if (currentLevel >= maxLevel) return false;
if (ResourceManager.Instance == null) return false;
foreach (var c in GetUpgradeCost())
{
if (!ResourceManager.Instance.HasEnough(c.type, c.amount))
return false;
}
return true;
}
public void Upgrade()
{
if (!CanUpgrade())
{
Debug.Log("[РАТУША] Не хватает ресурсов на апгрейд или максимальный уровень.");
return;
}
foreach (var c in GetUpgradeCost())
{
ResourceManager.Instance.TrySpend(c.type, c.amount);
}
currentLevel++;
Debug.Log($"[РАТУША] Улучшена до уровня {currentLevel}!");
}
void OnMouseDown()
{
Debug.Log($"[Ратуша] Клик! текущий уровень: {currentLevel}. Улучшение...");
Upgrade();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: aafc612f8913ade44b6b60cf76d8932d