fix чать инвенторя

This commit is contained in:
jze9
2026-04-16 01:51:46 +05:00
parent 057f5d3d1e
commit 6c3f72385e
35 changed files with 18160 additions and 14313 deletions

View File

@@ -78,51 +78,33 @@ public class InventoryManager : MonoBehaviour
void Start()
{
// Инициализация слотов
// Слоты созданы editor-инструментом и назначены в Inspector
if (slots == null || slots.Length == 0)
{
Debug.LogWarning("Slots not assigned! Assign them in Inspector or use InventoryUIBuilder.");
}
Debug.LogWarning("[InventoryManager] Slots not assigned! Run Tools > Setup Complete Scene first.");
else
{
foreach (var slot in slots)
{
slot?.UpdateSlotUI();
}
}
foreach (var slot in slots) slot?.UpdateSlotUI();
if (hotbarSlots != null)
{
foreach (var slot in hotbarSlots)
{
slot?.UpdateSlotUI();
}
}
foreach (var slot in hotbarSlots) slot?.UpdateSlotUI();
// Скрыть инвентарь по умолчанию
if (inventoryPanel != null)
inventoryPanel.SetActive(false);
// Убедимся что события существуют
if (InventoryEvents.Instance == null)
// Подписка на ввод
if (PlayerInput.Instance != null)
{
var eventsGO = new GameObject("InventoryEvents");
eventsGO.AddComponent<InventoryEvents>();
PlayerInput.Instance.OnToggleInventory += ToggleInventory;
PlayerInput.Instance.OnDropPressed += DropSelectedItem;
PlayerInput.Instance.OnHotbarSelect += SelectHotbarSlot;
}
else
{
Debug.LogWarning("[InventoryManager] PlayerInput not found. Run Tools > Setup Complete Scene first.");
}
// Попробуем загрузить сохранённый инвентарь
// Загрузить сохранённый инвентарь (слоты уже существуют)
LoadFromFile();
// Ensure PlayerInput exists and subscribe to input events
if (PlayerInput.Instance == null)
{
var go = new GameObject("PlayerInput");
go.AddComponent<PlayerInput>();
}
PlayerInput.Instance.OnToggleInventory += ToggleInventory;
PlayerInput.Instance.OnDropPressed += DropSelectedItem;
PlayerInput.Instance.OnHotbarSelect += SelectHotbarSlot;
}
@@ -135,14 +117,18 @@ public class InventoryManager : MonoBehaviour
if (!wasActive)
{
// Открытие инвентаря
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
InventoryEvents.Instance?.OnInventoryOpened?.Invoke();
if (PlayerInput.Instance != null)
PlayerInput.Instance.Enabled = false;
}
else
{
// Закрытие инвентаря
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
InventoryEvents.Instance?.OnInventoryClosed?.Invoke();
if (PlayerInput.Instance != null)
PlayerInput.Instance.Enabled = true;
@@ -559,8 +545,10 @@ public class InventoryManager : MonoBehaviour
}
/// <summary>
/// Сохранить инвентарь в файл (Application.persistentDataPath)
/// Сохранить инвентарь в файл (Application.persistentDataPath).
/// Доступно через правый клик на компоненте в Inspector во время Play Mode.
/// </summary>
[ContextMenu("Save Inventory")]
public void SaveToFile()
{
try
@@ -577,8 +565,11 @@ public class InventoryManager : MonoBehaviour
}
/// <summary>
/// Загрузить инвентарь из файла (если есть)
/// Загрузить инвентарь из файла (если есть).
/// Вызывается из InventoryUIBuilder после создания слотов.
/// Доступно через правый клик на компоненте в Inspector во время Play Mode.
/// </summary>
[ContextMenu("Load Inventory")]
public void LoadFromFile()
{
try

View File

@@ -1,302 +1,133 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;
public class InventorySceneSetup
/// <summary>
/// Tools > Setup Complete Scene — создаёт всё до запуска игры (editor-time).
/// Canvas инвентаря помещается внутрь Player.
/// InventoryManager получает все ссылки сразу.
/// </summary>
public static class InventorySceneSetup
{
[MenuItem("Tools/Create Player")]
public static void CreatePlayer()
[MenuItem("Tools/Setup Complete Scene")]
public static void SetupCompleteScene()
{
if (Object.FindObjectOfType<InventoryManager>() != null)
{
EditorUtility.DisplayDialog(
"Сцена уже настроена",
"InventoryManager уже существует. Удалите существующие объекты сцены перед повторной настройкой.",
"OK");
return;
}
// ── EventSystem ───────────────────────────────────────────────────────
if (Object.FindObjectOfType<EventSystem>() == null)
{
var esGO = new GameObject("EventSystem");
esGO.AddComponent<EventSystem>();
esGO.AddComponent<StandaloneInputModule>();
}
// ── InventoryEvents ───────────────────────────────────────────────────
new GameObject("InventoryEvents").AddComponent<InventoryEvents>();
// ── InventoryManager ──────────────────────────────────────────────────
var imGO = new GameObject("InventoryManager");
var im = imGO.AddComponent<InventoryManager>();
imGO.AddComponent<InventoryExampleItems>();
// ── Player ────────────────────────────────────────────────────────────
var player = CreatePlayerGO();
// ── InventoryCanvas (дочерний объект Player) ──────────────────────────
var canvasGO = new GameObject("InventoryCanvas");
canvasGO.transform.SetParent(player.transform, false);
var canvas = canvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
canvasGO.AddComponent<GraphicRaycaster>();
var uiBuilder = canvasGO.AddComponent<InventoryUIBuilder>();
// Строим весь UI прямо сейчас, до запуска игры
uiBuilder.BuildInventoryUI();
// Назначаем ссылки в InventoryManager
im.slots = uiBuilder.GetInventorySlots();
im.hotbarSlots = uiBuilder.GetHotbarSlots();
im.inventoryPanel = uiBuilder.inventoryPanelRect != null ? uiBuilder.inventoryPanelRect.gameObject : null;
im.inventoryCanvas = canvas;
// Скрыть панель инвентаря по умолчанию
if (im.inventoryPanel != null)
im.inventoryPanel.SetActive(false);
// ── PlayerInput (дочерний объект Player) ──────────────────────────────
var piGO = new GameObject("PlayerInput");
piGO.transform.SetParent(player.transform, false);
var pi = piGO.AddComponent<PlayerInput>();
var inputActions = AssetDatabase.LoadAssetAtPath<UnityEngine.InputSystem.InputActionAsset>(
"Assets/Input/PlayerControls.inputactions");
if (inputActions != null)
pi.actions = inputActions;
else
Debug.LogWarning("[Setup] Назначьте Assets/Input/PlayerControls.inputactions на компонент PlayerInput вручную.");
// ── HandEquipSystem ───────────────────────────────────────────────────
player.AddComponent<HandEquipSystem>();
Selection.activeGameObject = player;
Debug.Log("[Setup] Готово! Назначьте кость руки (handBone) в HandEquipSystem на Player.");
}
// ── Вспомогательные методы ────────────────────────────────────────────────
static GameObject CreatePlayerGO()
{
var go = new GameObject("Player");
var cc = go.AddComponent<CharacterController>();
var cc = go.AddComponent<CharacterController>();
cc.height = 1.8f;
cc.radius = 0.5f;
cc.center = new Vector3(0, 0.9f, 0);
var character = go.AddComponent<charecter>();
character.walkSpeed = 5f;
character.runSpeed = 9f;
character.crouchSpeed = 2.5f;
character.jumpHeight = 1.5f;
character.gravity = -9.81f;
var ch = go.AddComponent<charecter>();
ch.walkSpeed = 5f;
ch.runSpeed = 9f;
ch.crouchSpeed = 2.5f;
ch.jumpHeight = 1.5f;
ch.gravity = -9.81f;
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
// Визуальная капсула
var body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
body.name = "Body";
body.transform.SetParent(go.transform);
body.transform.localPosition = Vector3.zero;
GameObject.Destroy(body.GetComponent<Collider>());
body.transform.SetParent(go.transform, false);
Object.DestroyImmediate(body.GetComponent<Collider>());
GameObject head = new GameObject("Head");
head.transform.SetParent(go.transform);
// Голова
var head = new GameObject("Head");
head.transform.SetParent(go.transform, false);
head.transform.localPosition = new Vector3(0, 0.8f, 0);
character.head = head.transform;
ch.head = head.transform;
GameObject camObj = new GameObject("PlayerCamera");
camObj.transform.SetParent(head.transform);
// Камера
var camObj = new GameObject("PlayerCamera");
camObj.transform.SetParent(head.transform, false);
camObj.transform.localPosition = new Vector3(0, 0.1f, 0);
var cam = camObj.AddComponent<Camera>();
cam.tag = "MainCamera";
var cam = camObj.AddComponent<Camera>();
cam.tag = "MainCamera";
cam.nearClipPlane = 0.05f;
var existingCam = GameObject.FindGameObjectWithTag("MainCamera");
if (existingCam != null && existingCam != camObj)
{
GameObject.Destroy(existingCam);
}
var oldCam = GameObject.FindWithTag("MainCamera");
if (oldCam != null && oldCam != camObj)
Object.DestroyImmediate(oldCam);
go.tag = "Player";
go.transform.position = new Vector3(0, 2, -5);
Selection.activeGameObject = go;
Debug.Log("[Tools] Создан игрок!");
return go;
}
[MenuItem("Tools/Create Item Prefab")]
public static void CreateItemPrefab()
{
var itemGO = new GameObject("NewItem");
itemGO.AddComponent<WorldItem>();
var sphere = itemGO.AddComponent<SphereCollider>();
sphere.radius = 0.5f;
sphere.isTrigger = true;
var visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.name = "Visual";
visual.transform.SetParent(itemGO.transform);
visual.transform.localPosition = Vector3.zero;
visual.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
GameObject.Destroy(visual.GetComponent<Collider>());
Selection.activeGameObject = itemGO;
Debug.Log("[Tools] Создан префаб предмета!");
}
[MenuItem("Tools/Create Test Item in Scene")]
public static void CreateTestItem()
{
// Создаём тестовый предмет в сцене
var itemGO = new GameObject("TestItem");
itemGO.transform.position = new Vector3(0, 0.5f, 3);
// Визуал
var visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.name = "Visual";
visual.transform.SetParent(itemGO.transform);
visual.transform.localPosition = Vector3.zero;
visual.transform.localScale = Vector3.one * 0.3f;
GameObject.Destroy(visual.GetComponent<Collider>());
// Компонент предмета
var worldItem = itemGO.AddComponent<WorldItem>();
worldItem.useFactory = false;
// Тестовые данные
var testItem = ScriptableObject.CreateInstance<ItemData>();
testItem.id = "test_item";
testItem.itemName = "Тестовый предмет";
testItem.description = "Просто тестовый предмет";
testItem.type = ItemType.Material;
testItem.maxStack = 64;
// Используем Setup чтобы инициализировать визуал и включить мгновенный подбор
worldItem.Setup(testItem, 5);
// Коллайдер для подбора (trigger для обычных коллайдеров)
var box = itemGO.AddComponent<BoxCollider>();
box.isTrigger = true;
box.size = Vector3.one;
Selection.activeGameObject = itemGO;
Debug.Log("[Tools] Создан тестовый предмет! Подойдите к нему игроком.");
}
[MenuItem("Tools/Setup Inventory Scene")]
public static void SetupScene()
{
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
var rootObjects = scene.GetRootGameObjects();
foreach (var root in rootObjects)
{
if (root.GetComponent<InventoryManager>() != null)
{
Debug.LogWarning("InventoryManager уже существует!");
return;
}
}
// Создаём GameManager
var gameManager = new GameObject("GameManager");
var im = gameManager.AddComponent<InventoryManager>();
gameManager.AddComponent<InventoryExampleItems>();
gameManager.AddComponent<InventoryTester>();
// Создаём Canvas
var canvasGO = new GameObject("InventoryCanvas");
var canvas = canvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
canvasGO.AddComponent<GraphicRaycaster>();
// Ensure EventSystem exists for UI interaction
var existingES = GameObject.FindObjectOfType<UnityEngine.EventSystems.EventSystem>();
if (existingES == null)
{
var es = new GameObject("EventSystem");
es.AddComponent<UnityEngine.EventSystems.EventSystem>();
es.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
}
// Создаём панель инвентаря
var inventoryPanel = CreatePanel(canvasGO.transform, "InventoryPanel", new Color(0.15f, 0.15f, 0.15f, 0.95f));
var inventoryRect = inventoryPanel.GetComponent<RectTransform>();
SetRect(inventoryRect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(500, 300));
// Заголовок
CreateTitle(inventoryPanel.transform, "Инвентарь", new Vector2(0, 130));
// Сетка инвентаря (4x9 = 36 слотов)
var inventoryGrid = CreateGrid(inventoryPanel.transform, "InventoryGrid", 9, 4, 50, 4, new Vector2(10, 40));
im.slots = CreateSlots(inventoryGrid, "InventorySlot_", 0, 36);
// Создаём хотбар
var hotbarPanel = CreatePanel(canvasGO.transform, "HotbarPanel", new Color(0.1f, 0.1f, 0.1f, 0.9f));
var hotbarRect = hotbarPanel.GetComponent<RectTransform>();
SetRect(hotbarRect, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0, 15), new Vector2(460, 60));
var hotbarGrid = CreateGrid(hotbarPanel.transform, "HotbarGrid", 9, 1, 50, 4, new Vector2(10, 10));
im.hotbarSlots = CreateSlots(hotbarGrid, "HotbarSlot_", 0, 9);
// Подключаем ссылку на панель
im.inventoryPanel = inventoryPanel;
im.inventoryCanvas = canvas;
// Скрываем панель
inventoryPanel.SetActive(false);
Debug.Log("[Tools] Инвентарь настроен! (36 слотов + 9 хотбар)");
}
static GameObject CreatePanel(Transform parent, string name, Color color)
{
var panel = new GameObject(name);
panel.transform.SetParent(parent, false);
panel.AddComponent<RectTransform>();
panel.AddComponent<Image>().color = color;
return panel;
}
static void SetRect(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 sizeDelta)
{
rect.anchorMin = anchorMin;
rect.anchorMax = anchorMax;
rect.pivot = new Vector2(0.5f, 0.5f);
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = sizeDelta;
}
static void CreateTitle(Transform parent, string text, Vector2 position)
{
var titleObj = new GameObject("Title");
titleObj.transform.SetParent(parent, false);
var rect = titleObj.AddComponent<RectTransform>();
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(1, 1);
rect.pivot = new Vector2(0.5f, 1);
rect.anchoredPosition = position;
rect.sizeDelta = new Vector2(-20, 30);
var tmp = titleObj.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = 18;
tmp.alignment = TextAlignmentOptions.Center;
tmp.color = Color.white;
}
static Transform CreateGrid(Transform parent, string name, int cols, int rows, float cellSize, float spacing, Vector2 offset)
{
var gridObj = new GameObject(name);
gridObj.transform.SetParent(parent, false);
var rect = gridObj.AddComponent<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = offset;
rect.offsetMax = -offset;
var grid = gridObj.AddComponent<GridLayoutGroup>();
grid.cellSize = new Vector2(cellSize, cellSize);
grid.spacing = new Vector2(spacing, spacing);
grid.startCorner = GridLayoutGroup.Corner.UpperLeft;
grid.startAxis = GridLayoutGroup.Axis.Horizontal;
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
grid.constraintCount = cols;
return gridObj.transform;
}
static Slot[] CreateSlots(Transform parent, string prefix, int startIndex, int count)
{
var slots = new Slot[count];
var slotSize = 50f;
var emptyColor = new Color(0.25f, 0.25f, 0.25f, 0.8f);
for (int i = 0; i < count; i++)
{
var slotObj = new GameObject(prefix + (startIndex + i));
slotObj.transform.SetParent(parent, false);
slotObj.AddComponent<RectTransform>().sizeDelta = new Vector2(slotSize, slotSize);
var bg = slotObj.AddComponent<Image>();
bg.color = emptyColor;
var slot = slotObj.AddComponent<Slot>();
slot.slotIndex = startIndex + i;
// Icon
var iconObj = new GameObject("Icon");
iconObj.transform.SetParent(slotObj.transform, false);
var iconRect = iconObj.AddComponent<RectTransform>();
iconRect.anchorMin = Vector2.zero;
iconRect.anchorMax = Vector2.one;
iconRect.offsetMin = new Vector2(4, 4);
iconRect.offsetMax = new Vector2(-4, -4);
slot.iconImage = iconObj.AddComponent<Image>();
slot.iconImage.preserveAspect = true;
slot.iconImage.enabled = false;
// Amount panel
var amountObj = new GameObject("AmountPanel");
amountObj.transform.SetParent(slotObj.transform, false);
var amountRect = amountObj.AddComponent<RectTransform>();
amountRect.anchorMin = Vector2.one;
amountRect.anchorMax = Vector2.one;
amountRect.pivot = new Vector2(1, 0);
amountRect.anchoredPosition = new Vector2(-2, 2);
amountRect.sizeDelta = new Vector2(22, 22);
slot.amountPanel = amountObj;
slot.amountPanel.SetActive(false);
amountObj.AddComponent<Image>().color = new Color(0, 0, 0, 0.7f);
var amountTextObj = new GameObject("Text");
amountTextObj.transform.SetParent(amountObj.transform, false);
amountTextObj.AddComponent<RectTransform>().sizeDelta = new Vector2(20, 20);
slot.amountText = amountTextObj.AddComponent<TextMeshProUGUI>();
slot.amountText.fontSize = 14;
slot.amountText.alignment = TextAlignmentOptions.Right;
slot.amountText.color = Color.white;
slot.amountText.fontStyle = FontStyles.Bold;
// Drag & Drop
var dragDrop = slotObj.AddComponent<InventoryDragDrop>();
dragDrop.slot = slot;
dragDrop.slotIndex = slot.slotIndex;
slots[i] = slot;
}
return slots;
}
}
}

