test mesh

This commit is contained in:
jze9
2026-04-17 02:14:56 +05:00
parent 087db550f1
commit 64293937a7
50 changed files with 2570 additions and 963 deletions

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 20cff4d8f616b1140b2264c6f049ba8a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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: ferstitem
m_EditorClassIdentifier: Assembly-CSharp::ItemData
id: c3b1d462-dc84-4d7a-8463-34ffbde783d9
itemName: ferstitem
description: ferstitem
type: 5
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

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d870f627519521f4aa24b9a36766465d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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: ferstitem123
m_EditorClassIdentifier: Assembly-CSharp::ItemData
id: 4b147ba6-462a-4f90-9d00-29089fed390a
itemName: ferstitem123
description: 123
type: 5
modelPrefab: {fileID: 8569222662204961130, guid: 06e51a4df39df524397e91edba2a833d, 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

View File

@@ -10,14 +10,14 @@ MonoBehaviour:
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5741b63764436354ebad192c9b71f06c, type: 3}
m_Name: newItem
m_Name: tree
m_EditorClassIdentifier: Assembly-CSharp::ItemData
id: c58a6ea4-f064-4392-becd-3b4c2b17cd06
itemName: newItem
description: 123123
type: 5
modelPrefab: {fileID: 282714382156279063, guid: 4fcbba5b8725e254f98c052c4db89e02, type: 3}
icon: {fileID: 0}
id: f1f81603-393a-4a74-8c41-a9efe9f14e9e
itemName: tree
description: tree
type: 0
modelPrefab: {fileID: 2501859741750806132, guid: fe90e1e2b9dde0943b665ca3768153ae, type: 3}
icon: {fileID: 21300000, guid: 63b56781d36f85e49923a6b7b50381e7, type: 3}
maxStack: 64
isCraftable: 0
craftingRecipe:

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5db581fdeb06daf4da37cb3d6f32b5ff
guid: 131ca9b5c83edfe4d9c35a6ef4a380a5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000

View File

@@ -21,9 +21,15 @@ public class CraftingManager : MonoBehaviour
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
return;
}
_recipeDict = new Dictionary<string, CraftingRecipe>();
foreach (var recipe in recipes)

View File

