├── .gitignore
├── Demo Scripts.meta
├── Demo Scripts
├── InventoryTester.cs
└── InventoryTester.cs.meta
├── Demo.unity
├── Demo.unity.meta
├── Item Data.meta
├── Item Data
├── Item_Armor_Helmet.asset
├── Item_Armor_Helmet.asset.meta
├── Item_Armor_White Armor.asset
├── Item_Armor_White Armor.asset.meta
├── Item_Portion_HP.asset
├── Item_Portion_HP.asset.meta
├── Item_Portion_MP.asset
├── Item_Portion_MP.asset.meta
├── Item_Weapon_Blue Sword.asset
├── Item_Weapon_Blue Sword.asset.meta
├── Item_Weapon_Red Sword.asset
└── Item_Weapon_Red Sword.asset.meta
├── LICENSE
├── LICENSE.meta
├── README.md
├── README.md.meta
├── Scripts.meta
├── Scripts
├── Inventory.cs
├── Inventory.cs.meta
├── Item Data.meta
├── Item Data
│ ├── ArmorItemData.cs
│ ├── ArmorItemData.cs.meta
│ ├── Bases.meta
│ ├── Bases
│ │ ├── CountableItemData.cs
│ │ ├── CountableItemData.cs.meta
│ │ ├── EquipmentItemData.cs
│ │ ├── EquipmentItemData.cs.meta
│ │ ├── ItemData.cs
│ │ └── ItemData.cs.meta
│ ├── PortionItemData.cs
│ ├── PortionItemData.cs.meta
│ ├── WeaponItemData.cs
│ └── WeaponItemData.cs.meta
├── Item.meta
├── Item
│ ├── ArmorItem.cs
│ ├── ArmorItem.cs.meta
│ ├── Bases.meta
│ ├── Bases
│ │ ├── CountableItem.cs
│ │ ├── CountableItem.cs.meta
│ │ ├── EquipmentItem.cs
│ │ ├── EquipmentItem.cs.meta
│ │ ├── IUsableItem.cs
│ │ ├── IUsableItem.cs.meta
│ │ ├── Item.cs
│ │ └── Item.cs.meta
│ ├── PortionItem.cs
│ ├── PortionItem.cs.meta
│ ├── WeaponItem.cs
│ └── WeaponItem.cs.meta
├── UI.meta
└── UI
│ ├── InventoryPopupUI.cs
│ ├── InventoryPopupUI.cs.meta
│ ├── InventoryUI.cs
│ ├── InventoryUI.cs.meta
│ ├── ItemSlotUI.cs
│ ├── ItemSlotUI.cs.meta
│ ├── ItemTooltipUI.cs
│ ├── ItemTooltipUI.cs.meta
│ ├── MovableHeaderUI.cs
│ └── MovableHeaderUI.cs.meta
├── Sprites.meta
└── Sprites
├── ElixirBlue_64.png
├── ElixirBlue_64.png.meta
├── ElixirRed_64.png
├── ElixirRed_64.png.meta
├── Helmet_64.png
├── Helmet_64.png.meta
├── SwordBlue_64.png
├── SwordBlue_64.png.meta
├── SwordRed_64.png
├── SwordRed_64.png.meta
├── WhiteArmor_64.png
└── WhiteArmor_64.png.meta
/.gitignore:
--------------------------------------------------------------------------------
1 | # This .gitignore file should be placed at the root of your Unity project directory
2 | #
3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
4 | #
5 | /[Ll]ibrary/
6 | /[Tt]emp/
7 | /[Oo]bj/
8 | /[Bb]uild/
9 | /[Bb]uilds/
10 | /[Ll]ogs/
11 | /[Mm]emoryCaptures/
12 |
13 | # Asset meta data should only be ignored when the corresponding asset is also ignored
14 | !/[Aa]ssets/**/*.meta
15 |
16 | # Uncomment this line if you wish to ignore the asset store tools plugin
17 | # /[Aa]ssets/AssetStoreTools*
18 |
19 | # Autogenerated Jetbrains Rider plugin
20 | [Aa]ssets/Plugins/Editor/JetBrains*
21 |
22 | # Visual Studio cache directory
23 | .vs/
24 |
25 | # Gradle cache directory
26 | .gradle/
27 |
28 | # Autogenerated VS/MD/Consulo solution and project files
29 | ExportedObj/
30 | .consulo/
31 | *.csproj
32 | *.unityproj
33 | *.sln
34 | *.suo
35 | *.tmp
36 | *.user
37 | *.userprefs
38 | *.pidb
39 | *.booproj
40 | *.svd
41 | *.pdb
42 | *.mdb
43 | *.opendb
44 | *.VC.db
45 |
46 | # Unity3D generated meta files
47 | *.pidb.meta
48 | *.pdb.meta
49 | *.mdb.meta
50 |
51 | # Unity3D generated file on crash reports
52 | sysinfo.txt
53 |
54 | # Builds
55 | *.apk
56 | *.unitypackage
57 |
58 | # Crashlytics generated file
59 | crashlytics-build.properties
60 |
61 |
62 | # Additions by Rito
63 | *.cmd
64 | *.cmd.meta
65 | _*
66 |
--------------------------------------------------------------------------------
/Demo Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 19fafcdf7b1cf404795067b4cbfd845f
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Demo Scripts/InventoryTester.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 | using Rito.InventorySystem;
7 |
8 | // 날짜 : 2021-03-19 PM 11:01:36
9 | // 작성자 : Rito
10 |
11 | public class InventoryTester : MonoBehaviour
12 | {
13 | public Inventory _inventory;
14 |
15 | public ItemData[] _itemDataArray;
16 |
17 | [Space(12)]
18 | public Button _removeAllButton;
19 |
20 | [Space(8)]
21 | public Button _AddArmorA1;
22 | public Button _AddArmorB1;
23 | public Button _AddSwordA1;
24 | public Button _AddSwordB1;
25 | public Button _AddPortionA1;
26 | public Button _AddPortionA50;
27 | public Button _AddPortionB1;
28 | public Button _AddPortionB50;
29 |
30 | private void Start()
31 | {
32 | if (_itemDataArray?.Length > 0)
33 | {
34 | for (int i = 0; i < _itemDataArray.Length; i++)
35 | {
36 | _inventory.Add(_itemDataArray[i], 3);
37 |
38 | if(_itemDataArray[i] is CountableItemData)
39 | _inventory.Add(_itemDataArray[i], 255);
40 | }
41 | }
42 |
43 | _removeAllButton.onClick.AddListener(() =>
44 | {
45 | int capacity = _inventory.Capacity;
46 | for(int i = 0; i < capacity; i++)
47 | _inventory.Remove(i);
48 | });
49 |
50 | _AddArmorA1.onClick.AddListener(() => _inventory.Add(_itemDataArray[0]));
51 | _AddArmorB1.onClick.AddListener(() => _inventory.Add(_itemDataArray[1]));
52 |
53 | _AddSwordA1.onClick.AddListener(() => _inventory.Add(_itemDataArray[2]));
54 | _AddSwordB1.onClick.AddListener(() => _inventory.Add(_itemDataArray[3]));
55 |
56 | _AddPortionA1.onClick.AddListener(() => _inventory.Add(_itemDataArray[4]));
57 | _AddPortionA50.onClick.AddListener(() => _inventory.Add(_itemDataArray[4], 50));
58 | _AddPortionB1.onClick.AddListener(() => _inventory.Add(_itemDataArray[5]));
59 | _AddPortionB50.onClick.AddListener(() => _inventory.Add(_itemDataArray[5], 50));
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/Demo Scripts/InventoryTester.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7367a7830ed812249a4b205472fa9313
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Demo.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 312290db893094f49b25c39265d09882
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Item Data.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f7d09e6180ef70c47921c0ca3c86992d
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Item Data/Item_Armor_Helmet.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &11400000
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 11500000, guid: b50f428ada62c3a40b63fb5674b59ee3, type: 3}
13 | m_Name: Item_Armor_Helmet
14 | m_EditorClassIdentifier:
15 | _id: 5
16 | _name: "\uAE30\uC0AC\uC6A9 \uD22C\uAD6C"
17 | _tooltip: "\uAE30\uC0AC\uAC00 \uC4F8 \uAC83 \uAC19\uC740 \uD22C\uAD6C\uC774\uB2E4."
18 | _iconSprite: {fileID: 21300000, guid: 2a3a301121704ab45bb55331eb763958, type: 3}
19 | _dropItemPrefab: {fileID: 0}
20 | _maxDurability: 100
21 | _defence: 5
22 |
--------------------------------------------------------------------------------
/Item Data/Item_Armor_Helmet.asset.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8f19a473f3e8c5c408faaffb1802abea
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Item Data/Item_Armor_White Armor.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &11400000
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 11500000, guid: b50f428ada62c3a40b63fb5674b59ee3, type: 3}
13 | m_Name: Item_Armor_White Armor
14 | m_EditorClassIdentifier:
15 | _id: 4
16 | _name: "\uD558\uC580 \uAC11\uC637"
17 | _tooltip: "\uD558\uC580 \uAC11\uC637\uC774\uB2E4."
18 | _iconSprite: {fileID: 21300000, guid: a1f270ff46445564e9afa40d0d9574db, type: 3}
19 | _dropItemPrefab: {fileID: 0}
20 | _maxDurability: 150
21 | _defence: 10
22 |
--------------------------------------------------------------------------------
/Item Data/Item_Armor_White Armor.asset.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e7e18c97d59896341a1a487bee626a6d
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Item Data/Item_Portion_HP.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &11400000
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 11500000, guid: b597242af57330945a22e8bf03013df0, type: 3}
13 | m_Name: Item_Portion_HP
14 | m_EditorClassIdentifier:
15 | _id: 0
16 | _name: "\uC18C\uD615 \uCCB4\uB825 \uD3EC\uC158"
17 | _tooltip: "\uCCB4\uB825\uC744 100 \uCC44\uC6CC\uC900\uB2E4."
18 | _iconSprite: {fileID: 21300000, guid: c2ebadfcb9b8cbb4a8ae6f124b17e332, type: 3}
19 | _dropItemPrefab: {fileID: 0}
20 | _maxAmount: 99
21 | _value: 100
22 |
--------------------------------------------------------------------------------
/Item Data/Item_Portion_HP.asset.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c76044d8a642b6341882e805becf92df
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Item Data/Item_Portion_MP.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &11400000
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 11500000, guid: b597242af57330945a22e8bf03013df0, type: 3}
13 | m_Name: Item_Portion_MP
14 | m_EditorClassIdentifier:
15 | _id: 1
16 | _name: "\uC18C\uD615 \uB9C8\uB098 \uD3EC\uC158"
17 | _tooltip: "\uB9C8\uB098\uB97C 100 \uCC44\uC6CC\uC900\uB2E4."
18 | _iconSprite: {fileID: 21300000, guid: ac64af182bf5c9c4a8a16cb35547a3cb, type: 3}
19 | _dropItemPrefab: {fileID: 0}
20 | _maxAmount: 99
21 | _value: 100
22 |
--------------------------------------------------------------------------------
/Item Data/Item_Portion_MP.asset.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 04eef8332317ca946adaf6a2a23b00ca
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Item Data/Item_Weapon_Blue Sword.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &11400000
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 11500000, guid: 8be323efc2a71414498d934aca6fd26a, type: 3}
13 | m_Name: Item_Weapon_Blue Sword
14 | m_EditorClassIdentifier:
15 | _id: 2
16 | _name: "\uD478\uB978 \uC7A5\uAC80"
17 | _tooltip: "\uD30C\uB791\uD30C\uB791 \uC7A5\uAC80\uC774\uB2E4."
18 | _iconSprite: {fileID: 21300000, guid: bb1bdd56c1352fb419836f9c2b0bf4bf, type: 3}
19 | _dropItemPrefab: {fileID: 0}
20 | _maxDurability: 100
21 | _damage: 10
22 |
--------------------------------------------------------------------------------
/Item Data/Item_Weapon_Blue Sword.asset.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e44506062b1a62e4f9afc1bf9cf480fd
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Item Data/Item_Weapon_Red Sword.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &11400000
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 11500000, guid: 8be323efc2a71414498d934aca6fd26a, type: 3}
13 | m_Name: Item_Weapon_Red Sword
14 | m_EditorClassIdentifier:
15 | _id: 3
16 | _name: "\uBD89\uC740 \uB2E8\uAC80"
17 | _tooltip: "\uBD89\uAC8C \uBB3C\uB4E0 \uB2E8\uAC80\uC774\uB2E4."
18 | _iconSprite: {fileID: 21300000, guid: 1f0a1cc5b62b0d747928186abd4be7c2, type: 3}
19 | _dropItemPrefab: {fileID: 0}
20 | _maxDurability: 120
21 | _damage: 20
22 |
--------------------------------------------------------------------------------
/Item Data/Item_Weapon_Red Sword.asset.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e73a0198a0431f04a8ff190aae00be09
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 rito15
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 07f1f8d828d892e45a786df0607b2c8a
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unity RPG Inventory
2 |
3 | - RPG 게임에서 사용될 수 있는 인벤토리의 기본 구조 및 기능을 간단히 제작하였습니다.
4 |
5 | -
6 |
7 |
8 |
9 | ## 구현된 기능
10 |
11 | - 인벤토리 헤더 드래그 앤 드롭 이동
12 | - 아이템 슬롯 동적 생성
13 | - 아이템 추가
14 | - 아이템 제거
15 | - 아이템 사용
16 | - 아이템 위치 이동
17 | - 아이템 개수 합치기
18 | - 아이템 슬롯 하이라이트(강조) 표시
19 | - 아이템 정보 툴팁 표시
20 | - 아이템 버리기 팝업
21 | - 아이템 개수 나누기 팝업
22 | - 인벤토리 빈칸 채우기
23 | - 인벤토리 정렬
24 | - 아이템 타입에 따른 필터링(장비, 소비, ..)
25 |
26 |
27 |
28 | ## Preview
29 |
30 | 
31 |
32 | 
33 |
34 | 
35 |
36 | 
37 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 48e7e0188c02cbb42a122143e829ef5a
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 21ade3eb7f6b9724d913030dc42ee34c
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Scripts/Inventory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | /*
7 | [Item의 상속구조]
8 | - Item
9 | - CountableItem
10 | - PortionItem : IUsableItem.Use() -> 사용 및 수량 1 소모
11 | - EquipmentItem
12 | - WeaponItem
13 | - ArmorItem
14 |
15 | [ItemData의 상속구조]
16 | (ItemData는 해당 아이템이 공통으로 가질 데이터 필드 모음)
17 | (개체마다 달라져야 하는 현재 내구도, 강화도 등은 Item 클래스에서 관리)
18 |
19 | - ItemData
20 | - CountableItemData
21 | - PortionItemData : 효과량(Value : 회복량, 공격력 등에 사용)
22 | - EquipmentItemData : 최대 내구도
23 | - WeaponItemData : 기본 공격력
24 | - ArmorItemData : 기본 방어력
25 | */
26 |
27 | /*
28 | [API]
29 | - bool HasItem(int) : 해당 인덱스의 슬롯에 아이템이 존재하는지 여부
30 | - bool IsCountableItem(int) : 해당 인덱스의 아이템이 셀 수 있는 아이템인지 여부
31 | - int GetCurrentAmount(int) : 해당 인덱스의 아이템 수량
32 | - -1 : 잘못된 인덱스
33 | - 0 : 빈 슬롯
34 | - 1 : 셀 수 없는 아이템이거나 수량 1
35 | - ItemData GetItemData(int) : 해당 인덱스의 아이템 정보
36 | - string GetItemName(int) : 해당 인덱스의 아이템 이름
37 |
38 | - int Add(ItemData, int) : 해당 타입의 아이템을 지정한 개수만큼 인벤토리에 추가
39 | - 자리 부족으로 못넣은 개수만큼 리턴(0이면 모두 추가 성공했다는 의미)
40 | - void Remove(int) : 해당 인덱스의 슬롯에 있는 아이템 제거
41 | - void Swap(int, int) : 두 인덱스의 아이템 위치 서로 바꾸기
42 | - void SeparateAmount(int a, int b, int amount)
43 | - a 인덱스의 아이템이 셀 수 있는 아이템일 경우, amount만큼 분리하여 b 인덱스로 복제
44 | - void Use(int) : 해당 인덱스의 아이템 사용
45 | - void UpdateSlot(int) : 해당 인덱스의 슬롯 상태 및 UI 갱신
46 | - void UpdateAllSlot() : 모든 슬롯 상태 및 UI 갱신
47 | - void UpdateAccessibleStatesAll() : 모든 슬롯 UI에 접근 가능 여부 갱신
48 | - void TrimAll() : 앞에서부터 아이템 슬롯 채우기
49 | - void SortAll() : 앞에서부터 아이템 슬롯 채우면서 정렬
50 | */
51 |
52 | // 날짜 : 2021-03-07 PM 7:33:52
53 | // 작성자 : Rito
54 |
55 | namespace Rito.InventorySystem
56 | {
57 | public class Inventory : MonoBehaviour
58 | {
59 | /***********************************************************************
60 | * Public Properties
61 | ***********************************************************************/
62 | #region .
63 | /// 아이템 수용 한도
64 | public int Capacity { get; private set; }
65 |
66 | // /// 현재 아이템 개수
67 | //public int ItemCount => _itemArray.Count;
68 |
69 | #endregion
70 | /***********************************************************************
71 | * Private Fields
72 | ***********************************************************************/
73 | #region .
74 |
75 | // 초기 수용 한도
76 | [SerializeField, Range(8, 64)]
77 | private int _initalCapacity = 32;
78 |
79 | // 최대 수용 한도(아이템 배열 크기)
80 | [SerializeField, Range(8, 64)]
81 | private int _maxCapacity = 64;
82 |
83 | [SerializeField]
84 | private InventoryUI _inventoryUI; // 연결된 인벤토리 UI
85 |
86 | /// 아이템 목록
87 | [SerializeField]
88 | private Item[] _items;
89 |
90 | /// 업데이트 할 인덱스 목록
91 | private readonly HashSet _indexSetForUpdate = new HashSet();
92 |
93 | /// 아이템 데이터 타입별 정렬 가중치
94 | private readonly static Dictionary _sortWeightDict = new Dictionary
95 | {
96 | { typeof(PortionItemData), 10000 },
97 | { typeof(WeaponItemData), 20000 },
98 | { typeof(ArmorItemData), 30000 },
99 | };
100 |
101 | private class ItemComparer : IComparer-
102 | {
103 | public int Compare(Item a, Item b)
104 | {
105 | return (a.Data.ID + _sortWeightDict[a.Data.GetType()])
106 | - (b.Data.ID + _sortWeightDict[b.Data.GetType()]);
107 | }
108 | }
109 | private static readonly ItemComparer _itemComparer = new ItemComparer();
110 |
111 | #endregion
112 | /***********************************************************************
113 | * Unity Events
114 | ***********************************************************************/
115 | #region .
116 |
117 | #if UNITY_EDITOR
118 | private void OnValidate()
119 | {
120 | if(_initalCapacity > _maxCapacity)
121 | _initalCapacity = _maxCapacity;
122 | }
123 | #endif
124 | private void Awake()
125 | {
126 | _items = new Item[_maxCapacity];
127 | Capacity = _initalCapacity;
128 | _inventoryUI.SetInventoryReference(this);
129 | }
130 |
131 | private void Start()
132 | {
133 | UpdateAccessibleStatesAll();
134 | }
135 |
136 | #endregion
137 | /***********************************************************************
138 | * Private Methods
139 | ***********************************************************************/
140 | #region .
141 | /// 인덱스가 수용 범위 내에 있는지 검사
142 | private bool IsValidIndex(int index)
143 | {
144 | return index >= 0 && index < Capacity;
145 | }
146 |
147 | /// 앞에서부터 비어있는 슬롯 인덱스 탐색
148 | private int FindEmptySlotIndex(int startIndex = 0)
149 | {
150 | for (int i = startIndex; i < Capacity; i++)
151 | if (_items[i] == null)
152 | return i;
153 | return -1;
154 | }
155 |
156 | /// 앞에서부터 개수 여유가 있는 Countable 아이템의 슬롯 인덱스 탐색
157 | private int FindCountableItemSlotIndex(CountableItemData target, int startIndex = 0)
158 | {
159 | for (int i = startIndex; i < Capacity; i++)
160 | {
161 | var current = _items[i];
162 | if (current == null)
163 | continue;
164 |
165 | // 아이템 종류 일치, 개수 여유 확인
166 | if (current.Data == target && current is CountableItem ci)
167 | {
168 | if (!ci.IsMax)
169 | return i;
170 | }
171 | }
172 |
173 | return -1;
174 | }
175 |
176 | /// 해당하는 인덱스의 슬롯 상태 및 UI 갱신
177 | private void UpdateSlot(int index)
178 | {
179 | if (!IsValidIndex(index)) return;
180 |
181 | Item item = _items[index];
182 |
183 | // 1. 아이템이 슬롯에 존재하는 경우
184 | if (item != null)
185 | {
186 | // 아이콘 등록
187 | _inventoryUI.SetItemIcon(index, item.Data.IconSprite);
188 |
189 | // 1-1. 셀 수 있는 아이템
190 | if (item is CountableItem ci)
191 | {
192 | // 1-1-1. 수량이 0인 경우, 아이템 제거
193 | if (ci.IsEmpty)
194 | {
195 | _items[index] = null;
196 | RemoveIcon();
197 | return;
198 | }
199 | // 1-1-2. 수량 텍스트 표시
200 | else
201 | {
202 | _inventoryUI.SetItemAmountText(index, ci.Amount);
203 | }
204 | }
205 | // 1-2. 셀 수 없는 아이템인 경우 수량 텍스트 제거
206 | else
207 | {
208 | _inventoryUI.HideItemAmountText(index);
209 | }
210 |
211 | // 슬롯 필터 상태 업데이트
212 | _inventoryUI.UpdateSlotFilterState(index, item.Data);
213 | }
214 | // 2. 빈 슬롯인 경우 : 아이콘 제거
215 | else
216 | {
217 | RemoveIcon();
218 | }
219 |
220 | // 로컬 : 아이콘 제거하기
221 | void RemoveIcon()
222 | {
223 | _inventoryUI.RemoveItem(index);
224 | _inventoryUI.HideItemAmountText(index); // 수량 텍스트 숨기기
225 | }
226 | }
227 |
228 | /// 해당하는 인덱스의 슬롯들의 상태 및 UI 갱신
229 | private void UpdateSlot(params int[] indices)
230 | {
231 | foreach (var i in indices)
232 | {
233 | UpdateSlot(i);
234 | }
235 | }
236 |
237 | /// 모든 슬롯들의 상태를 UI에 갱신
238 | private void UpdateAllSlot()
239 | {
240 | for (int i = 0; i < Capacity; i++)
241 | {
242 | UpdateSlot(i);
243 | }
244 | }
245 |
246 | #endregion
247 | /***********************************************************************
248 | * Check & Getter Methods
249 | ***********************************************************************/
250 | #region .
251 |
252 | /// 해당 슬롯이 아이템을 갖고 있는지 여부
253 | public bool HasItem(int index)
254 | {
255 | return IsValidIndex(index) && _items[index] != null;
256 | }
257 |
258 | /// 해당 슬롯이 셀 수 있는 아이템인지 여부
259 | public bool IsCountableItem(int index)
260 | {
261 | return HasItem(index) && _items[index] is CountableItem;
262 | }
263 |
264 | ///
265 | /// 해당 슬롯의 현재 아이템 개수 리턴
266 | /// - 잘못된 인덱스 : -1 리턴
267 | /// - 빈 슬롯 : 0 리턴
268 | /// - 셀 수 없는 아이템 : 1 리턴
269 | ///
270 | public int GetCurrentAmount(int index)
271 | {
272 | if (!IsValidIndex(index)) return -1;
273 | if (_items[index] == null) return 0;
274 |
275 | CountableItem ci = _items[index] as CountableItem;
276 | if (ci == null)
277 | return 1;
278 |
279 | return ci.Amount;
280 | }
281 |
282 | /// 해당 슬롯의 아이템 정보 리턴
283 | public ItemData GetItemData(int index)
284 | {
285 | if (!IsValidIndex(index)) return null;
286 | if (_items[index] == null) return null;
287 |
288 | return _items[index].Data;
289 | }
290 |
291 | /// 해당 슬롯의 아이템 이름 리턴
292 | public string GetItemName(int index)
293 | {
294 | if (!IsValidIndex(index)) return "";
295 | if (_items[index] == null) return "";
296 |
297 | return _items[index].Data.Name;
298 | }
299 |
300 | #endregion
301 | /***********************************************************************
302 | * Public Methods
303 | ***********************************************************************/
304 | #region .
305 | /// 인벤토리 UI 연결
306 | public void ConnectUI(InventoryUI inventoryUI)
307 | {
308 | _inventoryUI = inventoryUI;
309 | _inventoryUI.SetInventoryReference(this);
310 | }
311 |
312 | /// 인벤토리에 아이템 추가
313 | /// 넣는 데 실패한 잉여 아이템 개수 리턴
314 | /// 리턴이 0이면 넣는데 모두 성공했다는 의미
315 | ///
316 | public int Add(ItemData itemData, int amount = 1)
317 | {
318 | int index;
319 |
320 | // 1. 수량이 있는 아이템
321 | if (itemData is CountableItemData ciData)
322 | {
323 | bool findNextCountable = true;
324 | index = -1;
325 |
326 | while (amount > 0)
327 | {
328 | // 1-1. 이미 해당 아이템이 인벤토리 내에 존재하고, 개수 여유 있는지 검사
329 | if (findNextCountable)
330 | {
331 | index = FindCountableItemSlotIndex(ciData, index + 1);
332 |
333 | // 개수 여유있는 기존재 슬롯이 더이상 없다고 판단될 경우, 빈 슬롯부터 탐색 시작
334 | if (index == -1)
335 | {
336 | findNextCountable = false;
337 | }
338 | // 기존재 슬롯을 찾은 경우, 양 증가시키고 초과량 존재 시 amount에 초기화
339 | else
340 | {
341 | CountableItem ci = _items[index] as CountableItem;
342 | amount = ci.AddAmountAndGetExcess(amount);
343 |
344 | UpdateSlot(index);
345 | }
346 | }
347 | // 1-2. 빈 슬롯 탐색
348 | else
349 | {
350 | index = FindEmptySlotIndex(index + 1);
351 |
352 | // 빈 슬롯조차 없는 경우 종료
353 | if (index == -1)
354 | {
355 | break;
356 | }
357 | // 빈 슬롯 발견 시, 슬롯에 아이템 추가 및 잉여량 계산
358 | else
359 | {
360 | // 새로운 아이템 생성
361 | CountableItem ci = ciData.CreateItem() as CountableItem;
362 | ci.SetAmount(amount);
363 |
364 | // 슬롯에 추가
365 | _items[index] = ci;
366 |
367 | // 남은 개수 계산
368 | amount = (amount > ciData.MaxAmount) ? (amount - ciData.MaxAmount) : 0;
369 |
370 | UpdateSlot(index);
371 | }
372 | }
373 | }
374 | }
375 | // 2. 수량이 없는 아이템
376 | else
377 | {
378 | // 2-1. 1개만 넣는 경우, 간단히 수행
379 | if (amount == 1)
380 | {
381 | index = FindEmptySlotIndex();
382 | if (index != -1)
383 | {
384 | // 아이템을 생성하여 슬롯에 추가
385 | _items[index] = itemData.CreateItem();
386 | amount = 0;
387 |
388 | UpdateSlot(index);
389 | }
390 | }
391 |
392 | // 2-2. 2개 이상의 수량 없는 아이템을 동시에 추가하는 경우
393 | index = -1;
394 | for (; amount > 0; amount--)
395 | {
396 | // 아이템 넣은 인덱스의 다음 인덱스부터 슬롯 탐색
397 | index = FindEmptySlotIndex(index + 1);
398 |
399 | // 다 넣지 못한 경우 루프 종료
400 | if (index == -1)
401 | {
402 | break;
403 | }
404 |
405 | // 아이템을 생성하여 슬롯에 추가
406 | _items[index] = itemData.CreateItem();
407 |
408 | UpdateSlot(index);
409 | }
410 | }
411 |
412 | return amount;
413 | }
414 |
415 | /// 해당 슬롯의 아이템 제거
416 | public void Remove(int index)
417 | {
418 | if (!IsValidIndex(index)) return;
419 |
420 | _items[index] = null;
421 | _inventoryUI.RemoveItem(index);
422 | }
423 |
424 | /// 두 인덱스의 아이템 위치를 서로 교체
425 | public void Swap(int indexA, int indexB)
426 | {
427 | if (!IsValidIndex(indexA)) return;
428 | if (!IsValidIndex(indexB)) return;
429 |
430 | Item itemA = _items[indexA];
431 | Item itemB = _items[indexB];
432 |
433 | // 1. 셀 수 있는 아이템이고, 동일한 아이템일 경우
434 | // indexA -> indexB로 개수 합치기
435 | if (itemA != null && itemB != null &&
436 | itemA.Data == itemB.Data &&
437 | itemA is CountableItem ciA && itemB is CountableItem ciB)
438 | {
439 | int maxAmount = ciB.MaxAmount;
440 | int sum = ciA.Amount + ciB.Amount;
441 |
442 | if (sum <= maxAmount)
443 | {
444 | ciA.SetAmount(0);
445 | ciB.SetAmount(sum);
446 | }
447 | else
448 | {
449 | ciA.SetAmount(sum - maxAmount);
450 | ciB.SetAmount(maxAmount);
451 | }
452 | }
453 | // 2. 일반적인 경우 : 슬롯 교체
454 | else
455 | {
456 | _items[indexA] = itemB;
457 | _items[indexB] = itemA;
458 | }
459 |
460 | // 두 슬롯 정보 갱신
461 | UpdateSlot(indexA, indexB);
462 | }
463 |
464 | /// 셀 수 있는 아이템의 수량 나누기(A -> B 슬롯으로)
465 | public void SeparateAmount(int indexA, int indexB, int amount)
466 | {
467 | // amount : 나눌 목표 수량
468 |
469 | if(!IsValidIndex(indexA)) return;
470 | if(!IsValidIndex(indexB)) return;
471 |
472 | Item _itemA = _items[indexA];
473 | Item _itemB = _items[indexB];
474 |
475 | CountableItem _ciA = _itemA as CountableItem;
476 |
477 | // 조건 : A 슬롯 - 셀 수 있는 아이템 / B 슬롯 - Null
478 | // 조건에 맞는 경우, 복제하여 슬롯 B에 추가
479 | if (_ciA != null && _itemB == null)
480 | {
481 | _items[indexB] = _ciA.SeperateAndClone(amount);
482 |
483 | UpdateSlot(indexA, indexB);
484 | }
485 | }
486 |
487 | /// 해당 슬롯의 아이템 사용
488 | public void Use(int index)
489 | {
490 | if (!IsValidIndex(index)) return;
491 | if (_items[index] == null) return;
492 |
493 | // 사용 가능한 아이템인 경우
494 | if (_items[index] is IUsableItem uItem)
495 | {
496 | // 아이템 사용
497 | bool succeeded = uItem.Use();
498 |
499 | if (succeeded)
500 | {
501 | UpdateSlot(index);
502 | }
503 | }
504 | }
505 |
506 | /// 모든 슬롯 UI에 접근 가능 여부 업데이트
507 | public void UpdateAccessibleStatesAll()
508 | {
509 | _inventoryUI.SetAccessibleSlotRange(Capacity);
510 | }
511 |
512 | /// 빈 슬롯 없이 앞에서부터 채우기
513 | public void TrimAll()
514 | {
515 | // 가장 빠른 배열 빈공간 채우기 알고리즘
516 |
517 | // i 커서와 j 커서
518 | // i 커서 : 가장 앞에 있는 빈칸을 찾는 커서
519 | // j 커서 : i 커서 위치에서부터 뒤로 이동하며 기존재 아이템을 찾는 커서
520 |
521 | // i커서가 빈칸을 찾으면 j 커서는 i+1 위치부터 탐색
522 | // j커서가 아이템을 찾으면 아이템을 옮기고, i 커서는 i+1 위치로 이동
523 | // j커서가 Capacity에 도달하면 루프 즉시 종료
524 |
525 | _indexSetForUpdate.Clear();
526 |
527 | int i = -1;
528 | while (_items[++i] != null) ;
529 | int j = i;
530 |
531 | while (true)
532 | {
533 | while (++j < Capacity && _items[j] == null);
534 |
535 | if (j == Capacity)
536 | break;
537 |
538 | _indexSetForUpdate.Add(i);
539 | _indexSetForUpdate.Add(j);
540 |
541 | _items[i] = _items[j];
542 | _items[j] = null;
543 | i++;
544 | }
545 |
546 | foreach (var index in _indexSetForUpdate)
547 | {
548 | UpdateSlot(index);
549 | }
550 | _inventoryUI.UpdateAllSlotFilters();
551 | }
552 |
553 | /// 빈 슬롯 없이 채우면서 아이템 종류별로 정렬하기
554 | public void SortAll()
555 | {
556 | // 1. Trim
557 | int i = -1;
558 | while (_items[++i] != null) ;
559 | int j = i;
560 |
561 | while (true)
562 | {
563 | while (++j < Capacity && _items[j] == null) ;
564 |
565 | if (j == Capacity)
566 | break;
567 |
568 | _items[i] = _items[j];
569 | _items[j] = null;
570 | i++;
571 | }
572 |
573 | // 2. Sort
574 | Array.Sort(_items, 0, i, _itemComparer);
575 |
576 | // 3. Update
577 | UpdateAllSlot();
578 | _inventoryUI.UpdateAllSlotFilters(); // 필터 상태 업데이트
579 | }
580 |
581 | #endregion
582 | }
583 | }
--------------------------------------------------------------------------------
/Scripts/Inventory.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cd347083a75615f448ffa248fb2c97d4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item Data.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a2abbec82c3388847b38df9750b9606e
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Scripts/Item Data/ArmorItemData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-28 PM 10:42:17
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 장비 - 방어구 아이템
12 | [CreateAssetMenu(fileName = "Item_Armor_", menuName = "Inventory System/Item Data/Armor", order = 2)]
13 | public class ArmorItemData : EquipmentItemData
14 | {
15 | /// 방어력
16 | public int Defence => _defence;
17 |
18 | [SerializeField] private int _defence = 1;
19 | public override Item CreateItem()
20 | {
21 | return new ArmorItem(this);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Scripts/Item Data/ArmorItemData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b50f428ada62c3a40b63fb5674b59ee3
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item Data/Bases.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1d57b80d4bb50f24ca5602ee9304a895
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Scripts/Item Data/Bases/CountableItemData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-27 PM 11:34:48
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 셀 수 있는 아이템 데이터
12 | public abstract class CountableItemData : ItemData
13 | {
14 | public int MaxAmount => _maxAmount;
15 | [SerializeField] private int _maxAmount = 99;
16 | }
17 | }
--------------------------------------------------------------------------------
/Scripts/Item Data/Bases/CountableItemData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1971c5cefe151b44683b2606035af84e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item Data/Bases/EquipmentItemData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-27 PM 11:07:41
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 장비 아이템
12 | public abstract class EquipmentItemData : ItemData
13 | {
14 | /// 최대 내구도
15 | public int MaxDurability => _maxDurability;
16 |
17 | [SerializeField] private int _maxDurability = 100;
18 | }
19 | }
--------------------------------------------------------------------------------
/Scripts/Item Data/Bases/EquipmentItemData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e5d999ba525e58243be0f4bfa5c8e9ec
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item Data/Bases/ItemData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 |
7 | // 날짜 : 2021-03-07 PM 8:45:55
8 | // 작성자 : Rito
9 |
10 | namespace Rito.InventorySystem
11 | {
12 | /*
13 | [상속 구조]
14 |
15 | ItemData(abstract)
16 | - CountableItemData(abstract)
17 | - PortionItemData
18 | - EquipmentItemData(abstract)
19 | - WeaponItemData
20 | - ArmorItemData
21 |
22 | */
23 |
24 | public abstract class ItemData : ScriptableObject
25 | {
26 | public int ID => _id;
27 | public string Name => _name;
28 | public string Tooltip => _tooltip;
29 | public Sprite IconSprite => _iconSprite;
30 |
31 | [SerializeField] private int _id;
32 | [SerializeField] private string _name; // 아이템 이름
33 | [Multiline]
34 | [SerializeField] private string _tooltip; // 아이템 설명
35 | [SerializeField] private Sprite _iconSprite; // 아이템 아이콘
36 | [SerializeField] private GameObject _dropItemPrefab; // 바닥에 떨어질 때 생성할 프리팹
37 |
38 | /// 타입에 맞는 새로운 아이템 생성
39 | public abstract Item CreateItem();
40 | }
41 | }
--------------------------------------------------------------------------------
/Scripts/Item Data/Bases/ItemData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8bf92190e57c1c940a83176f802e600c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item Data/PortionItemData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-28 PM 10:42:48
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 소비 아이템 정보
12 | [CreateAssetMenu(fileName = "Item_Portion_", menuName = "Inventory System/Item Data/Portion", order = 3)]
13 | public class PortionItemData : CountableItemData
14 | {
15 | /// 효과량(회복량 등)
16 | public float Value => _value;
17 | [SerializeField] private float _value;
18 | public override Item CreateItem()
19 | {
20 | return new PortionItem(this);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/Scripts/Item Data/PortionItemData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b597242af57330945a22e8bf03013df0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item Data/WeaponItemData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-28 PM 10:38:33
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 장비 - 무기 아이템
12 | [CreateAssetMenu(fileName = "Item_Weapon_", menuName = "Inventory System/Item Data/Weaopn", order = 1)]
13 | public class WeaponItemData : EquipmentItemData
14 | {
15 | /// 공격력
16 | public int Damage => _damage;
17 |
18 | [SerializeField] private int _damage = 1;
19 |
20 | public override Item CreateItem()
21 | {
22 | return new WeaponItem(this);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Scripts/Item Data/WeaponItemData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8be323efc2a71414498d934aca6fd26a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d80e7055b1dd5c4478a01806b1024355
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Scripts/Item/ArmorItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-28 PM 11:06:16
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 장비 - 방어구 아이템
12 | public class ArmorItem : EquipmentItem
13 | {
14 | public ArmorItem(ArmorItemData data) : base(data) { }
15 | }
16 | }
--------------------------------------------------------------------------------
/Scripts/Item/ArmorItem.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 60899e625c944a54ba420ddaf0e3e11e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item/Bases.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 10624e20782c20c4991a7c4b335736c8
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Scripts/Item/Bases/CountableItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-21 PM 11:01:27
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 수량을 셀 수 있는 아이템
12 | public abstract class CountableItem : Item
13 | {
14 | public CountableItemData CountableData { get; private set; }
15 |
16 | /// 현재 아이템 개수
17 | public int Amount { get; protected set; }
18 |
19 | /// 하나의 슬롯이 가질 수 있는 최대 개수(기본 99)
20 | public int MaxAmount => CountableData.MaxAmount;
21 |
22 | /// 수량이 가득 찼는지 여부
23 | public bool IsMax => Amount >= CountableData.MaxAmount;
24 |
25 | /// 개수가 없는지 여부
26 | public bool IsEmpty => Amount <= 0;
27 |
28 |
29 | public CountableItem(CountableItemData data, int amount = 1) : base(data)
30 | {
31 | CountableData = data;
32 | SetAmount(amount);
33 | }
34 |
35 | /// 개수 지정(범위 제한)
36 | public void SetAmount(int amount)
37 | {
38 | Amount = Mathf.Clamp(amount, 0, MaxAmount);
39 | }
40 |
41 | /// 개수 추가 및 최대치 초과량 반환(초과량 없을 경우 0)
42 | public int AddAmountAndGetExcess(int amount)
43 | {
44 | int nextAmount = Amount + amount;
45 | SetAmount(nextAmount);
46 |
47 | return (nextAmount > MaxAmount) ? (nextAmount - MaxAmount) : 0;
48 | }
49 |
50 | /// 개수를 나누어 복제
51 | public CountableItem SeperateAndClone(int amount)
52 | {
53 | // 수량이 한개 이하일 경우, 복제 불가
54 | if(Amount <= 1) return null;
55 |
56 | if(amount > Amount - 1)
57 | amount = Amount - 1;
58 |
59 | Amount -= amount;
60 | return Clone(amount);
61 | }
62 |
63 | protected abstract CountableItem Clone(int amount);
64 | }
65 | }
--------------------------------------------------------------------------------
/Scripts/Item/Bases/CountableItem.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4ba69c1f1c6d3a64387d543e847594c9
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item/Bases/EquipmentItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-28 PM 10:55:00
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 장비 아이템
12 | public abstract class EquipmentItem : Item
13 | {
14 | public EquipmentItemData EquipmentData { get; private set; }
15 |
16 | /// 현재 내구도
17 | public int Durability
18 | {
19 | get => _durability;
20 | set
21 | {
22 | if(value < 0) value = 0;
23 | if(value > EquipmentData.MaxDurability)
24 | value = EquipmentData.MaxDurability;
25 |
26 | _durability = value;
27 | }
28 | }
29 | private int _durability;
30 |
31 | public EquipmentItem(EquipmentItemData data) : base(data)
32 | {
33 | EquipmentData = data;
34 | Durability = data.MaxDurability;
35 | }
36 |
37 | // Item Data 외의 필드값에 대한 매개변수를 갖는 생성자는 추가로 제공하지 않음
38 | // 자식들에서 모두 추가해줘야 하므로 유지보수면에서 불편
39 | }
40 | }
--------------------------------------------------------------------------------
/Scripts/Item/Bases/EquipmentItem.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2effbefd157312243beefa076e4a72c4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item/Bases/IUsableItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-04-15 PM 2:59:59
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 사용 가능한 아이템(착용/소모)
12 | public interface IUsableItem
13 | {
14 | /// 아이템 사용하기(사용 성공 여부 리턴)
15 | bool Use();
16 | }
17 | }
--------------------------------------------------------------------------------
/Scripts/Item/Bases/IUsableItem.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 373c932af49bbd148acd3f2810025687
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item/Bases/Item.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 |
7 | // 날짜 : 2021-03-07 PM 7:34:39
8 | // 작성자 : Rito
9 |
10 | namespace Rito.InventorySystem
11 | {
12 | /*
13 | [상속 구조]
14 | Item : 기본 아이템
15 | - EquipmentItem : 장비 아이템
16 | - CountableItem : 수량이 존재하는 아이템
17 | */
18 | public abstract class Item
19 | {
20 | public ItemData Data { get; private set; }
21 |
22 | public Item(ItemData data) => Data = data;
23 | }
24 | }
--------------------------------------------------------------------------------
/Scripts/Item/Bases/Item.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ad04de80c001c4442ae031ac57ecad61
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item/PortionItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-28 PM 11:07:23
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 수량 아이템 - 포션 아이템
12 | public class PortionItem : CountableItem, IUsableItem
13 | {
14 | public PortionItem(PortionItemData data, int amount = 1) : base(data, amount) { }
15 |
16 | public bool Use()
17 | {
18 | // 임시 : 개수 하나 감소
19 | Amount--;
20 |
21 | return true;
22 | }
23 |
24 | protected override CountableItem Clone(int amount)
25 | {
26 | return new PortionItem(CountableData as PortionItemData, amount);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/Scripts/Item/PortionItem.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2f1024a7001da4a47a4ce4ca5d2322fa
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/Item/WeaponItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 |
6 | // 날짜 : 2021-03-28 PM 11:02:03
7 | // 작성자 : Rito
8 |
9 | namespace Rito.InventorySystem
10 | {
11 | /// 장비 - 무기 아이템
12 | public class WeaponItem : EquipmentItem
13 | {
14 | public WeaponItem(WeaponItemData data) : base(data) { }
15 | }
16 | }
--------------------------------------------------------------------------------
/Scripts/Item/WeaponItem.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f6bd85c2409af394884b994dc2e34534
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/UI.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ffd06526a7acc06479693b205efaa9c5
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Scripts/UI/InventoryPopupUI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 |
7 | // 날짜 : 2021-04-13 PM 7:47:35
8 | // 작성자 : Rito
9 |
10 | namespace Rito
11 | {
12 | /// 인벤토리 UI 위에 띄울 작은 팝업들 관리
13 | public class InventoryPopupUI : MonoBehaviour
14 | {
15 | /***********************************************************************
16 | * Fields
17 | ***********************************************************************/
18 | #region .
19 | // 1. 아이템 버리기 확인 팝업
20 | [Header("Confirmation Popup")]
21 | [SerializeField] private GameObject _confirmationPopupObject;
22 | [SerializeField] private Text _confirmationItemNameText;
23 | [SerializeField] private Text _confirmationText;
24 | [SerializeField] private Button _confirmationOkButton; // Ok
25 | [SerializeField] private Button _confirmationCancelButton; // Cancel
26 |
27 | // 2. 수량 입력 팝업
28 | [Header("Amount Input Popup")]
29 | [SerializeField] private GameObject _amountInputPopupObject;
30 | [SerializeField] private Text _amountInputItemNameText;
31 | [SerializeField] private InputField _amountInputField;
32 | [SerializeField] private Button _amountPlusButton; // +
33 | [SerializeField] private Button _amountMinusButton; // -
34 | [SerializeField] private Button _amountInputOkButton; // Ok
35 | [SerializeField] private Button _amountInputCancelButton; // Cancel
36 |
37 | // 확인 버튼 눌렀을 때 동작할 이벤트
38 | private event Action OnConfirmationOK;
39 | private event Action OnAmountInputOK;
40 |
41 | // 수량 입력 제한 개수
42 | private int _maxAmount;
43 |
44 | #endregion
45 | /***********************************************************************
46 | * Unity Events
47 | ***********************************************************************/
48 | #region .
49 | private void Awake()
50 | {
51 | InitUIEvents();
52 | HidePanel();
53 | HideConfirmationPopup();
54 | HideAmountInputPopup();
55 | }
56 |
57 | private void Update()
58 | {
59 | if (_confirmationPopupObject.activeSelf)
60 | {
61 | if (Input.GetKeyDown(KeyCode.Return))
62 | {
63 | _confirmationOkButton.onClick?.Invoke();
64 | }
65 | else if (Input.GetKeyDown(KeyCode.Escape))
66 | {
67 | _confirmationCancelButton.onClick?.Invoke();
68 | }
69 | }
70 | else if (_amountInputPopupObject.activeSelf)
71 | {
72 | if (Input.GetKeyDown(KeyCode.Return))
73 | {
74 | _amountInputOkButton.onClick?.Invoke();
75 | }
76 | else if (Input.GetKeyDown(KeyCode.Escape))
77 | {
78 | _amountInputCancelButton.onClick?.Invoke();
79 | }
80 | }
81 | }
82 |
83 | #endregion
84 | /***********************************************************************
85 | * Public Methods
86 | ***********************************************************************/
87 | #region .
88 | /// 확인/취소 팝업 띄우기
89 | public void OpenConfirmationPopup(Action okCallback, string itemName)
90 | {
91 | ShowPanel();
92 | ShowConfirmationPopup(itemName);
93 | SetConfirmationOKEvent(okCallback);
94 | }
95 | /// 수량 입력 팝업 띄우기
96 | public void OpenAmountInputPopup(Action okCallback, int currentAmount, string itemName)
97 | {
98 | _maxAmount = currentAmount - 1;
99 | _amountInputField.text = "1";
100 |
101 | ShowPanel();
102 | ShowAmountInputPopup(itemName);
103 | SetAmountInputOKEvent(okCallback);
104 | }
105 |
106 | #endregion
107 | /***********************************************************************
108 | * Private Methods
109 | ***********************************************************************/
110 | #region .
111 | private void InitUIEvents()
112 | {
113 | // 1. 확인 취소 팝업
114 | _confirmationOkButton.onClick.AddListener(HidePanel);
115 | _confirmationOkButton.onClick.AddListener(HideConfirmationPopup);
116 | _confirmationOkButton.onClick.AddListener(() => OnConfirmationOK?.Invoke());
117 |
118 | _confirmationCancelButton.onClick.AddListener(HidePanel);
119 | _confirmationCancelButton.onClick.AddListener(HideConfirmationPopup);
120 |
121 | // 2. 수량 입력 팝업
122 | _amountInputOkButton.onClick.AddListener(HidePanel);
123 | _amountInputOkButton.onClick.AddListener(HideAmountInputPopup);
124 | _amountInputOkButton.onClick.AddListener(() => OnAmountInputOK?.Invoke(int.Parse(_amountInputField.text)));
125 |
126 | _amountInputCancelButton.onClick.AddListener(HidePanel);
127 | _amountInputCancelButton.onClick.AddListener(HideAmountInputPopup);
128 |
129 | // [-] 버튼 이벤트
130 | _amountMinusButton.onClick.AddListener(() =>
131 | {
132 | int.TryParse(_amountInputField.text, out int amount);
133 | if (amount > 1)
134 | {
135 | // Shift 누르면 10씩 감소
136 | int nextAmount = Input.GetKey(KeyCode.LeftShift) ? amount - 10 : amount - 1;
137 | if(nextAmount < 1)
138 | nextAmount = 1;
139 | _amountInputField.text = nextAmount.ToString();
140 | }
141 | });
142 |
143 | // [+] 버튼 이벤트
144 | _amountPlusButton.onClick.AddListener(() =>
145 | {
146 | int.TryParse(_amountInputField.text, out int amount);
147 | if (amount < _maxAmount)
148 | {
149 | // Shift 누르면 10씩 증가
150 | int nextAmount = Input.GetKey(KeyCode.LeftShift) ? amount + 10 : amount + 1;
151 | if (nextAmount > _maxAmount)
152 | nextAmount = _maxAmount;
153 | _amountInputField.text = nextAmount.ToString();
154 | }
155 | });
156 |
157 | // 입력 값 범위 제한
158 | _amountInputField.onValueChanged.AddListener(str =>
159 | {
160 | int.TryParse(str, out int amount);
161 | bool flag = false;
162 |
163 | if (amount < 1)
164 | {
165 | flag = true;
166 | amount = 1;
167 | }
168 | else if (amount > _maxAmount)
169 | {
170 | flag = true;
171 | amount = _maxAmount;
172 | }
173 |
174 | if(flag)
175 | _amountInputField.text = amount.ToString();
176 | });
177 | }
178 |
179 | private void ShowPanel() => gameObject.SetActive(true);
180 | private void HidePanel() => gameObject.SetActive(false);
181 |
182 | private void ShowConfirmationPopup(string itemName)
183 | {
184 | _confirmationItemNameText.text = itemName;
185 | _confirmationPopupObject.SetActive(true);
186 | }
187 | private void HideConfirmationPopup() => _confirmationPopupObject.SetActive(false);
188 |
189 | private void ShowAmountInputPopup(string itemName)
190 | {
191 | _amountInputItemNameText.text = itemName;
192 | _amountInputPopupObject.SetActive(true);
193 | }
194 | private void HideAmountInputPopup() => _amountInputPopupObject.SetActive(false);
195 |
196 | private void SetConfirmationOKEvent(Action handler) => OnConfirmationOK = handler;
197 | private void SetAmountInputOKEvent(Action handler) => OnAmountInputOK = handler;
198 |
199 |
200 | #endregion
201 |
202 | }
203 | }
--------------------------------------------------------------------------------
/Scripts/UI/InventoryPopupUI.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 87a85f7172c6d864c8dd7b12622b4cdb
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/UI/InventoryUI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 | using UnityEngine.EventSystems;
7 |
8 | /*
9 | [기능 - 에디터 전용]
10 | - 게임 시작 시 동적으로 생성될 슬롯 미리보기(개수, 크기 미리보기 가능)
11 |
12 | [기능 - 유저 인터페이스]
13 | - 슬롯에 마우스 올리기
14 | - 사용 가능 슬롯 : 하이라이트 이미지 표시
15 | - 아이템 존재 슬롯 : 아이템 정보 툴팁 표시
16 |
17 | - 드래그 앤 드롭
18 | - 아이템 존재 슬롯 -> 아이템 존재 슬롯 : 두 아이템 위치 교환
19 | - 아이템 존재 슬롯 -> 아이템 미존재 슬롯 : 아이템 위치 변경
20 | - Shift 또는 Ctrl 누른 상태일 경우 : 셀 수 있는 아이템 수량 나누기
21 | - 아이템 존재 슬롯 -> UI 바깥 : 아이템 버리기
22 |
23 | - 슬롯 우클릭
24 | - 사용 가능한 아이템일 경우 : 아이템 사용
25 |
26 | - 기능 버튼(좌측 상단)
27 | - Trim : 앞에서부터 빈 칸 없이 아이템 채우기
28 | - Sort : 정해진 가중치대로 아이템 정렬
29 |
30 | - 필터 버튼(우측 상단)
31 | - [A] : 모든 아이템 필터링
32 | - [E] : 장비 아이템 필터링
33 | - [P] : 소비 아이템 필터링
34 |
35 | * 필터링에서 제외된 아이템 슬롯들은 조작 불가
36 |
37 | [기능 - 기타]
38 | - InvertMouse(bool) : 마우스 좌클릭/우클릭 반전 여부 설정
39 | */
40 |
41 | // 날짜 : 2021-03-07 PM 7:34:31
42 | // 작성자 : Rito
43 |
44 | namespace Rito.InventorySystem
45 | {
46 | public class InventoryUI : MonoBehaviour
47 | {
48 | /***********************************************************************
49 | * Option Fields
50 | ***********************************************************************/
51 | #region .
52 | [Header("Options")]
53 | [Range(0, 10)]
54 | [SerializeField] private int _horizontalSlotCount = 8; // 슬롯 가로 개수
55 | [Range(0, 10)]
56 | [SerializeField] private int _verticalSlotCount = 8; // 슬롯 세로 개수
57 | [SerializeField] private float _slotMargin = 8f; // 한 슬롯의 상하좌우 여백
58 | [SerializeField] private float _contentAreaPadding = 20f; // 인벤토리 영역의 내부 여백
59 | [Range(32, 64)]
60 | [SerializeField] private float _slotSize = 64f; // 각 슬롯의 크기
61 |
62 | [Space]
63 | [SerializeField] private bool _showTooltip = true;
64 | [SerializeField] private bool _showHighlight = true;
65 | [SerializeField] private bool _showRemovingPopup = true;
66 |
67 | [Header("Connected Objects")]
68 | [SerializeField] private RectTransform _contentAreaRT; // 슬롯들이 위치할 영역
69 | [SerializeField] private GameObject _slotUiPrefab; // 슬롯의 원본 프리팹
70 | [SerializeField] private ItemTooltipUI _itemTooltip; // 아이템 정보를 보여줄 툴팁 UI
71 | [SerializeField] private InventoryPopupUI _popup; // 팝업 UI 관리 객체
72 |
73 | [Header("Buttons")]
74 | [SerializeField] private Button _trimButton;
75 | [SerializeField] private Button _sortButton;
76 |
77 | [Header("Filter Toggles")]
78 | [SerializeField] private Toggle _toggleFilterAll;
79 | [SerializeField] private Toggle _toggleFilterEquipments;
80 | [SerializeField] private Toggle _toggleFilterPortions;
81 |
82 | [Space(16)]
83 | [SerializeField] private bool _mouseReversed = false; // 마우스 클릭 반전 여부
84 |
85 | #endregion
86 | /***********************************************************************
87 | * Private Fields
88 | ***********************************************************************/
89 | #region .
90 |
91 | /// 연결된 인벤토리
92 | private Inventory _inventory;
93 |
94 | private List _slotUIList = new List();
95 | private GraphicRaycaster _gr;
96 | private PointerEventData _ped;
97 | private List _rrList;
98 |
99 | private ItemSlotUI _pointerOverSlot; // 현재 포인터가 위치한 곳의 슬롯
100 | private ItemSlotUI _beginDragSlot; // 현재 드래그를 시작한 슬롯
101 | private Transform _beginDragIconTransform; // 해당 슬롯의 아이콘 트랜스폼
102 |
103 | private int _leftClick = 0;
104 | private int _rightClick = 1;
105 |
106 | private Vector3 _beginDragIconPoint; // 드래그 시작 시 슬롯의 위치
107 | private Vector3 _beginDragCursorPoint; // 드래그 시작 시 커서의 위치
108 | private int _beginDragSlotSiblingIndex;
109 |
110 | /// 인벤토리 UI 내 아이템 필터링 옵션
111 | private enum FilterOption
112 | {
113 | All, Equipment, Portion
114 | }
115 | private FilterOption _currentFilterOption = FilterOption.All;
116 |
117 | #endregion
118 | /***********************************************************************
119 | * Unity Events
120 | ***********************************************************************/
121 | #region .
122 | private void Awake()
123 | {
124 | Init();
125 | InitSlots();
126 | InitButtonEvents();
127 | InitToggleEvents();
128 | }
129 |
130 | private void Update()
131 | {
132 | _ped.position = Input.mousePosition;
133 |
134 | OnPointerEnterAndExit();
135 | if(_showTooltip) ShowOrHideItemTooltip();
136 | OnPointerDown();
137 | OnPointerDrag();
138 | OnPointerUp();
139 | }
140 |
141 | #endregion
142 | /***********************************************************************
143 | * Init Methods
144 | ***********************************************************************/
145 | #region .
146 | private void Init()
147 | {
148 | TryGetComponent(out _gr);
149 | if (_gr == null)
150 | _gr = gameObject.AddComponent();
151 |
152 | // Graphic Raycaster
153 | _ped = new PointerEventData(EventSystem.current);
154 | _rrList = new List(10);
155 |
156 | // Item Tooltip UI
157 | if (_itemTooltip == null)
158 | {
159 | _itemTooltip = GetComponentInChildren();
160 | EditorLog("인스펙터에서 아이템 툴팁 UI를 직접 지정하지 않아 자식에서 발견하여 초기화하였습니다.");
161 | }
162 | }
163 |
164 | /// 지정된 개수만큼 슬롯 영역 내에 슬롯들 동적 생성
165 | private void InitSlots()
166 | {
167 | // 슬롯 프리팹 설정
168 | _slotUiPrefab.TryGetComponent(out RectTransform slotRect);
169 | slotRect.sizeDelta = new Vector2(_slotSize, _slotSize);
170 |
171 | _slotUiPrefab.TryGetComponent(out ItemSlotUI itemSlot);
172 | if (itemSlot == null)
173 | _slotUiPrefab.AddComponent();
174 |
175 | _slotUiPrefab.SetActive(false);
176 |
177 | // --
178 | Vector2 beginPos = new Vector2(_contentAreaPadding, -_contentAreaPadding);
179 | Vector2 curPos = beginPos;
180 |
181 | _slotUIList = new List(_verticalSlotCount * _horizontalSlotCount);
182 |
183 | // 슬롯들 동적 생성
184 | for (int j = 0; j < _verticalSlotCount; j++)
185 | {
186 | for (int i = 0; i < _horizontalSlotCount; i++)
187 | {
188 | int slotIndex = (_horizontalSlotCount * j) + i;
189 |
190 | var slotRT = CloneSlot();
191 | slotRT.pivot = new Vector2(0f, 1f); // Left Top
192 | slotRT.anchoredPosition = curPos;
193 | slotRT.gameObject.SetActive(true);
194 | slotRT.gameObject.name = $"Item Slot [{slotIndex}]";
195 |
196 | var slotUI = slotRT.GetComponent();
197 | slotUI.SetSlotIndex(slotIndex);
198 | _slotUIList.Add(slotUI);
199 |
200 | // Next X
201 | curPos.x += (_slotMargin + _slotSize);
202 | }
203 |
204 | // Next Line
205 | curPos.x = beginPos.x;
206 | curPos.y -= (_slotMargin + _slotSize);
207 | }
208 |
209 | // 슬롯 프리팹 - 프리팹이 아닌 경우 파괴
210 | if(_slotUiPrefab.scene.rootCount != 0)
211 | Destroy(_slotUiPrefab);
212 |
213 | // -- Local Method --
214 | RectTransform CloneSlot()
215 | {
216 | GameObject slotGo = Instantiate(_slotUiPrefab);
217 | RectTransform rt = slotGo.GetComponent();
218 | rt.SetParent(_contentAreaRT);
219 |
220 | return rt;
221 | }
222 | }
223 |
224 | private void InitButtonEvents()
225 | {
226 | _trimButton.onClick.AddListener(() => _inventory.TrimAll());
227 | _sortButton.onClick.AddListener(() => _inventory.SortAll());
228 | }
229 |
230 | private void InitToggleEvents()
231 | {
232 | _toggleFilterAll.onValueChanged.AddListener( flag => UpdateFilter(flag, FilterOption.All));
233 | _toggleFilterEquipments.onValueChanged.AddListener(flag => UpdateFilter(flag, FilterOption.Equipment));
234 | _toggleFilterPortions.onValueChanged.AddListener( flag => UpdateFilter(flag, FilterOption.Portion));
235 |
236 | // Local Method
237 | void UpdateFilter(bool flag, FilterOption option)
238 | {
239 | if (flag)
240 | {
241 | _currentFilterOption = option;
242 | UpdateAllSlotFilters();
243 | }
244 | }
245 | }
246 |
247 | #endregion
248 | /***********************************************************************
249 | * Mouse Event Methods
250 | ***********************************************************************/
251 | #region .
252 | private bool IsOverUI()
253 | => EventSystem.current.IsPointerOverGameObject();
254 |
255 | /// 레이캐스트하여 얻은 첫 번째 UI에서 컴포넌트 찾아 리턴
256 | private T RaycastAndGetFirstComponent() where T : Component
257 | {
258 | _rrList.Clear();
259 |
260 | _gr.Raycast(_ped, _rrList);
261 |
262 | if(_rrList.Count == 0)
263 | return null;
264 |
265 | return _rrList[0].gameObject.GetComponent();
266 | }
267 | /// 슬롯에 포인터가 올라가는 경우, 슬롯에서 포인터가 빠져나가는 경우
268 | private void OnPointerEnterAndExit()
269 | {
270 | // 이전 프레임의 슬롯
271 | var prevSlot = _pointerOverSlot;
272 |
273 | // 현재 프레임의 슬롯
274 | var curSlot = _pointerOverSlot = RaycastAndGetFirstComponent();
275 |
276 | if (prevSlot == null)
277 | {
278 | // Enter
279 | if (curSlot != null)
280 | {
281 | OnCurrentEnter();
282 | }
283 | }
284 | else
285 | {
286 | // Exit
287 | if (curSlot == null)
288 | {
289 | OnPrevExit();
290 | }
291 |
292 | // Change
293 | else if (prevSlot != curSlot)
294 | {
295 | OnPrevExit();
296 | OnCurrentEnter();
297 | }
298 | }
299 |
300 | // ===================== Local Methods ===============================
301 | void OnCurrentEnter()
302 | {
303 | if(_showHighlight)
304 | curSlot.Highlight(true);
305 | }
306 | void OnPrevExit()
307 | {
308 | prevSlot.Highlight(false);
309 | }
310 | }
311 | /// 아이템 정보 툴팁 보여주거나 감추기
312 | private void ShowOrHideItemTooltip()
313 | {
314 | // 마우스가 유효한 아이템 아이콘 위에 올라와 있다면 툴팁 보여주기
315 | bool isValid =
316 | _pointerOverSlot != null && _pointerOverSlot.HasItem && _pointerOverSlot.IsAccessible
317 | && (_pointerOverSlot != _beginDragSlot); // 드래그 시작한 슬롯이면 보여주지 않기
318 |
319 | if (isValid)
320 | {
321 | UpdateTooltipUI(_pointerOverSlot);
322 | _itemTooltip.Show();
323 | }
324 | else
325 | _itemTooltip.Hide();
326 | }
327 | /// 슬롯에 클릭하는 경우
328 | private void OnPointerDown()
329 | {
330 | // Left Click : Begin Drag
331 | if (Input.GetMouseButtonDown(_leftClick))
332 | {
333 | _beginDragSlot = RaycastAndGetFirstComponent();
334 |
335 | // 아이템을 갖고 있는 슬롯만 해당
336 | if (_beginDragSlot != null && _beginDragSlot.HasItem && _beginDragSlot.IsAccessible)
337 | {
338 | EditorLog($"Drag Begin : Slot [{_beginDragSlot.Index}]");
339 |
340 | // 위치 기억, 참조 등록
341 | _beginDragIconTransform = _beginDragSlot.IconRect.transform;
342 | _beginDragIconPoint = _beginDragIconTransform.position;
343 | _beginDragCursorPoint = Input.mousePosition;
344 |
345 | // 맨 위에 보이기
346 | _beginDragSlotSiblingIndex = _beginDragSlot.transform.GetSiblingIndex();
347 | _beginDragSlot.transform.SetAsLastSibling();
348 |
349 | // 해당 슬롯의 하이라이트 이미지를 아이콘보다 뒤에 위치시키기
350 | _beginDragSlot.SetHighlightOnTop(false);
351 | }
352 | else
353 | {
354 | _beginDragSlot = null;
355 | }
356 | }
357 |
358 | // Right Click : Use Item
359 | else if (Input.GetMouseButtonDown(_rightClick))
360 | {
361 | ItemSlotUI slot = RaycastAndGetFirstComponent();
362 |
363 | if (slot != null && slot.HasItem && slot.IsAccessible)
364 | {
365 | TryUseItem(slot.Index);
366 | }
367 | }
368 | }
369 | /// 드래그하는 도중
370 | private void OnPointerDrag()
371 | {
372 | if(_beginDragSlot == null) return;
373 |
374 | if (Input.GetMouseButton(_leftClick))
375 | {
376 | // 위치 이동
377 | _beginDragIconTransform.position =
378 | _beginDragIconPoint + (Input.mousePosition - _beginDragCursorPoint);
379 | }
380 | }
381 | /// 클릭을 뗄 경우
382 | private void OnPointerUp()
383 | {
384 | if (Input.GetMouseButtonUp(_leftClick))
385 | {
386 | // End Drag
387 | if (_beginDragSlot != null)
388 | {
389 | // 위치 복원
390 | _beginDragIconTransform.position = _beginDragIconPoint;
391 |
392 | // UI 순서 복원
393 | _beginDragSlot.transform.SetSiblingIndex(_beginDragSlotSiblingIndex);
394 |
395 | // 드래그 완료 처리
396 | EndDrag();
397 |
398 | // 해당 슬롯의 하이라이트 이미지를 아이콘보다 앞에 위치시키기
399 | _beginDragSlot.SetHighlightOnTop(true);
400 |
401 | // 참조 제거
402 | _beginDragSlot = null;
403 | _beginDragIconTransform = null;
404 | }
405 | }
406 | }
407 |
408 | private void EndDrag()
409 | {
410 | ItemSlotUI endDragSlot = RaycastAndGetFirstComponent();
411 |
412 | // 아이템 슬롯끼리 아이콘 교환 또는 이동
413 | if (endDragSlot != null && endDragSlot.IsAccessible)
414 | {
415 | // 수량 나누기 조건
416 | // 1) 마우스 클릭 떼는 순간 좌측 Ctrl 또는 Shift 키 유지
417 | // 2) begin : 셀 수 있는 아이템 / end : 비어있는 슬롯
418 | // 3) begin 아이템의 수량 > 1
419 | bool isSeparatable =
420 | (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftShift)) &&
421 | (_inventory.IsCountableItem(_beginDragSlot.Index) && !_inventory.HasItem(endDragSlot.Index));
422 |
423 | // true : 수량 나누기, false : 교환 또는 이동
424 | bool isSeparation = false;
425 | int currentAmount = 0;
426 |
427 | // 현재 개수 확인
428 | if (isSeparatable)
429 | {
430 | currentAmount = _inventory.GetCurrentAmount(_beginDragSlot.Index);
431 | if (currentAmount > 1)
432 | {
433 | isSeparation = true;
434 | }
435 | }
436 |
437 | // 1. 개수 나누기
438 | if(isSeparation)
439 | TrySeparateAmount(_beginDragSlot.Index, endDragSlot.Index, currentAmount);
440 | // 2. 교환 또는 이동
441 | else
442 | TrySwapItems(_beginDragSlot, endDragSlot);
443 |
444 | // 툴팁 갱신
445 | UpdateTooltipUI(endDragSlot);
446 | return;
447 | }
448 |
449 | // 버리기(커서가 UI 레이캐스트 타겟 위에 있지 않은 경우)
450 | if (!IsOverUI())
451 | {
452 | // 확인 팝업 띄우고 콜백 위임
453 | int index = _beginDragSlot.Index;
454 | string itemName = _inventory.GetItemName(index);
455 | int amount = _inventory.GetCurrentAmount(index);
456 |
457 | // 셀 수 있는 아이템의 경우, 수량 표시
458 | if(amount > 1)
459 | itemName += $" x{amount}";
460 |
461 | if(_showRemovingPopup)
462 | _popup.OpenConfirmationPopup(() => TryRemoveItem(index), itemName);
463 | else
464 | TryRemoveItem(index);
465 | }
466 | // 슬롯이 아닌 다른 UI 위에 놓은 경우
467 | else
468 | {
469 | EditorLog($"Drag End(Do Nothing)");
470 | }
471 | }
472 |
473 | #endregion
474 | /***********************************************************************
475 | * Private Methods
476 | ***********************************************************************/
477 | #region .
478 |
479 | /// UI 및 인벤토리에서 아이템 제거
480 | private void TryRemoveItem(int index)
481 | {
482 | EditorLog($"UI - Try Remove Item : Slot [{index}]");
483 |
484 | _inventory.Remove(index);
485 | }
486 |
487 | /// 아이템 사용
488 | private void TryUseItem(int index)
489 | {
490 | EditorLog($"UI - Try Use Item : Slot [{index}]");
491 |
492 | _inventory.Use(index);
493 | }
494 |
495 | /// 두 슬롯의 아이템 교환
496 | private void TrySwapItems(ItemSlotUI from, ItemSlotUI to)
497 | {
498 | if (from == to)
499 | {
500 | EditorLog($"UI - Try Swap Items: Same Slot [{from.Index}]");
501 | return;
502 | }
503 |
504 | EditorLog($"UI - Try Swap Items: Slot [{from.Index} -> {to.Index}]");
505 |
506 | from.SwapOrMoveIcon(to);
507 | _inventory.Swap(from.Index, to.Index);
508 | }
509 |
510 | /// 셀 수 있는 아이템 개수 나누기
511 | private void TrySeparateAmount(int indexA, int indexB, int amount)
512 | {
513 | if (indexA == indexB)
514 | {
515 | EditorLog($"UI - Try Separate Amount: Same Slot [{indexA}]");
516 | return;
517 | }
518 |
519 | EditorLog($"UI - Try Separate Amount: Slot [{indexA} -> {indexB}]");
520 |
521 | string itemName = $"{_inventory.GetItemName(indexA)} x{amount}";
522 |
523 | _popup.OpenAmountInputPopup(
524 | amt => _inventory.SeparateAmount(indexA, indexB, amt),
525 | amount, itemName
526 | );
527 | }
528 |
529 | /// 툴팁 UI의 슬롯 데이터 갱신
530 | private void UpdateTooltipUI(ItemSlotUI slot)
531 | {
532 | if(!slot.IsAccessible || !slot.HasItem)
533 | return;
534 |
535 | // 툴팁 정보 갱신
536 | _itemTooltip.SetItemInfo(_inventory.GetItemData(slot.Index));
537 |
538 | // 툴팁 위치 조정
539 | _itemTooltip.SetRectPosition(slot.SlotRect);
540 | }
541 |
542 | #endregion
543 | /***********************************************************************
544 | * Public Methods
545 | ***********************************************************************/
546 | #region .
547 |
548 | /// 인벤토리 참조 등록 (인벤토리에서 직접 호출)
549 | public void SetInventoryReference(Inventory inventory)
550 | {
551 | _inventory = inventory;
552 | }
553 |
554 | /// 마우스 클릭 좌우 반전시키기 (true : 반전)
555 | public void InvertMouse(bool value)
556 | {
557 | _leftClick = value ? 1 : 0;
558 | _rightClick = value ? 0 : 1;
559 |
560 | _mouseReversed = value;
561 | }
562 |
563 | /// 슬롯에 아이템 아이콘 등록
564 | public void SetItemIcon(int index, Sprite icon)
565 | {
566 | EditorLog($"Set Item Icon : Slot [{index}]");
567 |
568 | _slotUIList[index].SetItem(icon);
569 | }
570 |
571 | /// 해당 슬롯의 아이템 개수 텍스트 지정
572 | public void SetItemAmountText(int index, int amount)
573 | {
574 | EditorLog($"Set Item Amount Text : Slot [{index}], Amount [{amount}]");
575 |
576 | // NOTE : amount가 1 이하일 경우 텍스트 미표시
577 | _slotUIList[index].SetItemAmount(amount);
578 | }
579 |
580 | /// 해당 슬롯의 아이템 개수 텍스트 지정
581 | public void HideItemAmountText(int index)
582 | {
583 | EditorLog($"Hide Item Amount Text : Slot [{index}]");
584 |
585 | _slotUIList[index].SetItemAmount(1);
586 | }
587 |
588 | /// 슬롯에서 아이템 아이콘 제거, 개수 텍스트 숨기기
589 | public void RemoveItem(int index)
590 | {
591 | EditorLog($"Remove Item : Slot [{index}]");
592 |
593 | _slotUIList[index].RemoveItem();
594 | }
595 |
596 | /// 접근 가능한 슬롯 범위 설정
597 | public void SetAccessibleSlotRange(int accessibleSlotCount)
598 | {
599 | for (int i = 0; i < _slotUIList.Count; i++)
600 | {
601 | _slotUIList[i].SetSlotAccessibleState(i < accessibleSlotCount);
602 | }
603 | }
604 |
605 | /// 특정 슬롯의 필터 상태 업데이트
606 | public void UpdateSlotFilterState(int index, ItemData itemData)
607 | {
608 | bool isFiltered = true;
609 |
610 | // null인 슬롯은 타입 검사 없이 필터 활성화
611 | if(itemData != null)
612 | switch (_currentFilterOption)
613 | {
614 | case FilterOption.Equipment:
615 | isFiltered = (itemData is EquipmentItemData);
616 | break;
617 |
618 | case FilterOption.Portion:
619 | isFiltered = (itemData is PortionItemData);
620 | break;
621 | }
622 |
623 | _slotUIList[index].SetItemAccessibleState(isFiltered);
624 | }
625 |
626 | /// 모든 슬롯 필터 상태 업데이트
627 | public void UpdateAllSlotFilters()
628 | {
629 | int capacity = _inventory.Capacity;
630 |
631 | for (int i = 0; i < capacity; i++)
632 | {
633 | ItemData data = _inventory.GetItemData(i);
634 | UpdateSlotFilterState(i, data);
635 | }
636 | }
637 |
638 | #endregion
639 | /***********************************************************************
640 | * Editor Only Debug
641 | ***********************************************************************/
642 | #region .
643 |
644 | [Header("Editor Options")]
645 | [SerializeField] private bool _showDebug = true;
646 | [System.Diagnostics.Conditional("UNITY_EDITOR")]
647 | private void EditorLog(object message)
648 | {
649 | if (!_showDebug) return;
650 | UnityEngine.Debug.Log($"[InventoryUI] {message}");
651 | }
652 |
653 | #endregion
654 | /***********************************************************************
655 | * Editor Preview
656 | ***********************************************************************/
657 | #region .
658 | #if UNITY_EDITOR
659 | [SerializeField] private bool __showPreview = false;
660 |
661 | [Range(0.01f, 1f)]
662 | [SerializeField] private float __previewAlpha = 0.1f;
663 |
664 | private List __previewSlotGoList = new List();
665 | private int __prevSlotCountPerLine;
666 | private int __prevSlotLineCount;
667 | private float __prevSlotSize;
668 | private float __prevSlotMargin;
669 | private float __prevContentPadding;
670 | private float __prevAlpha;
671 | private bool __prevShow = false;
672 | private bool __prevMouseReversed = false;
673 |
674 | private void OnValidate()
675 | {
676 | if (__prevMouseReversed != _mouseReversed)
677 | {
678 | __prevMouseReversed = _mouseReversed;
679 | InvertMouse(_mouseReversed);
680 |
681 | EditorLog($"Mouse Reversed : {_mouseReversed}");
682 | }
683 |
684 | if (Application.isPlaying) return;
685 |
686 | if (__showPreview && !__prevShow)
687 | {
688 | CreateSlots();
689 | }
690 | __prevShow = __showPreview;
691 |
692 | if (Unavailable())
693 | {
694 | ClearAll();
695 | return;
696 | }
697 | if (CountChanged())
698 | {
699 | ClearAll();
700 | CreateSlots();
701 | __prevSlotCountPerLine = _horizontalSlotCount;
702 | __prevSlotLineCount = _verticalSlotCount;
703 | }
704 | if (ValueChanged())
705 | {
706 | DrawGrid();
707 | __prevSlotSize = _slotSize;
708 | __prevSlotMargin = _slotMargin;
709 | __prevContentPadding = _contentAreaPadding;
710 | }
711 | if (AlphaChanged())
712 | {
713 | SetImageAlpha();
714 | __prevAlpha = __previewAlpha;
715 | }
716 |
717 | bool Unavailable()
718 | {
719 | return !__showPreview ||
720 | _horizontalSlotCount < 1 ||
721 | _verticalSlotCount < 1 ||
722 | _slotSize <= 0f ||
723 | _contentAreaRT == null ||
724 | _slotUiPrefab == null;
725 | }
726 | bool CountChanged()
727 | {
728 | return _horizontalSlotCount != __prevSlotCountPerLine ||
729 | _verticalSlotCount != __prevSlotLineCount;
730 | }
731 | bool ValueChanged()
732 | {
733 | return _slotSize != __prevSlotSize ||
734 | _slotMargin != __prevSlotMargin ||
735 | _contentAreaPadding != __prevContentPadding;
736 | }
737 | bool AlphaChanged()
738 | {
739 | return __previewAlpha != __prevAlpha;
740 | }
741 | void ClearAll()
742 | {
743 | foreach (var go in __previewSlotGoList)
744 | {
745 | Destroyer.Destroy(go);
746 | }
747 | __previewSlotGoList.Clear();
748 | }
749 | void CreateSlots()
750 | {
751 | int count = _horizontalSlotCount * _verticalSlotCount;
752 | __previewSlotGoList.Capacity = count;
753 |
754 | // 슬롯의 피벗은 Left Top으로 고정
755 | RectTransform slotPrefabRT = _slotUiPrefab.GetComponent();
756 | slotPrefabRT.pivot = new Vector2(0f, 1f);
757 |
758 | for (int i = 0; i < count; i++)
759 | {
760 | GameObject slotGo = Instantiate(_slotUiPrefab);
761 | slotGo.transform.SetParent(_contentAreaRT.transform);
762 | slotGo.SetActive(true);
763 | slotGo.AddComponent();
764 |
765 | slotGo.transform.localScale = Vector3.one; // 버그 해결
766 |
767 | HideGameObject(slotGo);
768 |
769 | __previewSlotGoList.Add(slotGo);
770 | }
771 |
772 | DrawGrid();
773 | SetImageAlpha();
774 | }
775 | void DrawGrid()
776 | {
777 | Vector2 beginPos = new Vector2(_contentAreaPadding, -_contentAreaPadding);
778 | Vector2 curPos = beginPos;
779 |
780 | // Draw Slots
781 | int index = 0;
782 | for (int j = 0; j < _verticalSlotCount; j++)
783 | {
784 | for (int i = 0; i < _horizontalSlotCount; i++)
785 | {
786 | GameObject slotGo = __previewSlotGoList[index++];
787 | RectTransform slotRT = slotGo.GetComponent();
788 |
789 | slotRT.anchoredPosition = curPos;
790 | slotRT.sizeDelta = new Vector2(_slotSize, _slotSize);
791 | __previewSlotGoList.Add(slotGo);
792 |
793 | // Next X
794 | curPos.x += (_slotMargin + _slotSize);
795 | }
796 |
797 | // Next Line
798 | curPos.x = beginPos.x;
799 | curPos.y -= (_slotMargin + _slotSize);
800 | }
801 | }
802 | void HideGameObject(GameObject go)
803 | {
804 | go.hideFlags = HideFlags.HideAndDontSave;
805 |
806 | Transform tr = go.transform;
807 | for (int i = 0; i < tr.childCount; i++)
808 | {
809 | tr.GetChild(i).gameObject.hideFlags = HideFlags.HideAndDontSave;
810 | }
811 | }
812 | void SetImageAlpha()
813 | {
814 | foreach (var go in __previewSlotGoList)
815 | {
816 | var images = go.GetComponentsInChildren();
817 | foreach (var img in images)
818 | {
819 | img.color = new Color(img.color.r, img.color.g, img.color.b, __previewAlpha);
820 | var outline = img.GetComponent();
821 | if (outline)
822 | outline.effectColor = new Color(outline.effectColor.r, outline.effectColor.g, outline.effectColor.b, __previewAlpha);
823 | }
824 | }
825 | }
826 | }
827 |
828 | private class PreviewItemSlot : MonoBehaviour { }
829 |
830 | [UnityEditor.InitializeOnLoad]
831 | private static class Destroyer
832 | {
833 | private static Queue targetQueue = new Queue();
834 |
835 | static Destroyer()
836 | {
837 | UnityEditor.EditorApplication.update += () =>
838 | {
839 | for (int i = 0; targetQueue.Count > 0 && i < 100000; i++)
840 | {
841 | var next = targetQueue.Dequeue();
842 | DestroyImmediate(next);
843 | }
844 | };
845 | }
846 | public static void Destroy(GameObject go) => targetQueue.Enqueue(go);
847 | }
848 | #endif
849 |
850 | #endregion
851 | }
852 | }
--------------------------------------------------------------------------------
/Scripts/UI/InventoryUI.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f5b81c692dc3d044483c3ae773ada1f0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/UI/ItemSlotUI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 | using UnityEngine.EventSystems;
7 |
8 | // 날짜 : 2021-03-07 PM 10:20:05
9 | // 작성자 : Rito
10 |
11 | namespace Rito.InventorySystem
12 | {
13 | public class ItemSlotUI : MonoBehaviour
14 | {
15 | /***********************************************************************
16 | * Option Fields
17 | ***********************************************************************/
18 | #region .
19 | [Tooltip("슬롯 내에서 아이콘과 슬롯 사이의 여백")]
20 | [SerializeField] private float _padding = 1f;
21 |
22 | [Tooltip("아이템 아이콘 이미지")]
23 | [SerializeField] private Image _iconImage;
24 |
25 | [Tooltip("아이템 개수 텍스트")]
26 | [SerializeField] private Text _amountText;
27 |
28 | [Tooltip("슬롯이 포커스될 때 나타나는 하이라이트 이미지")]
29 | [SerializeField] private Image _highlightImage;
30 |
31 | [Space]
32 | [Tooltip("하이라이트 이미지 알파 값")]
33 | [SerializeField] private float _highlightAlpha = 0.5f;
34 |
35 | [Tooltip("하이라이트 소요 시간")]
36 | [SerializeField] private float _highlightFadeDuration = 0.2f;
37 |
38 | #endregion
39 | /***********************************************************************
40 | * Properties
41 | ***********************************************************************/
42 | #region .
43 | /// 슬롯의 인덱스
44 | public int Index { get; private set; }
45 |
46 | /// 슬롯이 아이템을 보유하고 있는지 여부
47 | public bool HasItem => _iconImage.sprite != null;
48 |
49 | /// 접근 가능한 슬롯인지 여부
50 | public bool IsAccessible => _isAccessibleSlot && _isAccessibleItem;
51 |
52 | public RectTransform SlotRect => _slotRect;
53 | public RectTransform IconRect => _iconRect;
54 |
55 | #endregion
56 | /***********************************************************************
57 | * Fields
58 | ***********************************************************************/
59 | #region .
60 | private InventoryUI _inventoryUI;
61 |
62 | private RectTransform _slotRect;
63 | private RectTransform _iconRect;
64 | private RectTransform _highlightRect;
65 |
66 | private GameObject _iconGo;
67 | private GameObject _textGo;
68 | private GameObject _highlightGo;
69 |
70 | private Image _slotImage;
71 |
72 | // 현재 하이라이트 알파값
73 | private float _currentHLAlpha = 0f;
74 |
75 | private bool _isAccessibleSlot = true; // 슬롯 접근가능 여부
76 | private bool _isAccessibleItem = true; // 아이템 접근가능 여부
77 |
78 | /// 비활성화된 슬롯의 색상
79 | private static readonly Color InaccessibleSlotColor = new Color(0.2f, 0.2f, 0.2f, 0.5f);
80 | /// 비활성화된 아이콘 색상
81 | private static readonly Color InaccessibleIconColor = new Color(0.5f, 0.5f, 0.5f, 0.5f);
82 |
83 | #endregion
84 | /***********************************************************************
85 | * Unity Events
86 | ***********************************************************************/
87 | #region .
88 | private void Awake()
89 | {
90 | InitComponents();
91 | InitValues();
92 | }
93 |
94 | #endregion
95 | /***********************************************************************
96 | * Private Methods
97 | ***********************************************************************/
98 | #region .
99 | private void InitComponents()
100 | {
101 | _inventoryUI = GetComponentInParent();
102 |
103 | // Rects
104 | _slotRect = GetComponent();
105 | _iconRect = _iconImage.rectTransform;
106 | _highlightRect = _highlightImage.rectTransform;
107 |
108 | // Game Objects
109 | _iconGo = _iconRect.gameObject;
110 | _textGo = _amountText.gameObject;
111 | _highlightGo = _highlightImage.gameObject;
112 |
113 | // Images
114 | _slotImage = GetComponent();
115 | }
116 | private void InitValues()
117 | {
118 | // 1. Item Icon, Highlight Rect
119 | _iconRect.pivot = new Vector2(0.5f, 0.5f); // 피벗은 중앙
120 | _iconRect.anchorMin = Vector2.zero; // 앵커는 Top Left
121 | _iconRect.anchorMax = Vector2.one;
122 |
123 | // 패딩 조절
124 | _iconRect.offsetMin = Vector2.one * (_padding);
125 | _iconRect.offsetMax = Vector2.one * (-_padding);
126 |
127 | // 아이콘과 하이라이트 크기가 동일하도록
128 | _highlightRect.pivot = _iconRect.pivot;
129 | _highlightRect.anchorMin = _iconRect.anchorMin;
130 | _highlightRect.anchorMax = _iconRect.anchorMax;
131 | _highlightRect.offsetMin = _iconRect.offsetMin;
132 | _highlightRect.offsetMax = _iconRect.offsetMax;
133 |
134 | // 2. Image
135 | _iconImage.raycastTarget = false;
136 | _highlightImage.raycastTarget = false;
137 |
138 | // 3. Deactivate Icon
139 | HideIcon();
140 | _highlightGo.SetActive(false);
141 | }
142 |
143 | private void ShowIcon() => _iconGo.SetActive(true);
144 | private void HideIcon() => _iconGo.SetActive(false);
145 |
146 | private void ShowText() => _textGo.SetActive(true);
147 | private void HideText() => _textGo.SetActive(false);
148 |
149 | #endregion
150 | /***********************************************************************
151 | * Public Methods
152 | ***********************************************************************/
153 | #region .
154 |
155 | public void SetSlotIndex(int index) => Index = index;
156 |
157 | /// 슬롯 자체의 활성화/비활성화 여부 설정
158 | public void SetSlotAccessibleState(bool value)
159 | {
160 | // 중복 처리는 지양
161 | if (_isAccessibleSlot == value) return;
162 |
163 | if (value)
164 | {
165 | _slotImage.color = Color.black;
166 | }
167 | else
168 | {
169 | _slotImage.color = InaccessibleSlotColor;
170 | HideIcon();
171 | HideText();
172 | }
173 |
174 | _isAccessibleSlot = value;
175 | }
176 |
177 | /// 아이템 활성화/비활성화 여부 설정
178 | public void SetItemAccessibleState(bool value)
179 | {
180 | // 중복 처리는 지양
181 | if(_isAccessibleItem == value) return;
182 |
183 | if (value)
184 | {
185 | _iconImage.color = Color.white;
186 | _amountText.color = Color.white;
187 | }
188 | else
189 | {
190 | _iconImage.color = InaccessibleIconColor;
191 | _amountText.color = InaccessibleIconColor;
192 | }
193 |
194 | _isAccessibleItem = value;
195 | }
196 |
197 | /// 다른 슬롯과 아이템 아이콘 교환
198 | public void SwapOrMoveIcon(ItemSlotUI other)
199 | {
200 | if (other == null) return;
201 | if (other == this) return; // 자기 자신과 교환 불가
202 | if (!this.IsAccessible) return;
203 | if (!other.IsAccessible) return;
204 |
205 | var temp = _iconImage.sprite;
206 |
207 | // 1. 대상에 아이템이 있는 경우 : 교환
208 | if (other.HasItem) SetItem(other._iconImage.sprite);
209 |
210 | // 2. 없는 경우 : 이동
211 | else RemoveItem();
212 |
213 | other.SetItem(temp);
214 | }
215 |
216 | /// 슬롯에 아이템 등록
217 | public void SetItem(Sprite itemSprite)
218 | {
219 | //if (!this.IsAccessible) return;
220 |
221 | if (itemSprite != null)
222 | {
223 | _iconImage.sprite = itemSprite;
224 | ShowIcon();
225 | }
226 | else
227 | {
228 | RemoveItem();
229 | }
230 | }
231 |
232 | /// 슬롯에서 아이템 제거
233 | public void RemoveItem()
234 | {
235 | _iconImage.sprite = null;
236 | HideIcon();
237 | HideText();
238 | }
239 |
240 | /// 아이템 이미지 투명도 설정
241 | public void SetIconAlpha(float alpha)
242 | {
243 | _iconImage.color = new Color(
244 | _iconImage.color.r, _iconImage.color.g, _iconImage.color.b, alpha
245 | );
246 | }
247 |
248 | /// 아이템 개수 텍스트 설정(amount가 1 이하일 경우 텍스트 미표시)
249 | public void SetItemAmount(int amount)
250 | {
251 | //if (!this.IsAccessible) return;
252 |
253 | if (HasItem && amount > 1)
254 | ShowText();
255 | else
256 | HideText();
257 |
258 | _amountText.text = amount.ToString();
259 | }
260 |
261 | /// 슬롯에 하이라이트 표시/해제
262 | public void Highlight(bool show)
263 | {
264 | if (!this.IsAccessible) return;
265 |
266 | if (show)
267 | StartCoroutine(nameof(HighlightFadeInRoutine));
268 | else
269 | StartCoroutine(nameof(HighlightFadeOutRoutine));
270 | }
271 |
272 | /// 하이라이트 이미지를 아이콘 이미지의 상단/하단으로 표시
273 | public void SetHighlightOnTop(bool value)
274 | {
275 | if(value)
276 | _highlightRect.SetAsLastSibling();
277 | else
278 | _highlightRect.SetAsFirstSibling();
279 | }
280 |
281 | #endregion
282 | /***********************************************************************
283 | * Coroutines
284 | ***********************************************************************/
285 | #region .
286 | /// 하이라이트 알파값 서서히 증가
287 | private IEnumerator HighlightFadeInRoutine()
288 | {
289 | StopCoroutine(nameof(HighlightFadeOutRoutine));
290 | _highlightGo.SetActive(true);
291 |
292 | float unit = _highlightAlpha / _highlightFadeDuration;
293 |
294 | for (; _currentHLAlpha <= _highlightAlpha; _currentHLAlpha += unit * Time.deltaTime)
295 | {
296 | _highlightImage.color = new Color(
297 | _highlightImage.color.r,
298 | _highlightImage.color.g,
299 | _highlightImage.color.b,
300 | _currentHLAlpha
301 | );
302 |
303 | yield return null;
304 | }
305 | }
306 |
307 | /// 하이라이트 알파값 0%까지 서서히 감소
308 | private IEnumerator HighlightFadeOutRoutine()
309 | {
310 | StopCoroutine(nameof(HighlightFadeInRoutine));
311 |
312 | float unit = _highlightAlpha / _highlightFadeDuration;
313 |
314 | for (; _currentHLAlpha >= 0f; _currentHLAlpha -= unit * Time.deltaTime)
315 | {
316 | _highlightImage.color = new Color(
317 | _highlightImage.color.r,
318 | _highlightImage.color.g,
319 | _highlightImage.color.b,
320 | _currentHLAlpha
321 | );
322 |
323 | yield return null;
324 | }
325 |
326 | _highlightGo.SetActive(false);
327 | }
328 |
329 | #endregion
330 | }
331 | }
--------------------------------------------------------------------------------
/Scripts/UI/ItemSlotUI.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8199efbdbf68e8548b9c046b339fab53
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/UI/ItemTooltipUI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 |
7 | // 날짜 : 2021-04-01 PM 8:33:22
8 | // 작성자 : Rito
9 |
10 | namespace Rito.InventorySystem
11 | {
12 | /// 슬롯 내의 아이템 아이콘에 마우스를 올렸을 때 보이는 툴팁
13 | public class ItemTooltipUI : MonoBehaviour
14 | {
15 | /***********************************************************************
16 | * Inspector Option Fields
17 | ***********************************************************************/
18 | #region .
19 | [SerializeField]
20 | private Text _titleText; // 아이템 이름 텍스트
21 |
22 | [SerializeField]
23 | private Text _contentText; // 아이템 설명 텍스트
24 |
25 | #endregion
26 | /***********************************************************************
27 | * Private Fields
28 | ***********************************************************************/
29 | #region .
30 | private RectTransform _rt;
31 | private CanvasScaler _canvasScaler;
32 |
33 | private static readonly Vector2 LeftTop = new Vector2(0f, 1f);
34 | private static readonly Vector2 LeftBottom = new Vector2(0f, 0f);
35 | private static readonly Vector2 RightTop = new Vector2(1f, 1f);
36 | private static readonly Vector2 RightBottom = new Vector2(1f, 0f);
37 |
38 | #endregion
39 | /***********************************************************************
40 | * Unity Events
41 | ***********************************************************************/
42 | #region .
43 | private void Awake()
44 | {
45 | Init();
46 | Hide();
47 | }
48 |
49 | #endregion
50 | /***********************************************************************
51 | * Private Methods
52 | ***********************************************************************/
53 | #region .
54 | private void Init()
55 | {
56 | TryGetComponent(out _rt);
57 | _rt.pivot = LeftTop;
58 | _canvasScaler = GetComponentInParent();
59 |
60 | DisableAllChildrenRaycastTarget(transform);
61 | }
62 |
63 | /// 모든 자식 UI에 레이캐스트 타겟 해제
64 | private void DisableAllChildrenRaycastTarget(Transform tr)
65 | {
66 | // 본인이 Graphic(UI)를 상속하면 레이캐스트 타겟 해제
67 | tr.TryGetComponent(out Graphic gr);
68 | if(gr != null)
69 | gr.raycastTarget = false;
70 |
71 | // 자식이 없으면 종료
72 | int childCount = tr.childCount;
73 | if (childCount == 0) return;
74 |
75 | for (int i = 0; i < childCount; i++)
76 | {
77 | DisableAllChildrenRaycastTarget(tr.GetChild(i));
78 | }
79 | }
80 |
81 | #endregion
82 | /***********************************************************************
83 | * Public Methods
84 | ***********************************************************************/
85 | #region .
86 | /// 툴팁 UI에 아이템 정보 등록
87 | public void SetItemInfo(ItemData data)
88 | {
89 | _titleText.text = data.Name;
90 | _contentText.text = data.Tooltip;
91 | }
92 |
93 | /// 툴팁의 위치 조정
94 | public void SetRectPosition(RectTransform slotRect)
95 | {
96 | // 캔버스 스케일러에 따른 해상도 대응
97 | float wRatio = Screen.width / _canvasScaler.referenceResolution.x;
98 | float hRatio = Screen.height / _canvasScaler.referenceResolution.y;
99 | float ratio =
100 | wRatio * (1f - _canvasScaler.matchWidthOrHeight) +
101 | hRatio * (_canvasScaler.matchWidthOrHeight);
102 |
103 | float slotWidth = slotRect.rect.width * ratio;
104 | float slotHeight = slotRect.rect.height * ratio;
105 |
106 | // 툴팁 초기 위치(슬롯 우하단) 설정
107 | _rt.position = slotRect.position + new Vector3(slotWidth, -slotHeight);
108 | Vector2 pos = _rt.position;
109 |
110 | // 툴팁의 크기
111 | float width = _rt.rect.width * ratio;
112 | float height = _rt.rect.height * ratio;
113 |
114 | // 우측, 하단이 잘렸는지 여부
115 | bool rightTruncated = pos.x + width > Screen.width;
116 | bool bottomTruncated = pos.y - height < 0f;
117 |
118 | ref bool R = ref rightTruncated;
119 | ref bool B = ref bottomTruncated;
120 |
121 | // 오른쪽만 잘림 => 슬롯의 Left Bottom 방향으로 표시
122 | if (R && !B)
123 | {
124 | _rt.position = new Vector2(pos.x - width - slotWidth, pos.y);
125 | }
126 | // 아래쪽만 잘림 => 슬롯의 Right Top 방향으로 표시
127 | else if (!R && B)
128 | {
129 | _rt.position = new Vector2(pos.x, pos.y + height + slotHeight);
130 | }
131 | // 모두 잘림 => 슬롯의 Left Top 방향으로 표시
132 | else if (R && B)
133 | {
134 | _rt.position = new Vector2(pos.x - width - slotWidth, pos.y + height + slotHeight);
135 | }
136 | // 잘리지 않음 => 슬롯의 Right Bottom 방향으로 표시
137 | // Do Nothing
138 | }
139 |
140 | public void Show() => gameObject.SetActive(true);
141 |
142 | public void Hide() => gameObject.SetActive(false);
143 |
144 | #endregion
145 | }
146 | }
--------------------------------------------------------------------------------
/Scripts/UI/ItemTooltipUI.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: da0ca6a2d0d960540ae2cb7cd6bd64ec
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Scripts/UI/MovableHeaderUI.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.EventSystems;
3 |
4 | // 날짜 : 2021-03-18 PM 9:05:42
5 | // 작성자 : Rito
6 |
7 | namespace Rito
8 | {
9 | /// 헤더 드래그 앤 드롭에 의한 UI 이동
10 | public class MovableHeaderUI : MonoBehaviour, IPointerDownHandler, IDragHandler
11 | {
12 | [SerializeField]
13 | private Transform _targetTr; // 이동될 UI
14 |
15 | private Vector2 _beginPoint;
16 | private Vector2 _moveBegin;
17 |
18 | private void Awake()
19 | {
20 | // 이동 대상 UI를 지정하지 않은 경우, 자동으로 부모로 초기화
21 | if(_targetTr == null)
22 | _targetTr = transform.parent;
23 | }
24 |
25 | // 드래그 시작 위치 지정
26 | void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
27 | {
28 | _beginPoint = _targetTr.position;
29 | _moveBegin = eventData.position;
30 | }
31 |
32 | // 드래그 : 마우스 커서 위치로 이동
33 | void IDragHandler.OnDrag(PointerEventData eventData)
34 | {
35 | _targetTr.position = _beginPoint + (eventData.position - _moveBegin);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Scripts/UI/MovableHeaderUI.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5cefe2861ad5fe649b5ac07155fce36c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Sprites.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bcf4a0f9a366c6a4e995c26717728302
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Sprites/ElixirBlue_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rito15/Unity-RPG-Inventory/e0ed8276483161d4f5ba24fc142d8a3dccc386cb/Sprites/ElixirBlue_64.png
--------------------------------------------------------------------------------
/Sprites/ElixirBlue_64.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ac64af182bf5c9c4a8a16cb35547a3cb
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 0
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 64
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | - serializedVersion: 3
75 | buildTarget: Standalone
76 | maxTextureSize: 64
77 | resizeAlgorithm: 0
78 | textureFormat: -1
79 | textureCompression: 1
80 | compressionQuality: 50
81 | crunchedCompression: 0
82 | allowsAlphaSplitting: 0
83 | overridden: 0
84 | androidETC2FallbackOverride: 0
85 | forceMaximumCompressionQuality_BC6H_BC7: 0
86 | spriteSheet:
87 | serializedVersion: 2
88 | sprites: []
89 | outline: []
90 | physicsShape: []
91 | bones: []
92 | spriteID: 5e97eb03825dee720800000000000000
93 | internalID: 0
94 | vertices: []
95 | indices:
96 | edges: []
97 | weights: []
98 | secondaryTextures: []
99 | spritePackingTag:
100 | pSDRemoveMatte: 0
101 | pSDShowRemoveMatteOption: 0
102 | userData:
103 | assetBundleName:
104 | assetBundleVariant:
105 |
--------------------------------------------------------------------------------
/Sprites/ElixirRed_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rito15/Unity-RPG-Inventory/e0ed8276483161d4f5ba24fc142d8a3dccc386cb/Sprites/ElixirRed_64.png
--------------------------------------------------------------------------------
/Sprites/ElixirRed_64.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c2ebadfcb9b8cbb4a8ae6f124b17e332
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 0
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 64
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | - serializedVersion: 3
75 | buildTarget: Standalone
76 | maxTextureSize: 64
77 | resizeAlgorithm: 0
78 | textureFormat: -1
79 | textureCompression: 1
80 | compressionQuality: 50
81 | crunchedCompression: 0
82 | allowsAlphaSplitting: 0
83 | overridden: 0
84 | androidETC2FallbackOverride: 0
85 | forceMaximumCompressionQuality_BC6H_BC7: 0
86 | spriteSheet:
87 | serializedVersion: 2
88 | sprites: []
89 | outline: []
90 | physicsShape: []
91 | bones: []
92 | spriteID: 5e97eb03825dee720800000000000000
93 | internalID: 0
94 | vertices: []
95 | indices:
96 | edges: []
97 | weights: []
98 | secondaryTextures: []
99 | spritePackingTag:
100 | pSDRemoveMatte: 0
101 | pSDShowRemoveMatteOption: 0
102 | userData:
103 | assetBundleName:
104 | assetBundleVariant:
105 |
--------------------------------------------------------------------------------
/Sprites/Helmet_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rito15/Unity-RPG-Inventory/e0ed8276483161d4f5ba24fc142d8a3dccc386cb/Sprites/Helmet_64.png
--------------------------------------------------------------------------------
/Sprites/Helmet_64.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2a3a301121704ab45bb55331eb763958
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 0
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 64
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | - serializedVersion: 3
75 | buildTarget: Standalone
76 | maxTextureSize: 64
77 | resizeAlgorithm: 0
78 | textureFormat: -1
79 | textureCompression: 1
80 | compressionQuality: 50
81 | crunchedCompression: 0
82 | allowsAlphaSplitting: 0
83 | overridden: 0
84 | androidETC2FallbackOverride: 0
85 | forceMaximumCompressionQuality_BC6H_BC7: 0
86 | spriteSheet:
87 | serializedVersion: 2
88 | sprites: []
89 | outline: []
90 | physicsShape: []
91 | bones: []
92 | spriteID: 5e97eb03825dee720800000000000000
93 | internalID: 0
94 | vertices: []
95 | indices:
96 | edges: []
97 | weights: []
98 | secondaryTextures: []
99 | spritePackingTag:
100 | pSDRemoveMatte: 0
101 | pSDShowRemoveMatteOption: 0
102 | userData:
103 | assetBundleName:
104 | assetBundleVariant:
105 |
--------------------------------------------------------------------------------
/Sprites/SwordBlue_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rito15/Unity-RPG-Inventory/e0ed8276483161d4f5ba24fc142d8a3dccc386cb/Sprites/SwordBlue_64.png
--------------------------------------------------------------------------------
/Sprites/SwordBlue_64.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bb1bdd56c1352fb419836f9c2b0bf4bf
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 0
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 64
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | - serializedVersion: 3
75 | buildTarget: Standalone
76 | maxTextureSize: 64
77 | resizeAlgorithm: 0
78 | textureFormat: -1
79 | textureCompression: 1
80 | compressionQuality: 50
81 | crunchedCompression: 0
82 | allowsAlphaSplitting: 0
83 | overridden: 0
84 | androidETC2FallbackOverride: 0
85 | forceMaximumCompressionQuality_BC6H_BC7: 0
86 | spriteSheet:
87 | serializedVersion: 2
88 | sprites: []
89 | outline: []
90 | physicsShape: []
91 | bones: []
92 | spriteID: 5e97eb03825dee720800000000000000
93 | internalID: 0
94 | vertices: []
95 | indices:
96 | edges: []
97 | weights: []
98 | secondaryTextures: []
99 | spritePackingTag:
100 | pSDRemoveMatte: 0
101 | pSDShowRemoveMatteOption: 0
102 | userData:
103 | assetBundleName:
104 | assetBundleVariant:
105 |
--------------------------------------------------------------------------------
/Sprites/SwordRed_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rito15/Unity-RPG-Inventory/e0ed8276483161d4f5ba24fc142d8a3dccc386cb/Sprites/SwordRed_64.png
--------------------------------------------------------------------------------
/Sprites/SwordRed_64.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1f0a1cc5b62b0d747928186abd4be7c2
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 0
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 64
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | - serializedVersion: 3
75 | buildTarget: Standalone
76 | maxTextureSize: 64
77 | resizeAlgorithm: 0
78 | textureFormat: -1
79 | textureCompression: 1
80 | compressionQuality: 50
81 | crunchedCompression: 0
82 | allowsAlphaSplitting: 0
83 | overridden: 0
84 | androidETC2FallbackOverride: 0
85 | forceMaximumCompressionQuality_BC6H_BC7: 0
86 | spriteSheet:
87 | serializedVersion: 2
88 | sprites: []
89 | outline: []
90 | physicsShape: []
91 | bones: []
92 | spriteID: 5e97eb03825dee720800000000000000
93 | internalID: 0
94 | vertices: []
95 | indices:
96 | edges: []
97 | weights: []
98 | secondaryTextures: []
99 | spritePackingTag:
100 | pSDRemoveMatte: 0
101 | pSDShowRemoveMatteOption: 0
102 | userData:
103 | assetBundleName:
104 | assetBundleVariant:
105 |
--------------------------------------------------------------------------------
/Sprites/WhiteArmor_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rito15/Unity-RPG-Inventory/e0ed8276483161d4f5ba24fc142d8a3dccc386cb/Sprites/WhiteArmor_64.png
--------------------------------------------------------------------------------
/Sprites/WhiteArmor_64.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a1f270ff46445564e9afa40d0d9574db
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 0
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 64
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | - serializedVersion: 3
75 | buildTarget: Standalone
76 | maxTextureSize: 64
77 | resizeAlgorithm: 0
78 | textureFormat: -1
79 | textureCompression: 1
80 | compressionQuality: 50
81 | crunchedCompression: 0
82 | allowsAlphaSplitting: 0
83 | overridden: 0
84 | androidETC2FallbackOverride: 0
85 | forceMaximumCompressionQuality_BC6H_BC7: 0
86 | spriteSheet:
87 | serializedVersion: 2
88 | sprites: []
89 | outline: []
90 | physicsShape: []
91 | bones: []
92 | spriteID: 5e97eb03825dee720800000000000000
93 | internalID: 0
94 | vertices: []
95 | indices:
96 | edges: []
97 | weights: []
98 | secondaryTextures: []
99 | spritePackingTag:
100 | pSDRemoveMatte: 0
101 | pSDShowRemoveMatteOption: 0
102 | userData:
103 | assetBundleName:
104 | assetBundleVariant:
105 |
--------------------------------------------------------------------------------