View File

@@ -1,101 +0,0 @@
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Автоматически настраивает всю систему инвентаря на сцене
/// Добавить на пустой GameObject "GameManager" в сцене
/// </summary>
public class InventorySetup : MonoBehaviour
{
[Header("Настройки")]
public bool createUI = true;
public bool createManagers = true;
void Awake()
{
// Не создаём если уже есть на сцене (после ручной настройки)
if (FindObjectOfType<InventoryManager>() != null)
{
Debug.Log("[InventorySetup] Inventory already exists, skipping creation");
return;
}
if (createManagers)
SetupManagers();
if (createUI)
SetupUI();
}
void SetupManagers()
{
var imGO = new GameObject("InventoryManager");
imGO.transform.SetParent(transform);
imGO.AddComponent<InventoryManager>();
var itemsGO = new GameObject("InventoryItems");
itemsGO.transform.SetParent(transform);
itemsGO.AddComponent<InventoryExampleItems>();
var testerGO = new GameObject("InventoryTester");
testerGO.transform.SetParent(transform);
testerGO.AddComponent<InventoryTester>();
Debug.Log("[InventorySetup] Managers created");
}
void SetupUI()
{
var canvasGO = new GameObject("InventoryCanvas");
canvasGO.transform.SetParent(transform);
var canvas = canvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
canvasGO.AddComponent<GraphicRaycaster>();
canvasGO.AddComponent<InventoryUIBuilder>();
Debug.Log("[InventorySetup] UI created");
}
}
#if UNITY_EDITOR
/// <summary>
/// Кнопка в инспекторе для быстрой настройки сцены
/// </summary>
[CustomEditor(typeof(InventorySetup))]
public class InventorySetupEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Настроить сцену для инвентаря"))
{
var target = (InventorySetup)this.target;
if (FindObjectOfType<InventoryManager>() == null)
{
target.createManagers = true;
target.createUI = true;
Debug.Log("Инвентарь будет настроен при запуске");
}
else
{
Debug.LogWarning("InventoryManager уже существует на сцене!");
}
}
GUILayout.Space(10);
if (GUILayout.Button("Открыть сцену в Unity"))
{
EditorApplication.ExecuteMenuItem("Window/General/Scene");
}
}
}
#endif

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 9a832ca7ed419e6479e1cc308a222f33