@@ -3,86 +3,84 @@ using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// Обработка Drag & Drop предметов между слотами и выброс за пределы инвентаря
/// Drag & Drop предметов между слотами.
/// Один общий DragIconCanvas на все слоты — создаётся при первом старте.
/// </summary>
public class InventoryDragDrop : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{
public Slot slot;
public int slotIndex;
[Header("Drag Visual")]
[SerializeField] private RectTransform dragIcon;
[SerializeField] private Image dragImage;
[SerializeField] private float dragIconScale = 1.2f;
[SerializeField] private float dragIconScale = 1.2f;
[SerializeField] private bool allowDropOutside = true;
[SerializeField] private float dropThreshold = 100f;
[Header("Drop Settings")]
[SerializeField] private bool allowDropOutside = true;
[SerializeField] private float dropThreshold = 100f; // пикселей за пределами инвентаря
// ── Общий на все слоты drag-icon ─────────────────────────────────────────
private static GameObject _sharedCanvas;
private static RectTransform _sharedIcon;
private static Image _sharedImage;
private Canvas _canvas;
private CanvasGroup _canvasGroup;
private bool _isDragging;
private Vector2 _dragStartPosition;
// ── Per-instance поля ─────────────────────────────────────────────────────
private CanvasGroup _canvasGroup;
private bool _isDragging;
private RectTransform _inventoryPanelRect;
// ── Unity lifecycle ───────────────────────────────────────────────────────
void Start()
{
_canvas = GetComponentInParent<Canvas>();
_canvasGroup = GetComponent<CanvasGroup>();
if (dragIcon == null)
{
CreateDragIcon();
}
// Создаём общий DragCanvas один раз для всех слотов
if (_sharedCanvas == null)
CreateSharedDragCanvas();
// Находим панель инвентаря
var inventoryManager = FindObjectOfType<InventoryManager>();
if (inventoryManager?.inventoryPanel != null)
{
_inventoryPanelRect = inventoryManager.inventoryPanel.GetComponent<RectTransform>();
}
if (dragIcon != null)
dragIcon.gameObject.SetActive(false);
// Ищем rect инвентарной панели для определения «вне панели»
if (InventoryManager.Instance?.inventoryPanel != null)
_inventoryPanelRect = InventoryManager.Instance.inventoryPanel
.GetComponent<RectTransform>();
}
private void CreateDragIcon()
private static void CreateSharedDragCanvas()
{
GameObject canvasGO = new GameObject("DragIconCanvas");
canvasGO.transform.SetParent(_canvas != null ? _canvas.transform : transform.root);
_sharedCanvas = new GameObject("DragIconCanvas");
DontDestroyOnLoad(_sharedCanvas);
var dragCanvas = canvasGO.AddComponent<Canvas>();
dragCanvas.overrideSorting = true;
dragCanvas.sortingOrder = 1000;
dragCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvasGO.AddComponent<GraphicRaycaster>();
var c = _sharedCanvas.AddComponent<Canvas>();
c.renderMode = RenderMode.ScreenSpaceOverlay;
c.sortingOrder = 1000;
c.overrideSorting = true;
_sharedCanvas.AddComponent<GraphicRaycaster>();
dragIcon = new GameObject("DragIcon").AddComponent<RectTransform>();
dragIcon.SetParent(canvasGO.transform, false);
dragIcon.sizeDelta = new Vector2(48, 48);
var iconGO = new GameObject("DragIcon");
iconGO.transform.SetParent(_sharedCanvas.transform, false);
dragImage = dragIcon.gameObject.AddComponent<Image>();
dragImage.raycastTarget = false;
_sharedIcon = iconGO.AddComponent<RectTransform>();
_sharedIcon.sizeDelta = new Vector2(48, 48);
dragIcon.gameObject.SetActive(false);
_sharedImage = iconGO.AddComponent<Image>();
_sharedImage.raycastTarget = false;
iconGO.SetActive(false);
}
// ── Drag handlers ─────────────────────────────────────────────────────────
public void OnBeginDrag(PointerEventData eventData)
{
if (slot == null || slot.IsEmpty) return;
_isDragging = true;
_dragStartPosition = eventData.position;
if (_canvasGroup != null)
_canvasGroup.blocksRaycasts = false;
if (dragIcon != null && slot.item?.icon != null)
if (_sharedIcon != null && slot.item?.icon != null)
{
dragIcon.gameObject.SetActive(true);
dragImage.sprite = slot.item.icon;
dragIcon.localScale = Vector3.one * dragIconScale;
dragIcon.position = eventData.position;
_sharedImage.sprite = slot.item.icon;
_sharedIcon.localScale = Vector3.one * dragIconScale;
_sharedIcon.position = eventData.position;
_sharedIcon.gameObject.SetActive(true);
}
var bg = GetComponent<Image>();
@@ -92,20 +90,19 @@ public class InventoryDragDrop : MonoBehaviour, IBeginDragHandler, IDragHandler,
public void OnDrag(PointerEventData eventData)
{
if (!_isDragging || dragIcon == null) return;
dragIcon.position = eventData.position;
if (!_isDragging || _sharedIcon == null) return;
_sharedIcon.position = eventData.position;
}
public void OnEndDrag(PointerEventData eventData)
{
if (!_isDragging) return;
_isDragging = false;
if (dragIcon != null)
if (_sharedIcon != null)
{
dragIcon.gameObject.SetActive(false);
dragIcon.localScale = Vector3.one;
_sharedIcon.gameObject.SetActive(false);
_sharedIcon.localScale = Vector3.one;
}
if (_canvasGroup != null)
@@ -113,124 +110,66 @@ public class InventoryDragDrop : MonoBehaviour, IBeginDragHandler, IDragHandler,
var bg = GetComponent<Image>();
if (bg != null)
bg.color = slot.IsEmpty ? new Color(0.25f, 0.25f, 0.25f, 0.8f) : new Color(0.35f, 0.35f, 0.35f, 0.95f);
bg.color = slot.IsEmpty
? new Color(0.25f, 0.25f, 0.25f, 0.8f)
: new Color(0.35f, 0.35f, 0.35f, 0.95f);
// Проверяем - выбросили за пределы инвентаря?
if (allowDropOutside && IsOutsideInventory(eventData.position))
{
DropItemOutside();
return;
}
// Иначе - пробуем переместить в другой слот
// Сначала ищем целевой слот (инвентарь ИЛИ хотбар)
var raycastResults = new System.Collections.Generic.List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, raycastResults);
Slot targetSlot = null;
foreach (var result in raycastResults)
{
var slotComponent = result.gameObject.GetComponent<Slot>();
if (slotComponent != null && slotComponent != slot)
{
targetSlot = slotComponent;
break;
}
var s = result.gameObject.GetComponent<Slot>();
if (s != null && s != slot) { targetSlot = s; break; }
}
if (targetSlot != null && InventoryManager.Instance != null)
if (targetSlot != null)
{
InventoryManager.Instance.SwapSlots(slotIndex, targetSlot.slotIndex);
// Нашли слот — перемещаем (работает и для хотбара)
InventoryManager.Instance?.SwapSlots(slot, targetSlot);
return;
}
// Слот не найден — выбросить в мир если за пределами инвентаря
if (allowDropOutside && IsOutsideInventory(eventData.position))
DropItemOutside();
}
private bool IsOutsideInventory(Vector2 screenPosition)
// ── Вспомогательные ───────────────────────────────────────────────────────
private bool IsOutsideInventory(Vector2 screenPos)
{
if (_inventoryPanelRect == null) return false;
// Получаем границы панели инвентаря в экранных координатах
Vector3[] corners = new Vector3[4];
_inventoryPanelRect.GetWorldCorners(corners);
// rect панели в экранных координатах
float minX = corners[0].x;
float maxX = corners[2].x;
float minY = corners[0].y;
float maxY = corners[2].y;
// Добавляем отступ
float threshold = dropThreshold;
// Проверяем находится ли курсор за пределами панели + threshold
bool outside = screenPosition.x < minX - threshold ||
screenPosition.x > maxX + threshold ||
screenPosition.y < minY - threshold ||
screenPosition.y > maxY + threshold;
return outside;
return screenPos.x < corners[0].x - dropThreshold ||
screenPos.x > corners[2].x + dropThreshold ||
screenPos.y < corners[0].y - dropThreshold ||
screenPos.y > corners[2].y + dropThreshold;
}
private void DropItemOutside()
{
if (slot == null || slot.IsEmpty) return;
var item = slot.item;
int amount = slot.amount;
var item = slot.item;
int amount = slot.amount;
// Определяем позицию выброса
Vector3 dropPos = Vector3.zero;
if (Camera.main != null)
{
// Выбрасываем перед игроком
dropPos = Camera.main.transform.position + Camera.main.transform.forward * 2f;
}
// Создаём предмет в мире
if (InventoryItemFactory.Instance != null)
{
InventoryItemFactory.Instance.DropItem(item, amount, dropPos);
}
else
{
// Fallback - просто создаём объект
var go = new GameObject($"Dropped_{item.itemName}");
go.transform.position = dropPos;
var visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.transform.SetParent(go.transform);
visual.transform.localPosition = Vector3.zero;
visual.transform.localScale = Vector3.one * 0.3f;
DestroyImmediate(visual.GetComponent<Collider>());
var worldItem = go.AddComponent<WorldItem>();
worldItem.Setup(item, amount);
}
// Очищаем слот
slot.Clear();
Debug.Log($"[DragDrop] Выброшен: {item.itemName} x{amount}");
}
public void OnPointerEnter(PointerEventData eventData)
{
if (!_isDragging && slot != null && !slot.IsEmpty)
{
var bg = GetComponent<Image>();
if (bg != null)
bg.color = new Color(0.5f, 0.5f, 0.5f, 1f);
}
}
public void OnPointerExit(PointerEventData eventData)
{
if (!_isDragging && slot != null)
{
var bg = GetComponent<Image>();
if (bg != null)
bg.color = slot.IsEmpty ?
new Color(0.25f, 0.25f, 0.25f, 0.8f) :
new Color(0.35f, 0.35f, 0.35f, 0.95f);
}
}
}
public void OnPointerEnter(PointerEventData eventData) { }
public void OnPointerExit(PointerEventData eventData) { }
}

