MVP1 #3

Merged
vologdia merged 3 commits from MVP1 into main 2026-08-25 18:57:34 +03:00
15 changed files with 3922 additions and 2018 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d2475bd20689b914cb6e99c4a5f1fcd5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+2 -14
View File
@@ -3,18 +3,6 @@ using UnityEngine.UI;
using UnityEditor;
using TMPro;
// СКЕЛЕТ HUD БЕЗ СПРАЙТОВ.
//
// Как использовать:
// 1. Положи этот файл в папку Assets/Editor/ (обязательно "Editor" в пути, иначе не соберётся билд)
// 2. В Unity сверху появится пункт меню: Tools -> Build HUD Skeleton
// 3. Жми -- создастся полный Canvas с иерархией, якорями под мобильный экран,
// цветными заглушками вместо спрайтов и подписанными текстами.
// 4. Дизайнеры позже просто меняют Image -> Source Image на каждом объекте,
// ничего не пересобирая -- имена и структура объектов остаются те же.
//
// Референс: HUD в духе Rise of Kingdoms / Vikings (портретная мобильная стратегия)
public class HUDSkeletonBuilder : EditorWindow
{
[MenuItem("Tools/Build HUD Skeleton")]
@@ -105,8 +93,8 @@ public class HUDSkeletonBuilder : EditorWindow
RectTransform bottomBar = CreatePanel(canvasRect, "BottomNavBar", new Color(0.08f, 0.08f, 0.1f, 0.92f));
AnchorBottomStretch(bottomBar, height: 190);
string[] navNames = { "Home", "Hero", "Battle", "Troops", "Shop" };
string[] navLabels = { "ГЛАВНАЯ", "ГЕРОЙ", "БОЙ", "ВОЙСКА", "МАГАЗИН" };
string[] navNames = { "Home", "Hero", "Battle", "Troops", "Shop", "ARENA" };
string[] navLabels = { "ГЛАВНАЯ", "ГЕРОЙ", "БОЙ", "ВОЙСКА", "МАГАЗИН", "АРЕНА" };
float navWidth = 1080f / 5f;
for (int i = 0; i < navNames.Length; i++)
{
-9
View File
@@ -3,15 +3,6 @@ using UnityEngine.UI;
using UnityEditor;
using TMPro;
// СКЕЛЕТ ЗАГЛАВНОГО ЭКРАНА (Main Menu) БЕЗ СПРАЙТОВ.
//
// Как использовать:
// 1. Файл должен лежать в Assets/Editor/
// 2. Tools -> Build Main Menu Skeleton
// 3. Создастся Canvas с фоном-заглушкой, заголовком и тремя кнопками:
// Играть / Настройки / Выход.
// 4. Дизайнеры позже меняют Image -> Source Image на объектах,
// структуру и имена не трогают.
public class MainMenuSkeletonBuilder : EditorWindow
{
+1169 -29
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0930acd12dcae2c4a87c45419e24391c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+88
View File
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using UnityEngine;
// Центральное хранилище всех ресурсов игрока.
// Один объект на сцену (Singleton) -- любой скрипт обращается через ResourceManager.Instance
public enum ResourceType
{
Power, // "сила" -- как в референсе HUD
Food,
Wood,
Stone,
Iron,
Gold,
Gems
}
public class ResourceManager : MonoBehaviour
{
public static ResourceManager Instance { get; private set; }
// Событие: вызывается каждый раз, когда любой ресурс меняется.
// UI подписывается на него, чтобы обновлять текст автоматически, без Update() каждый кадр.
public event Action<ResourceType, float> OnResourceChanged;
[Header("Стартовые значения (для теста/баланса)")]
public float startWood = 0f;
public float startStone = 0f;
public float startIron = 0f;
public float startFood = 0f;
public float startGold = 0f;
public float startGems = 0f;
public float startPower = 0f;
private Dictionary<ResourceType, float> resources = new Dictionary<ResourceType, float>();
void Awake()
{
// Классический паттерн Singleton: если уже есть экземпляр -- уничтожаем дубликат
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject); // менеджер ресурсов должен жить между сценами (меню -> игра)
resources[ResourceType.Wood] = startWood;
resources[ResourceType.Stone] = startStone;
resources[ResourceType.Iron] = startIron;
resources[ResourceType.Food] = startFood;
resources[ResourceType.Gold] = startGold;
resources[ResourceType.Gems] = startGems;
resources[ResourceType.Power] = startPower;
}
// Добавить ресурс (можно и отрицательное число -- для трат)
public void AddResource(ResourceType type, float amount)
{
if (!resources.ContainsKey(type))
resources[type] = 0f;
resources[type] += amount;
if (resources[type] < 0f) resources[type] = 0f; // ресурс не уходит в минус
OnResourceChanged?.Invoke(type, resources[type]);
}
// Проверить, хватает ли ресурса (для трат/построек в будущем)
public bool HasEnough(ResourceType type, float amount)
{
return GetResource(type) >= amount;
}
// Попробовать потратить -- вернёт false, если не хватило (и ничего не спишет)
public bool TrySpend(ResourceType type, float amount)
{
if (!HasEnough(type, amount)) return false;
AddResource(type, -amount);
return true;
}
public float GetResource(ResourceType type)
{
return resources.TryGetValue(type, out float value) ? value : 0f;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5505825aa4d83af49a3a641e48af3ff7
@@ -0,0 +1,56 @@
using Unity.Microsoft.GDK;
using UnityEngine;
// Вешается на ЛЮБОЕ здание (лесопилка, каменоломня, шахта) -- настраиваешь
// в Inspector, какой ресурс и сколько оно производит. Один скрипт на все типы построек.
//
// ПРИМЕР НАСТРОЙКИ:
// Лесопилка: resourceType = Wood, amountPerTick = 5, tickInterval = 3
// Каменоломня: resourceType = Stone, amountPerTick = 4, tickInterval = 3
// Шахта: resourceType = Iron, amountPerTick = 2, tickInterval = 4 (руда добывается медленнее)
public class ResourceProducer : MonoBehaviour
{
[Header("Что производит это здание")]
public ResourceType resourceType;
public float amountPerTick = 1f;
public float tickInterval = 3f; // секунд между "тиками" производства
[Header("Уровень здания (для будущего апгрейда)")]
public int level = 1;
[Tooltip("Множитель производства за каждый уровень выше первого")]
public float perLevelMultiplier = 1.2f;
private float timer;
void Update()
{
timer += Time.deltaTime;
if (timer >= tickInterval)
{
timer -= tickInterval;
Produce();
}
}
void Produce()
{
if (ResourceManager.Instance == null)
{
Debug.LogWarning($"{gameObject.name}: ResourceManager.Instance не найден. " +
"Убедись, что на сцене есть объект с компонентом ResourceManager.");
return;
}
float finalAmount = amountPerTick * Mathf.Pow(perLevelMultiplier, level - 1);
ResourceManager.Instance.AddResource(resourceType, finalAmount);
Debug.Log($"{gameObject.name} произвел {finalAmount} {resourceType}. Итого: {ResourceManager.Instance.GetResource(resourceType)}");
}
// Вызывай при апгрейде здания (кнопка "улучшить" повесишь позже)
public void Upgrade()
{
level++;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: eb8009d4824ec6f4392a50d60eead242
+71
View File
@@ -0,0 +1,71 @@
using UnityEngine;
using TMPro;
[RequireComponent(typeof(TMP_Text))]
public class ResourceUIText : MonoBehaviour
{
public ResourceType resourceType;
[Tooltip("Сокращать большие числа (1200 -> 1.2K, 1500000 -> 1.5M), как на референсе HUD")]
public bool useShortFormat = true;
private TMP_Text label;
void Awake()
{
label = GetComponent<TMP_Text>();
}
void OnEnable()
{
// Ждём, пока ResourceManager точно проинициализируется (Awake порядок не гарантирован)
if (ResourceManager.Instance != null)
{
Subscribe();
}
else
{
Invoke(nameof(TrySubscribeDelayed), 0.1f);
}
}
void TrySubscribeDelayed()
{
if (ResourceManager.Instance != null)
Subscribe();
}
void Subscribe()
{
ResourceManager.Instance.OnResourceChanged += HandleResourceChanged;
// сразу показать текущее значение при старте, не дожидаясь первого изменения
UpdateText(ResourceManager.Instance.GetResource(resourceType));
}
void OnDisable()
{
if (ResourceManager.Instance != null)
ResourceManager.Instance.OnResourceChanged -= HandleResourceChanged;
}
void HandleResourceChanged(ResourceType type, float newValue)
{
if (type != resourceType) return; // реагируем только на "свой" ресурс
UpdateText(newValue);
}
void UpdateText(float value)
{
label.text = useShortFormat ? FormatShort(value) : Mathf.FloorToInt(value).ToString();
}
string FormatShort(float value)
{
if (value >= 1_000_000f)
return (value / 1_000_000f).ToString("0.#") + "M";
if (value >= 1_000f)
return (value / 1_000f).ToString("0.#") + "K";
return Mathf.FloorToInt(value).ToString();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8fb37401fb35fdc4496913e6c8d442eb
File diff suppressed because one or more lines are too long
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0fa6a80c1f57f044c8deaf91dc062bdf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+1
View File
@@ -1,3 +1,4 @@
<Solution>
<Project Path="Assembly-CSharp-Editor.csproj" />
<Project Path="Assembly-CSharp.csproj" />
</Solution>
+7 -3
View File
@@ -512,7 +512,10 @@ PlayerSettings:
m_BuildTargetGroupLightmapSettings: []
m_BuildTargetGroupLoadStoreDebugModeSettings: []
m_BuildTargetNormalMapEncoding: []
m_BuildTargetDefaultTextureCompressionFormat: []
m_BuildTargetDefaultTextureCompressionFormat:
- serializedVersion: 3
m_BuildTarget: Android
m_Formats: 01000000
playModeTestRunnerEnabled: 0
runPlayModeTestAsEditModeTest: 0
actionOnDotNetUnhandledException: 1
@@ -857,7 +860,8 @@ PlayerSettings:
XboxOneXTitleMemory: 8
XboxOneOverrideIdentityName:
XboxOneOverrideIdentityPublisher:
cloudServicesEnabled: {}
cloudServicesEnabled:
Purchasing: 0
luminIcon:
m_Name:
m_ModelFolderPath:
@@ -877,7 +881,7 @@ PlayerSettings:
qnxGraphicConfPath:
apiCompatibilityLevel: 6
captureStartupLogs: {}
activeInputHandler: 1
activeInputHandler: 2
windowsGamepadBackendHint: 0
enableDirectStorage: 0
cloudProjectId: 31566238-597b-4365-ab7f-7669af6314b9