View File

@@ -3,7 +3,9 @@ using UnityEngine.UI;
using TMPro;
/// <summary>
/// Создаёт UI инвентаря программно с использованием TextMeshPro
/// Создаёт UI инвентаря программно.
/// Вызывается ТОЛЬКО из редакторных инструментов (InventorySceneSetup, PlayerBuilderTool).
/// Никакого runtime-создания: Start() отсутствует.
/// </summary>
public class InventoryUIBuilder : MonoBehaviour
{
@@ -22,138 +24,113 @@ public class InventoryUIBuilder : MonoBehaviour
[SerializeField] private Color inventoryPanelColor = new Color(0.15f, 0.15f, 0.15f, 0.95f);
[SerializeField] private Color hotbarPanelColor = new Color(0.1f, 0.1f, 0.1f, 0.9f);
[SerializeField] private Color slotEmptyColor = new Color(0.25f, 0.25f, 0.25f, 0.8f);
[SerializeField] private Color slotFilledColor = new Color(0.35f, 0.35f, 0.35f, 0.95f);
[Header("Container References")]
public RectTransform inventoryPanelRect;
public RectTransform hotbarPanelRect;
public Canvas inventoryCanvas;
void Start()
{
// Проверяем, не запущен ли уже BuildInventoryUI
if (InventoryManager.Instance != null &&
InventoryManager.Instance.slots != null &&
InventoryManager.Instance.slots.Length > 0)
{
Debug.Log("[InventoryUIBuilder] UI already built, skipping");
return;
}
// Созданные слоты — читаются редакторным инструментом после BuildInventoryUI()
[SerializeField] private Slot[] _inventorySlots;
[SerializeField] private Slot[] _hotbarSlots;
BuildInventoryUI();
}
public Slot[] GetInventorySlots() => _inventorySlots;
public Slot[] GetHotbarSlots() => _hotbarSlots;
// ── Построение UI ────────────────────────────────────────────────────────
public void BuildInventoryUI()
{
// Создаём или получаем Canvas
// Получаем или добавляем Canvas
var canvas = GetComponent<Canvas>();
if (canvas == null)
{
canvas = gameObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
gameObject.AddComponent<GraphicRaycaster>();
}
inventoryCanvas = canvas;
// Настраиваем EventSystem если нет
if (FindObjectOfType<TMPro.TMP_InputField>() != null || FindObjectsOfType<TMPro.TextMeshProUGUI>().Length > 0)
{
// EventSystem уже есть
}
// Создаём панель инвентаря
// Панель инвентаря
GameObject inventoryPanel = CreatePanel("InventoryPanel", transform, inventoryPanelColor);
inventoryPanelRect = inventoryPanel.GetComponent<RectTransform>();
float inventoryWidth = inventoryCols * (slotSize + slotSpacing) + inventoryPadding * 2;
float inventoryHeight = inventoryRows * (slotSize + slotSpacing) + inventoryPadding * 2 + 40; // +40 для заголовка
float inventoryWidth = inventoryCols * (slotSize + slotSpacing) + inventoryPadding * 2;
float inventoryHeight = inventoryRows * (slotSize + slotSpacing) + inventoryPadding * 2 + 40;
inventoryPanelRect.anchorMin = new Vector2(0.5f, 0.5f);
inventoryPanelRect.anchorMax = new Vector2(0.5f, 0.5f);
inventoryPanelRect.pivot = new Vector2(0.5f, 0.5f);
inventoryPanelRect.anchorMin = new Vector2(0.5f, 0.5f);
inventoryPanelRect.anchorMax = new Vector2(0.5f, 0.5f);
inventoryPanelRect.pivot = new Vector2(0.5f, 0.5f);
inventoryPanelRect.anchoredPosition = Vector2.zero;
inventoryPanelRect.sizeDelta = new Vector2(inventoryWidth, inventoryHeight);
inventoryPanelRect.sizeDelta = new Vector2(inventoryWidth, inventoryHeight);
// Заголовок
CreateTitle(inventoryPanel.transform, "Инвентарь", new Vector2(0, inventoryHeight / 2 - 20));
// Сетка инвентаря (4x9 = 36 слотов)
float gridHeight = inventoryRows * (slotSize + slotSpacing);
var inventoryGrid = CreateGrid(
inventoryPanel.transform,
"InventoryGrid",
inventoryCols,
inventoryRows,
slotSize,
slotSpacing,
new Vector2(inventoryPadding, inventoryPadding + 30)
);
InventoryManager.Instance.slots = CreateSlots(inventoryGrid, "InventorySlot_", 0, inventoryCols * inventoryRows);
inventoryPanel.transform, "InventoryGrid",
inventoryCols, inventoryRows,
slotSize, slotSpacing,
new Vector2(inventoryPadding, inventoryPadding + 30));
// Создаём хотбар
_inventorySlots = CreateSlots(inventoryGrid, "InventorySlot_", 0, inventoryCols * inventoryRows);
// Хотбар
GameObject hotbarPanel = CreatePanel("HotbarPanel", transform, hotbarPanelColor);
hotbarPanelRect = hotbarPanel.GetComponent<RectTransform>();
float hotbarWidth = hotbarSlots * (slotSize + slotSpacing) + hotbarPadding * 2;
hotbarPanelRect.anchorMin = new Vector2(0.5f, 0f);
hotbarPanelRect.anchorMax = new Vector2(0.5f, 0f);
hotbarPanelRect.pivot = new Vector2(0.5f, 0f);
hotbarPanelRect.anchorMin = new Vector2(0.5f, 0f);
hotbarPanelRect.anchorMax = new Vector2(0.5f, 0f);
hotbarPanelRect.pivot = new Vector2(0.5f, 0f);
hotbarPanelRect.anchoredPosition = new Vector2(0, 15);
hotbarPanelRect.sizeDelta = new Vector2(hotbarWidth, slotSize + hotbarPadding * 2);
hotbarPanelRect.sizeDelta = new Vector2(hotbarWidth, slotSize + hotbarPadding * 2);
var hotbarGrid = CreateGrid(
hotbarPanel.transform,
"HotbarGrid",
hotbarSlots,
1,
slotSize,
slotSpacing,
new Vector2(hotbarPadding, hotbarPadding)
);
InventoryManager.Instance.hotbarSlots = CreateSlots(hotbarGrid, "HotbarSlot_", 0, hotbarSlots);
hotbarPanel.transform, "HotbarGrid",
hotbarSlots, 1,
slotSize, slotSpacing,
new Vector2(hotbarPadding, hotbarPadding));
// Настраиваем InventoryManager
InventoryManager.Instance.inventoryPanel = inventoryPanel;
InventoryManager.Instance.inventoryCanvas = inventoryCanvas;
_hotbarSlots = CreateSlots(hotbarGrid, "HotbarSlot_", 0, hotbarSlots);
Debug.Log($"[InventoryUIBuilder] Created {inventoryCols * inventoryRows} inventory slots + {hotbarSlots} hotbar slots");
Debug.Log($"[InventoryUIBuilder] Built {inventoryCols * inventoryRows} inventory slots + {hotbarSlots} hotbar slots");
}
// ── Вспомогательные методы ────────────────────────────────────────────────
GameObject CreatePanel(string name, Transform parent, Color color)
{
GameObject panel = new GameObject(name);
var panel = new GameObject(name);
panel.transform.SetParent(parent, false);
var rect = panel.AddComponent<RectTransform>();
panel.AddComponent<RectTransform>();
panel.AddComponent<Image>().color = color;
return panel;
}
void CreateTitle(Transform parent, string text, Vector2 position)
{
GameObject titleObj = new GameObject("Title");
var titleObj = new GameObject("Title");
titleObj.transform.SetParent(parent, false);
var rect = titleObj.AddComponent<RectTransform>();
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(1, 1);
rect.pivot = new Vector2(0.5f, 1);
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(1, 1);
rect.pivot = new Vector2(0.5f, 1);
rect.anchoredPosition = position;
rect.sizeDelta = new Vector2(-20, 30);
rect.sizeDelta = new Vector2(-20, 30);
var tmp = titleObj.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = 18;
tmp.text = text;
tmp.fontSize = 18;
tmp.alignment = TextAlignmentOptions.Center;
tmp.color = Color.white;
tmp.color = Color.white;
}
Transform CreateGrid(Transform parent, string name, int cols, int rows, float cellSize, float spacing, Vector2 offset)
{
GameObject gridObj = new GameObject(name);
var gridObj = new GameObject(name);
gridObj.transform.SetParent(parent, false);
var rect = gridObj.AddComponent<RectTransform>();
@@ -163,13 +140,13 @@ public class InventoryUIBuilder : MonoBehaviour
rect.offsetMax = -offset;
var grid = gridObj.AddComponent<GridLayoutGroup>();
grid.cellSize = new Vector2(cellSize, cellSize);
grid.spacing = new Vector2(spacing, spacing);
grid.startCorner = GridLayoutGroup.Corner.UpperLeft;
grid.startAxis = GridLayoutGroup.Axis.Horizontal;
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
grid.cellSize = new Vector2(cellSize, cellSize);
grid.spacing = new Vector2(spacing, spacing);
grid.startCorner = GridLayoutGroup.Corner.UpperLeft;
grid.startAxis = GridLayoutGroup.Axis.Horizontal;
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
grid.constraintCount = cols;
grid.padding = new RectOffset(0, 0, 0, 0);
grid.padding = new RectOffset(0, 0, 0, 0);
return gridObj.transform;
}
@@ -180,22 +157,21 @@ public class InventoryUIBuilder : MonoBehaviour
for (int i = 0; i < count; i++)
{
GameObject slotObj = new GameObject(prefix + (startIndex + i));
var slotObj = new GameObject(prefix + (startIndex + i));
slotObj.transform.SetParent(parent, false);
var rect = slotObj.AddComponent<RectTransform>();
rect.sizeDelta = new Vector2(slotSize, slotSize);
// Background
var bg = slotObj.AddComponent<Image>();
bg.color = slotEmptyColor;
// Slot компонент
var slot = slotObj.AddComponent<Slot>();
slot.slotIndex = startIndex + i;
slot.slotIndex = startIndex + i;
slot.slotBackground = bg;
// Icon
GameObject iconObj = new GameObject("Icon");
// Иконка
var iconObj = new GameObject("Icon");
iconObj.transform.SetParent(slotObj.transform, false);
var iconRect = iconObj.AddComponent<RectTransform>();
iconRect.anchorMin = Vector2.zero;
@@ -206,41 +182,37 @@ public class InventoryUIBuilder : MonoBehaviour
slot.iconImage.preserveAspect = true;
slot.iconImage.enabled = false;
// Amount panel
GameObject amountObj = new GameObject("AmountPanel");
// Количество
var amountObj = new GameObject("AmountPanel");
amountObj.transform.SetParent(slotObj.transform, false);
var amountRect = amountObj.AddComponent<RectTransform>();
amountRect.anchorMin = Vector2.one;
amountRect.anchorMax = Vector2.one;
amountRect.pivot = new Vector2(1, 0);
amountRect.anchorMin = Vector2.one;
amountRect.anchorMax = Vector2.one;
amountRect.pivot = new Vector2(1, 0);
amountRect.anchoredPosition = new Vector2(-2, 2);
amountRect.sizeDelta = new Vector2(22, 22);
amountRect.sizeDelta = new Vector2(22, 22);
slot.amountPanel = amountObj;
var amountBg = amountObj.AddComponent<Image>();
amountBg.color = new Color(0, 0, 0, 0.7f);
amountObj.AddComponent<Image>().color = new Color(0, 0, 0, 0.7f);
GameObject amountTextObj = new GameObject("Text");
var amountTextObj = new GameObject("Text");
amountTextObj.transform.SetParent(amountObj.transform, false);
var amountTextRect = amountTextObj.AddComponent<RectTransform>();
amountTextRect.anchorMin = Vector2.zero;
amountTextRect.anchorMax = Vector2.one;
slot.amountText = amountTextObj.AddComponent<TextMeshProUGUI>();
slot.amountText.fontSize = 14;
slot.amountText.alignment = TextAlignmentOptions.Right;
slot.amountText.color = Color.white;
slot.amountText.fontStyle = FontStyles.Bold;
slot.amountText = amountTextObj.AddComponent<TextMeshProUGUI>();
slot.amountText.fontSize = 14;
slot.amountText.alignment = TextAlignmentOptions.Right;
slot.amountText.color = Color.white;
slot.amountText.fontStyle = FontStyles.Bold;
// Применяем настройки через сеттер
slot.amountPanel.SetActive(false);
slot.slotBackground = bg;
// Drag & Drop
var dragDrop = slotObj.AddComponent<InventoryDragDrop>();
dragDrop.slot = slot;
dragDrop.slot = slot;
dragDrop.slotIndex = slot.slotIndex;
// Подписка на события слота
slot.OnSlotClicked += OnSlotClicked;
slots[i] = slot;
@@ -249,43 +221,38 @@ public class InventoryUIBuilder : MonoBehaviour
return slots;
}
// ── Выделение слота (runtime) ─────────────────────────────────────────────
private int _selectedSlot = -1;
private void OnSlotClicked(Slot slot)
{
// Снимаем выделение с предыдущего
if (_selectedSlot >= 0 && _selectedSlot < InventoryManager.Instance.slots.Length)
{
InventoryManager.Instance.slots[_selectedSlot].SetSelected(false);
}
if (_selectedSlot >= 0 && _inventorySlots != null && _selectedSlot < _inventorySlots.Length)
_inventorySlots[_selectedSlot].SetSelected(false);
// Выделяем новый
slot.SetSelected(true);
_selectedSlot = slot.slotIndex;
Debug.Log($"[UI] Selected slot: {_selectedSlot} - {slot.GetItem()?.itemName ?? "Empty"}");
}
/// <summary>
/// Пересоздать UI (например, при изменении настроек)
/// </summary>
// ── Утилиты ───────────────────────────────────────────────────────────────
public void Rebuild()
{
// Удаляем старые панели
if (inventoryPanelRect != null)
Destroy(inventoryPanelRect.gameObject);
if (hotbarPanelRect != null)
Destroy(hotbarPanelRect.gameObject);
_inventorySlots = null;
_hotbarSlots = null;
BuildInventoryUI();
}
/// <summary>
/// Показать/скрыть инвентарь
/// </summary>
public void ToggleInventory(bool show)
{
if (inventoryPanelRect != null)
inventoryPanelRect.gameObject.SetActive(show);
}
}
}