View File

@@ -1,5 +1,6 @@
using UnityEngine;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// Пример конфигурации предметов
@@ -11,28 +12,43 @@ public class InventoryExampleItems : MonoBehaviour
void Awake()
{
// Создаём примеры предметов если база пустая
// Создаём примеры предметов если список пустой
if (items == null || items.Count == 0)
{
items = CreateExampleItems();
}
// Передаём в InventoryManager
var im = FindObjectOfType<InventoryManager>();
if (im != null)
{
im.itemDatabase = items;
im.RebuildItemDictionary();
if (im == null) return;
// Сразу добавляем тестовые предметы
// Объединяем с существующей базой — не заменяем!
// Так кастомные предметы (tree.asset и т.д.) из Inspector'а сохраняются.
if (im.itemDatabase == null)
im.itemDatabase = new List<ItemData>();
foreach (var item in items)
{
if (item != null && !im.itemDatabase.Exists(x => x?.id == item.id))
im.itemDatabase.Add(item);
}
im.RebuildItemDictionary();
// Тестовые предметы добавляем ТОЛЬКО если нет сохранения
// Иначе они будут перезаписаны загрузкой в Start() и появляться снова при каждом запуске
string savePath = Path.Combine(Application.persistentDataPath, "inventory_save.json");
if (!File.Exists(savePath))
{
im.AddItem("stone", 32);
im.AddItem("dirt", 16);
im.AddItem("wood", 8);
im.AddItem("apple", 5);
im.AddItem("sword", 1);
im.AddItem("pickaxe", 1);
Debug.Log("[InventoryExampleItems] Added test items");
Debug.Log("[InventoryExampleItems] No save found — added test items");
}
else
{
Debug.Log("[InventoryExampleItems] Save exists — skipping test items, load will restore inventory");
}
}

View File

@@ -163,11 +163,7 @@ public class InventoryItemFactory : MonoBehaviour
}
Vector3 spawnPos = dropPosition + offset;
Quaternion rotation = Quaternion.Euler(
Random.Range(0, 360),
Random.Range(0, 360),
Random.Range(0, 360)
);
Quaternion rotation = Quaternion.identity;
var worldItem = CreateWorldItem(itemData, amount, spawnPos, rotation);

