fix чать инвенторя
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91470f1965cf83a4a8c49831ec2f7691
|
||||
NativeFormatImporter:
|
||||
guid: ebd3754781a0d3e48a34311a699ad38b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
135
Assets/Inventory/ItemPrefab/NewItem.prefab
Normal file
135
Assets/Inventory/ItemPrefab/NewItem.prefab
Normal file
@@ -0,0 +1,135 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &4546282257700047937
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1948853633481286021}
|
||||
- component: {fileID: 6658874805278007762}
|
||||
- component: {fileID: 3056228376157587138}
|
||||
m_Layer: 0
|
||||
m_Name: NewItem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1948853633481286021
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4546282257700047937}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7434756968248499828}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &6658874805278007762
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4546282257700047937}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b8ac54fbf9ed99e45a83319443952a91, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::WorldItem
|
||||
itemData: {fileID: 0}
|
||||
amount: 1
|
||||
pickupDelay: 0
|
||||
interactRange: 2.5
|
||||
--- !u!135 &3056228376157587138
|
||||
SphereCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4546282257700047937}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 1
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Radius: 0.5
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &1495056769490629797
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 1948853633481286021}
|
||||
m_Modifications:
|
||||
- target: {fileID: 282714382156279063, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Visual
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
--- !u!4 &7434756968248499828 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 8327738622413892305, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
m_PrefabInstance: {fileID: 1495056769490629797}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 223ec2dc359ac0845957ab0b29284414
|
||||
guid: 9f444e9048146434087c3db3550617f9
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
@@ -12,9 +12,9 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 5741b63764436354ebad192c9b71f06c, type: 3}
|
||||
m_Name: NewItem
|
||||
m_EditorClassIdentifier: Assembly-CSharp::ItemData
|
||||
id: 2
|
||||
id: d28a3db9-17bd-489d-9caf-739005a7c780
|
||||
itemName: NewItem
|
||||
description: 23
|
||||
description: 123
|
||||
type: 5
|
||||
modelPrefab: {fileID: 282714382156279063, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
icon: {fileID: 21300000, guid: 0115b2d990086ae4cbc31baf7c64e680, type: 3}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9765c2fbc4980049940f96e9b0f70dc
|
||||
guid: 20cff4d8f616b1140b2264c6f049ba8a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5741b63764436354ebad192c9b71f06c, type: 3}
|
||||
m_Name: test
|
||||
m_EditorClassIdentifier: Assembly-CSharp::ItemData
|
||||
id: 1
|
||||
itemName: test
|
||||
description: 123
|
||||
type: 0
|
||||
modelPrefab: {fileID: 282714382156279063, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
|
||||
icon: {fileID: 21300000, guid: 0115b2d990086ae4cbc31baf7c64e680, type: 3}
|
||||
maxStack: 64
|
||||
isCraftable: 0
|
||||
craftingRecipe:
|
||||
ingredients: []
|
||||
amounts:
|
||||
resultId:
|
||||
resultAmount: 1
|
||||
weight: 1
|
||||
value: 1
|
||||
isDroppable: 1
|
||||
isStackable: 1
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a832ca7ed419e6479e1cc308a222f33
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
39
Assets/Inventory/Scripts/ItemSpawner.cs
Normal file
39
Assets/Inventory/Scripts/ItemSpawner.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
2
Assets/Inventory/Scripts/ItemSpawner.cs.meta
Normal file
2
Assets/Inventory/Scripts/ItemSpawner.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42e70e59bb536584aa49153a0f13c202
|
||||
78
Assets/Inventory/Scripts/WorldItem.cs
Normal file
78
Assets/Inventory/Scripts/WorldItem.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
2
Assets/Inventory/Scripts/WorldItem.cs.meta
Normal file
2
Assets/Inventory/Scripts/WorldItem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8ac54fbf9ed99e45a83319443952a91
|
||||
Reference in New Issue
Block a user