View File

@@ -1,30 +1,33 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using TMPro;
/// <summary>
/// Инструмент создания предметов.
/// Tools > Item Builder
/// </summary>
public class ItemBuilderTool : EditorWindow
{
// Основные данные
private string itemName = "NewItem";
private string itemId = "";
private string description = "";
private ItemType itemType = ItemType.Material;
private string itemName = "NewItem";
private string itemId = "";
private string description = "";
private ItemType itemType = ItemType.Material;
// Визуал
private GameObject modelPrefab;
private Sprite icon;
private Color previewColor = Color.gray;
private Sprite icon;
private Color previewColor = Color.gray;
// Свойства
private int maxStack = 64;
private int value = 1;
private float weight = 1f;
private bool isDroppable = true;
private bool isStackable = true;
private int maxStack = 64;
private int value = 1;
private float weight = 1f;
private bool isDroppable = true;
private bool isStackable = true;
// Префаб
private GameObject worldItemPrefab;
// Pickup
private float interactRange = 2.5f;
[MenuItem("Tools/Item Builder")]
public static void ShowWindow()
@@ -38,67 +41,59 @@ public class ItemBuilderTool : EditorWindow
// Основные данные
GUILayout.Label("Основное", EditorStyles.miniBoldLabel);
itemName = EditorGUILayout.TextField("Название", itemName);
itemId = EditorGUILayout.TextField("ID", itemId);
itemName = EditorGUILayout.TextField("Название", itemName);
itemId = EditorGUILayout.TextField("ID (оставь пустым для GUID)", itemId);
description = EditorGUILayout.TextField("Описание", description);
itemType = (ItemType)EditorGUILayout.EnumPopup("Тип предмета", itemType);
itemType = (ItemType)EditorGUILayout.EnumPopup("Тип предмета", itemType);
GUILayout.Space(10);
// Визуал
GUILayout.Label("Визуал", EditorStyles.miniBoldLabel);
modelPrefab = (GameObject)EditorGUILayout.ObjectField("3D модель", modelPrefab, typeof(GameObject), false);
icon = (Sprite)EditorGUILayout.ObjectField("Иконка", icon, typeof(Sprite), false);
modelPrefab = (GameObject)EditorGUILayout.ObjectField("3D модель (префаб)", modelPrefab, typeof(GameObject), false);
icon = (Sprite)EditorGUILayout.ObjectField("Иконка", icon, typeof(Sprite), false);
if (icon == null)
{
previewColor = EditorGUILayout.ColorField("Цвет иконки", previewColor);
}
previewColor = EditorGUILayout.ColorField("Цвет иконки (заглушка)", previewColor);
GUILayout.Space(10);
// Свойства
GUILayout.Label("Свойства", EditorStyles.miniBoldLabel);
maxStack = EditorGUILayout.IntField("Макс. стек", maxStack);
value = EditorGUILayout.IntField("Цена", value);
weight = EditorGUILayout.FloatField("Вес", weight);
maxStack = EditorGUILayout.IntField("Макс. стек", maxStack);
value = EditorGUILayout.IntField("Цена", value);
weight = EditorGUILayout.FloatField("Вес", weight);
isDroppable = EditorGUILayout.Toggle("Можно выбросить", isDroppable);
isStackable = EditorGUILayout.Toggle("Стек", isStackable);
isStackable = EditorGUILayout.Toggle("Стекируемый", isStackable);
GUILayout.Space(10);
// Префаб
GUILayout.Label("Префаб предмета в мире", EditorStyles.miniBoldLabel);
worldItemPrefab = (GameObject)EditorGUILayout.ObjectField("WorldItem Prefab", worldItemPrefab, typeof(GameObject), false);
GUILayout.Label("Подбор", EditorStyles.miniBoldLabel);
interactRange = EditorGUILayout.FloatField("Дальность подбора (E)", interactRange);
GUILayout.Space(20);
// Кнопки
GUI.color = Color.green;
if (GUILayout.Button("Создать ItemData"))
{
if (GUILayout.Button("Создать ItemData (ScriptableObject)"))
CreateItemData();
}
GUI.color = Color.cyan;
if (GUILayout.Button("Создать WorldItem в сцене"))
{
CreateWorldItem();
}
GUI.color = Color.yellow;
if (GUILayout.Button("Создать префаб"))
{
if (GUILayout.Button("Создать WorldItem как префаб"))
CreateItemPrefab();
}
GUI.color = Color.white;
GUILayout.Space(10);
EditorGUILayout.HelpBox(
"1. Заполните данные предмета\n" +
"2. Назначьте модель и иконку\n" +
"3. Создайте ItemData или префаб",
"Подбор предметов — только по кнопке E (авто-pickup при касании убран намеренно).\n" +
"1. Заполните данные → Создайте ItemData\n" +
"2. Назначьте ItemData в InventoryManager.itemDatabase\n" +
"3. Создайте WorldItem для размещения предметов на сцене",
MessageType.Info);
}
@@ -112,105 +107,101 @@ public class ItemBuilderTool : EditorWindow
var item = ScriptableObject.CreateInstance<ItemData>();
item.id = string.IsNullOrEmpty(itemId) ? GUID.Generate().ToString() : itemId;
item.itemName = itemName;
item.id = string.IsNullOrEmpty(itemId) ? System.Guid.NewGuid().ToString() : itemId;
item.itemName = itemName;
item.description = description;
item.type = itemType;
item.type = itemType;
item.modelPrefab = modelPrefab;
item.icon = icon != null ? icon : CreatePlaceholderIcon();
item.maxStack = maxStack;
item.value = value;
item.weight = weight;
item.icon = icon != null ? icon : CreatePlaceholderIcon();
item.maxStack = maxStack;
item.value = value;
item.weight = weight;
item.isDroppable = isDroppable;
item.isStackable = isStackable;
// Сохраняем
string path = $"Assets/Inventory/Items/{itemName}.asset";
string folder = System.IO.Path.GetDirectoryName(path);
string folder = "Assets/Inventory/Items";
if (!System.IO.Directory.Exists(folder))
{
System.IO.Directory.CreateDirectory(folder);
}
string path = $"{folder}/{itemName}.asset";
AssetDatabase.CreateAsset(item, path);
AssetDatabase.SaveAssets();
Selection.activeObject = item;
Debug.Log($"[ItemBuilder] Создан: {itemName}");
Debug.Log($"[ItemBuilder] Создан: {itemName} → {path}");
}
void CreateWorldItem()
{
if (modelPrefab == null)
var itemGO = new GameObject(itemName);
// Визуал
GameObject visual;
if (modelPrefab != null)
{
// Создаём простой визуал
var itemGO = new GameObject(itemName);
var visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.name = "Visual";
visual.transform.SetParent(itemGO.transform);
visual.transform.localPosition = Vector3.zero;
visual.transform.localScale = Vector3.one * 0.3f;
DestroyImmediate(visual.GetComponent<Collider>());
var worldItem = itemGO.AddComponent<WorldItem>();
var col = itemGO.AddComponent<SphereCollider>();
col.radius = 0.5f;
col.isTrigger = true;
itemGO.transform.position = Vector3.zero;
Selection.activeGameObject = itemGO;
visual = (GameObject)PrefabUtility.InstantiatePrefab(modelPrefab, itemGO.transform);
}
else
{
var itemGO = Instantiate(modelPrefab);
itemGO.name = itemName;
itemGO.AddComponent<WorldItem>();
itemGO.transform.position = Vector3.zero;
Selection.activeGameObject = itemGO;
visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.transform.SetParent(itemGO.transform, false);
visual.transform.localScale = Vector3.one * 0.3f;
DestroyImmediate(visual.GetComponent<Collider>());
}
visual.name = "Visual";
visual.transform.localPosition = Vector3.zero;
// WorldItem компонент
var worldItem = itemGO.AddComponent<WorldItem>();
worldItem.interactRange = interactRange;
// Trigger-коллайдер для OverlapSphere (не для авто-pickup!)
var col = itemGO.AddComponent<SphereCollider>();
col.radius = 0.5f;
col.isTrigger = true;
itemGO.transform.position = Vector3.zero;
Selection.activeGameObject = itemGO;
Debug.Log($"[ItemBuilder] Создан WorldItem: {itemName}");
}
void CreateItemPrefab()
{
string path = EditorUtility.SaveFilePanelInProject(
"Сохранить префаб",
"Сохранить WorldItem префаб",
itemName,
"prefab",
"Выберите место");
"Выберите место сохранения");
if (string.IsNullOrEmpty(path)) return;
GameObject prefab;
var prefabGO = new GameObject(itemName);
// Визуал
GameObject visual;
if (modelPrefab != null)
{
prefab = Instantiate(modelPrefab);
prefab.name = itemName;
visual = (GameObject)PrefabUtility.InstantiatePrefab(modelPrefab, prefabGO.transform);
}
else
{
prefab = new GameObject(itemName);
var visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.name = "Visual";
visual.transform.SetParent(prefab.transform);
visual.transform.localPosition = Vector3.zero;
visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.transform.SetParent(prefabGO.transform, false);
visual.transform.localScale = Vector3.one * 0.3f;
DestroyImmediate(visual.GetComponent<Collider>());
}
visual.name = "Visual";
visual.transform.localPosition = Vector3.zero;
prefab.AddComponent<WorldItem>();
var worldItem = prefabGO.AddComponent<WorldItem>();
worldItem.interactRange = interactRange;
var col = prefab.GetComponent<Collider>();
if (col == null)
{
var sphere = prefab.AddComponent<SphereCollider>();
sphere.radius = 0.5f;
sphere.isTrigger = true;
}
var col = prefabGO.AddComponent<SphereCollider>();
col.radius = 0.5f;
col.isTrigger = true;
GameObject savedPrefab = PrefabUtility.SaveAsPrefabAsset(prefab, path);
DestroyImmediate(prefab);
GameObject savedPrefab = PrefabUtility.SaveAsPrefabAsset(prefabGO, path);
DestroyImmediate(prefabGO);
Selection.activeObject = savedPrefab;
Debug.Log($"[ItemBuilder] Префаб сохранён: {path}");
@@ -218,14 +209,12 @@ public class ItemBuilderTool : EditorWindow
Sprite CreatePlaceholderIcon()
{
var tex = new Texture2D(64, 64);
var tex = new Texture2D(64, 64);
var colors = new Color[64 * 64];
for (int i = 0; i < colors.Length; i++)
{
colors[i] = previewColor;
}
tex.SetPixels(colors);
tex.Apply();
return Sprite.Create(tex, new Rect(0, 0, 64, 64), new Vector2(0.5f, 0.5f));
}
}
}