View File

@@ -318,10 +318,11 @@ public class InventoryManager : MonoBehaviour
}
else
{
remaining -= slot.amount;
int cleared = slot.amount; // сохранить ДО Clear()
remaining -= cleared;
slot.Clear();
OnItemRemoved?.Invoke(itemId, slot.amount);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, slot.amount);
OnItemRemoved?.Invoke(itemId, cleared);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, cleared);
}
}
}
@@ -342,10 +343,11 @@ public class InventoryManager : MonoBehaviour
}
else
{
remaining -= slot.amount;
int cleared = slot.amount; // сохранить ДО Clear()
remaining -= cleared;
slot.Clear();
OnItemRemoved?.Invoke(itemId, slot.amount);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, slot.amount);
OnItemRemoved?.Invoke(itemId, cleared);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, cleared);
}
}
}
@@ -447,7 +449,7 @@ public class InventoryManager : MonoBehaviour
}
/// <summary>
/// Поменять предметы между двумя слотами
/// Поменять предметы между двумя слотами (по индексам, только основной инвентарь)
/// </summary>
public void SwapSlots(int fromIndex, int toIndex)
{
@@ -455,14 +457,19 @@ public class InventoryManager : MonoBehaviour
toIndex < 0 || toIndex >= slots.Length)
return;
var fromSlot = slots[fromIndex];
var toSlot = slots[toIndex];
SwapSlots(slots[fromIndex], slots[toIndex]);
}
// Сохраняем данные
var tempItem = fromSlot.item;
/// <summary>
/// Поменять предметы между любыми двумя слотами (инвентарь ↔ хотбар и т.д.)
/// </summary>
public void SwapSlots(Slot fromSlot, Slot toSlot)
{
if (fromSlot == null || toSlot == null || fromSlot == toSlot) return;
var tempItem = fromSlot.item;
var tempAmount = fromSlot.amount;
// Меняем
if (toSlot.IsEmpty)
{
fromSlot.Clear();
@@ -541,6 +548,19 @@ public class InventoryManager : MonoBehaviour
};
}
if (hotbarSlots != null)
{
inventoryData.hotbarSlots = new SlotData[hotbarSlots.Length];
for (int i = 0; i < hotbarSlots.Length; i++)
{
inventoryData.hotbarSlots[i] = new SlotData
{
itemId = hotbarSlots[i].item?.id,
amount = hotbarSlots[i].amount
};
}
}
return JsonUtility.ToJson(inventoryData, true);
}
@@ -600,12 +620,20 @@ public class InventoryManager : MonoBehaviour
{
var slotData = inventoryData.slots[i];
if (!string.IsNullOrEmpty(slotData.itemId) && _itemDict.TryGetValue(slotData.itemId, out ItemData item))
{
slots[i].SetItem(item, slotData.amount);
}
else
{
slots[i].Clear();
}
if (inventoryData.hotbarSlots != null && hotbarSlots != null)
{
for (int i = 0; i < Mathf.Min(inventoryData.hotbarSlots.Length, hotbarSlots.Length); i++)
{
var slotData = inventoryData.hotbarSlots[i];
if (!string.IsNullOrEmpty(slotData.itemId) && _itemDict.TryGetValue(slotData.itemId, out ItemData item))
hotbarSlots[i].SetItem(item, slotData.amount);
else
hotbarSlots[i].Clear();
}
}
}
@@ -614,6 +642,7 @@ public class InventoryManager : MonoBehaviour
class InventorySaveData
{
public SlotData[] slots;
public SlotData[] hotbarSlots;
}
[System.Serializable]
@@ -623,6 +652,16 @@ public class InventoryManager : MonoBehaviour
public int amount;
}
void OnDestroy()
{
if (PlayerInput.Instance != null)
{
PlayerInput.Instance.OnToggleInventory -= ToggleInventory;
PlayerInput.Instance.OnDropPressed -= DropSelectedItem;
PlayerInput.Instance.OnHotbarSelect -= SelectHotbarSlot;
}
}
void OnApplicationQuit()
{
SaveToFile();

View File

@@ -35,10 +35,16 @@ public class WorldItem : MonoBehaviour
itemData = data;
amount = count;
if (data.modelPrefab != null)
// Спавним модель только если визуал ещё не встроен в prefab
if (data.modelPrefab != null && transform.childCount == 0)
{
var model = Instantiate(data.modelPrefab, transform);
model.transform.localPosition = Vector3.zero;
// Удаляем WorldItem из визуальной модели — она только для отображения,
// не должна быть самостоятельным подбираемым объектом
foreach (var wi in model.GetComponentsInChildren<WorldItem>())
Destroy(wi);
}
if (pickupDelay > 0)
@@ -73,6 +79,7 @@ public class WorldItem : MonoBehaviour
int remaining = InventoryManager.Instance.AddItem(itemData, amount);
if (remaining <= 0)
{
CancelInvoke(); // отменяем отложенный EnablePickup если он ещё не сработал
Destroy(gameObject);
return true;
}