View File

@@ -5,6 +5,10 @@ using System.Collections.Generic;
using UnityEditor;
#endif
/// <summary>
/// Компонент для генерации ItemData ScriptableObject из настроек в Inspector.
/// Добавьте на GameObject, заполните поля и нажмите "Generate ItemData" в Inspector.
/// </summary>
public class ItemGenerator : MonoBehaviour
{
[Header("Item Template")]
@@ -26,27 +30,25 @@ public class ItemGenerator : MonoBehaviour
public ItemData GenerateItemData()
{
if (string.IsNullOrEmpty(itemId))
{
itemId = System.Guid.NewGuid().ToString();
}
var itemData = ScriptableObject.CreateInstance<ItemData>();
itemData.id = itemId;
itemData.itemName = itemName;
itemData.id = itemId;
itemData.itemName = itemName;
itemData.description = description;
itemData.type = itemType;
itemData.type = itemType;
itemData.modelPrefab = modelPrefab;
itemData.icon = icon;
itemData.maxStack = maxStack;
itemData.icon = icon;
itemData.maxStack = maxStack;
itemData.isCraftable = isCraftable;
if (isCraftable && craftIngredients != null && craftIngredients.Length > 0)
{
itemData.craftingRecipe = new ItemData.CraftingRecipe
{
ingredients = craftIngredients,
amounts = craftAmounts,
resultId = craftResultId,
ingredients = craftIngredients,
amounts = craftAmounts,
resultId = craftResultId,
resultAmount = craftResultAmount
};
}
@@ -57,187 +59,15 @@ public class ItemGenerator : MonoBehaviour
public static List<ItemData> GenerateAll(List<ItemGenerator> generators)
{
var items = new List<ItemData>();
foreach (var generator in generators)
foreach (var gen in generators)
{
if (generator != null)
{
items.Add(generator.GenerateItemData());
}
if (gen != null)
items.Add(gen.GenerateItemData());
}
return items;
}
}
/// <summary>
/// Предмет в мире - подбирается игроком
/// </summary>
public class WorldItem : MonoBehaviour
{
public ItemData itemData;
public int amount = 1;
[Header("Settings")]
public float pickupDelay = 0f;
public bool useFactory = true;
private bool _canPickup;
public void Setup(ItemData data, int count)
{
itemData = data;
amount = count;
if (data.modelPrefab != null)
{
var model = Instantiate(data.modelPrefab, transform);
model.transform.localPosition = Vector3.zero;
}
// Задержка перед подбором
if (pickupDelay > 0)
Invoke(nameof(EnablePickup), pickupDelay);
else
_canPickup = true;
}
private void EnablePickup() => _canPickup = true;
void OnTriggerEnter(Collider other)
{
TryPickup(other);
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
TryPickup(hit.controller);
}
/// <summary>
/// Подобрать предмет вручную (по кнопке)
/// </summary>
public bool TryPickupByButton()
{
Debug.Log($"[TryPickup] _canPickup={_canPickup}, itemData={itemData?.itemName}, amount={amount}, Instance={InventoryManager.Instance != null}");
if (!_canPickup)
{
Debug.LogWarning("[TryPickup] Cannot pickup - not ready yet");
return false;
}
if (itemData == null)
{
Debug.LogWarning("[TryPickup] itemData is null!");
return false;
}
if (InventoryManager.Instance == null)
{
Debug.LogWarning("[TryPickup] InventoryManager.Instance is null!");
return false;
}
int remaining = InventoryManager.Instance.AddItem(itemData, amount);
Debug.Log($"[TryPickup] AddItem returned remaining={remaining}");
if (remaining <= 0)
{
Destroy(gameObject);
return true;
}
else
{
amount = remaining;
return false;
}
}
void TryPickup(Collider other)
{
if (!_canPickup) return;
if (other == null) return;
if (!other.CompareTag("Player")) return;
if (useFactory && InventoryItemFactory.Instance != null)
{
InventoryItemFactory.Instance.PickupItem(this, this);
}
else
{
// Fallback - старый способ
int remaining = InventoryManager.Instance.AddItem(itemData, amount);
if (remaining <= 0)
{
Destroy(gameObject);
}
else
{
amount = remaining;
}
}
}
/// <summary>
/// Выбросить предмет с силой
/// </summary>
public void Throw(Vector3 velocity)
{
var rb = GetComponent<Rigidbody>();
if (rb == null) rb = gameObject.AddComponent<Rigidbody>();
rb.linearVelocity = velocity;
}
}
/// <summary>
/// Точка спавна предмета
/// </summary>
public class ItemSpawner : MonoBehaviour
{
public ItemData item;
public int amount = 1;
public float despawnTime = 300f;
public bool spawnOnStart = false;
public bool useFactory = true;
public void Setup(ItemData itemData, int itemAmount, float despawn)
{
item = itemData;
amount = itemAmount;
despawnTime = despawn;
}
void Start()
{
if (spawnOnStart) Spawn();
}
public void Spawn()
{
if (useFactory && InventoryItemFactory.Instance != null)
{
var worldItem = InventoryItemFactory.Instance.CreateWorldItem(item, amount, transform.position);
if (worldItem != null && despawnTime > 0)
{
Destroy(worldItem.gameObject, despawnTime);
}
}
else
{
// Fallback - старый способ
var prefab = Resources.Load<GameObject>("Prefabs/WorldItem");
if (prefab != null)
{
var worldItem = Instantiate(prefab, transform.position, Quaternion.identity).GetComponent<WorldItem>();
worldItem.Setup(item, amount);
Destroy(worldItem.gameObject, despawnTime);
}
}
}
public void Despawn()
{
Destroy(gameObject);
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(ItemGenerator))]
public class ItemGeneratorEditor : Editor
@@ -246,17 +76,23 @@ public class ItemGeneratorEditor : Editor
{
base.OnInspectorGUI();
GUILayout.Space(8);
if (GUILayout.Button("Generate ItemData"))
{
var generator = (ItemGenerator)target;
var itemData = generator.GenerateItemData();
var itemData = generator.GenerateItemData();
string path = $"Assets/Inventory/Items/{itemData.itemName}.asset";
string folder = "Assets/Inventory/Items";
if (!System.IO.Directory.Exists(folder))
System.IO.Directory.CreateDirectory(folder);
string path = $"{folder}/{itemData.itemName}.asset";
AssetDatabase.CreateAsset(itemData, path);
AssetDatabase.SaveAssets();
Debug.Log($"Created item: {itemData.itemName} at {path}");
Debug.Log($"[ItemGenerator] Created item: {itemData.itemName} at {path}");
}
}
}
#endif
#endif

View File

@@ -0,0 +1,39 @@
using UnityEngine;
/// <summary>
/// Точка спавна предмета в мире. Создаёт WorldItem при старте или по вызову Spawn().
/// </summary>
public class ItemSpawner : MonoBehaviour
{
public ItemData item;
public int amount = 1;
public float despawnTime = 300f;
public bool spawnOnStart = false;
public void Setup(ItemData itemData, int itemAmount, float despawn)
{
item = itemData;
amount = itemAmount;
despawnTime = despawn;
}
void Start()
{
if (spawnOnStart) Spawn();
}
public void Spawn()
{
if (InventoryItemFactory.Instance != null)
{
var worldItem = InventoryItemFactory.Instance.CreateWorldItem(item, amount, transform.position);
if (worldItem != null && despawnTime > 0)
Destroy(worldItem.gameObject, despawnTime);
}
}
public void Despawn()
{
Destroy(gameObject);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 42e70e59bb536584aa49153a0f13c202

View File

@@ -0,0 +1,78 @@
using UnityEngine;
/// <summary>
/// Предмет в мире — подбирается только по нажатию кнопки E (через charecter.cs).
/// Авто-подбор при касании намеренно убран.
/// </summary>
public class WorldItem : MonoBehaviour
{
public ItemData itemData;
public int amount = 1;
[Header("Pickup")]
public float pickupDelay = 0f;
public float interactRange = 2.5f;
private bool _canPickup;
public void Setup(ItemData data, int count)
{
itemData = data;
amount = count;
if (data.modelPrefab != null)
{
var model = Instantiate(data.modelPrefab, transform);
model.transform.localPosition = Vector3.zero;
}
if (pickupDelay > 0)
Invoke(nameof(EnablePickup), pickupDelay);
else
_canPickup = true;
}
private void EnablePickup() => _canPickup = true;
/// <summary>
/// Вызывается из charecter.cs при нажатии E рядом с предметом.
/// </summary>
public bool TryPickupByButton()
{
if (!_canPickup)
{
Debug.LogWarning("[WorldItem] Cannot pickup — delay not elapsed yet");
return false;
}
if (itemData == null)
{
Debug.LogWarning("[WorldItem] itemData is null!");
return false;
}
if (InventoryManager.Instance == null)
{
Debug.LogWarning("[WorldItem] InventoryManager.Instance is null!");
return false;
}
int remaining = InventoryManager.Instance.AddItem(itemData, amount);
if (remaining <= 0)
{
Destroy(gameObject);
return true;
}
amount = remaining;
return false;
}
/// <summary>
/// Выбросить предмет с физической силой.
/// </summary>
public void Throw(Vector3 velocity)
{
var rb = GetComponent<Rigidbody>();
if (rb == null) rb = gameObject.AddComponent<Rigidbody>();
rb.linearVelocity = velocity;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b8ac54fbf9ed99e45a83319443952a91