├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
└── Scripts
├── Inventory.cs
├── InventoryController.cs
├── InventoryGrid.cs
├── Item.cs
└── ItemData.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.meta
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Davidsouzals
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Inventory Tetris
2 |
3 |
4 | A Tetris-style inventory system for Unity would feature a grid where players organize items with various shapes into an efficient layout. Items must be rotated and strategically placed to optimize space.
5 |
6 |
7 | ## Summary
8 |
9 | [1. Introduction](#introduction)
10 |
11 | [2. Starting](#starting)
12 |
13 | [3. Creating Inventory](#creating-inventory)
14 |
15 | [4. Creating Items](#creating-items)
16 |
17 | [5. Testing The Inventory](#testing-the-inventory)
18 |
19 | ### Introduction
20 |
21 | Hi, welcome to an attempt to document the implementation of the
22 | inventory system in your project!!
23 | Feel free to contact me if something goes wrong or if you have any good
24 | ideas to implement in the project. :)
25 |
26 |
27 | ### Starting
28 |
29 | First open the "Inventory" script, go to the first lines where there is the static class "InventorySettings" and choose the desired options for your inventory.
30 |
31 |
32 | 
33 |
34 |
35 |
36 | Then we need to create a "Canvas" in Unity.
37 |
38 | Click with the right mouse button to open the Unity menu and create a new canvas, follow the image as an example:
39 |
40 |
41 | 
42 |
43 | After adding the Canvas, we will create a new object within the canvas and name it as we wish, in my case I will use "Inventory", because this will be the object responsible for keeping the main "Inventory" script.
44 |
45 | Right after creating, we need to set a configuration in the "RectTransform" component of our new object.
46 |
47 | Hold Shift + Alt and change the stretch, that way:
48 |
49 |
50 | 
51 |
52 |
53 | I'm going to add a camera to the scene, so we don't get that "No camera rendering" warning.
54 |
55 | Let's also add another object of type "Image" inside our new Inventory object, I'll call it "Backpack [6x3]",
56 | I will use the name backpack because this object will be responsible for being our “Backpack” and the values that come after will be the slot numbers that our backpack will have.
57 |
58 |
59 | 
60 |
61 |
62 | Now let's do some simple math, as I want my backpack to be 6x3 we need to calculate each separate value using the slot size as the second value.
63 |
64 |
65 |
66 | I will use the 96x96 slot size, the same as the one I use in my current project.
67 |
68 | It will look like this:
69 |
70 | - Width: 6x96 = 576
71 | - Height: 3x96 = 288
72 |
73 |
74 | I changed the color of the image component too, after these changes we should have something similar to this:
75 |
76 |
77 | 
78 |
79 |
80 | Let's create another object called "Grid", this object will be a child of the "Backpack [6x3]" object and every backpack object needs a grid object.
81 |
82 | In the grid object, I will also add an image element.
83 |
84 | > [!IMPORTANT]
85 | IMPORTANT POINT: Use Shift + Alt to change your Stretch to the top left corner.
86 |
87 |
88 | 
89 |
90 | ### Creating Inventory
91 |
92 | Now we get to the best part, adding scripts and building our inventory.
93 |
94 | We will need to add 2 scripts to our Inventory object.
95 | They are:
96 |
97 |
98 | - Inventory
99 | - InventoryController
100 |
101 |
102 | Later on I'll talk more about each one, for now let's try to make it work first.
103 |
104 |
105 | 
106 |
107 |
108 | Now let's add the "InventoryGrid" script to our "Backpack [6x3]" object.
109 |
110 |
111 | 
112 |
113 |
114 | Now that we have the necessary scripts, let's add the variables for each one.
115 |
116 | We already have the necessary scripts, let's add our "Grid" object to the InventoryGrid variable "RectTransform", like this:
117 |
118 |
119 | 
120 |
121 |
122 | And let's set the grid size to the size of our backpack, in my case 6x3.
123 |
124 |
125 | 
126 |
127 |
128 | Alright, now we just need to place a sprite of the size we want our slot to have in our "Grid" object in the "Image" component, after we add the sprite, let's go under the "Image" component in the "ImageType" option and select the Tilled option.
129 |
130 | I'll leave the image I use in my project so you can get an idea of how it works.
131 |
132 |
133 |
134 | Our grid object will look like this:
135 |
136 |
137 | 
138 |
139 | ### Creating Items
140 |
141 | Now let's create some items to test our inventory :)
142 |
143 | To create an item we will need to create a folder called "Items" or something similar, after creating it, right click inside it and look for the "Create/InventoryTetris/ItemData" option.
144 |
145 | It will look something like this image:
146 |
147 |
148 | 
149 |
150 |
151 | The item name is defined by the name of the ScriptableObject. After choosing the name, we will choose how our object will work.
152 |
153 | Select a good photo for the item and choose the size it will use from the inventory.
154 |
155 |
156 | 
157 |
158 |
159 | Let's go to the last step now, creating the prefabricated object.
160 |
161 |
162 | - Create a new empty object inside the canvas and name it "ItemPrefab", go to "RectTransform" and select the width and height of the slot size that you configured in "InventorySettings".
163 | - Then create two more image objects, name one "Icon" and the other "Background".
164 | - Leave the two image-type objects you just created with the maximized stretch the same as we did a while ago.
165 | - Add the "Item" script to the object we just created and place the "Icon" and "Background" components and place the image components we just created in them.
166 |
167 |
168 |
169 |
170 | 
171 |
172 |
173 | Now save this item we just created in some folder, I recommend creating a folder called "Prefabs" and put it there
174 |
175 |
176 | 
177 |
178 | ### Testing The Inventory
179 |
180 | Now we have almost everything to start using our inventory.
181 |
182 | Let's drag our "ItemPrefab" to the "Inventory" script and place it in the corresponding variable.
183 |
184 |
185 | 
186 |
187 |
188 | And we will also add the data of the items we created previously to the "Inventory" script, like this:
189 |
190 |
191 | 
192 |
193 |
194 | Now let's test our inventory :)
195 |
196 | but
197 |
198 | First of all, you can change the buttons in the "InventoryController" script, in addition, all the code is commented and written semantically.
199 |
200 | If you have any ideas, don't hesitate to try!!
201 | Thank you for your patience and follow the video of our inventory working.
202 |
203 |
204 | https://github.com/DavidSouzaLD/InventoryTetris-Unity/assets/100738882/c4e7d658-c2d9-460d-833e-1079066e97f7
205 |
206 | https://github.com/DavidSouzaLD/InventoryTetris-Unity/assets/100738882/3af77012-27dc-4489-8972-ec2e4bf83acb
207 |
208 | https://github.com/DavidSouzaLD/InventoryTetris-Unity/assets/100738882/87ad7602-934e-433d-b9bf-6ca447e6439a
209 |
--------------------------------------------------------------------------------
/Scripts/Inventory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | public static class InventorySettings
5 | {
6 | ///
7 | /// Size that each slot has.
8 | ///
9 | public static readonly Vector2Int slotSize = new(96, 96);
10 |
11 | ///
12 | /// Slot scale for external changes. Do not touch.
13 | ///
14 | public static readonly float slotScale = 1f;
15 |
16 | ///
17 | /// Speed at which the item will return to its target.
18 | ///
19 | public static readonly float rotationAnimationSpeed = 30f;
20 | }
21 |
22 | public class Inventory : MonoBehaviour
23 | {
24 | ///
25 | /// List of data for each item in the game.
26 | ///
27 | [Header("Settings")]
28 | public ItemData[] itemsData;
29 |
30 | ///
31 | /// Prefab used to instantiate new items.
32 | ///
33 | public Item itemPrefab;
34 |
35 | ///
36 | /// Returns the InventoryGrid the mouse is currently on.
37 | ///
38 | public InventoryGrid gridOnMouse { get; set; }
39 |
40 | ///
41 | /// Dynamic list that has all inventory grids automatically when starting the game.
42 | ///
43 | public InventoryGrid[] grids { get; private set; }
44 |
45 | ///
46 | /// Currently selected item.
47 | ///
48 | public Item selectedItem { get; private set; }
49 |
50 | ///
51 | /// Awake is called when the script instance is being loaded.
52 | ///
53 | private void Awake()
54 | {
55 | grids = GameObject.FindObjectsOfType();
56 | }
57 |
58 | ///
59 | /// Selects the item and turns everything necessary to select it and make room for another item on and off
60 | ///
61 | /// Item to be selected.
62 | public void SelectItem(Item item)
63 | {
64 | ClearItemReferences(item);
65 | selectedItem = item;
66 | selectedItem.rectTransform.SetParent(transform);
67 | selectedItem.rectTransform.SetAsLastSibling();
68 | }
69 |
70 | ///
71 | /// Deselects the current item.
72 | ///
73 | private void DeselectItem()
74 | {
75 | selectedItem = null;
76 | }
77 |
78 | ///
79 | /// Add an item to the inventory dynamically by looking for where the item might fit.
80 | ///
81 | /// Data of the item that will be added to the inventory.
82 | public void AddItem(ItemData itemData)
83 | {
84 | for (int g = 0; g < grids.Length; g++)
85 | {
86 | for (int y = 0; y < grids[g].gridSize.y; y++)
87 | {
88 | for (int x = 0; x < grids[g].gridSize.x; x++)
89 | {
90 | Vector2Int slotPosition = new Vector2Int(x, y);
91 |
92 | for (int r = 0; r < 2; r++)
93 | {
94 | if (r == 0)
95 | {
96 | if (!ExistsItem(slotPosition, grids[g], itemData.size.width, itemData.size.height))
97 | {
98 | Item newItem = Instantiate(itemPrefab);
99 | newItem.rectTransform = newItem.GetComponent();
100 | newItem.rectTransform.SetParent(grids[g].rectTransform);
101 | newItem.rectTransform.sizeDelta = new Vector2(
102 | itemData.size.width * InventorySettings.slotSize.x,
103 | itemData.size.height * InventorySettings.slotSize.y
104 | );
105 |
106 | newItem.indexPosition = slotPosition;
107 | newItem.inventory = this;
108 |
109 | for (int xx = 0; xx < itemData.size.width; xx++)
110 | {
111 | for (int yy = 0; yy < itemData.size.height; yy++)
112 | {
113 | int slotX = slotPosition.x + xx;
114 | int slotY = slotPosition.y + yy;
115 |
116 | grids[g].items[slotX, slotY] = newItem;
117 | grids[g].items[slotX, slotY].data = itemData;
118 | }
119 | }
120 |
121 | newItem.rectTransform.localPosition = IndexToInventoryPosition(newItem);
122 | newItem.inventoryGrid = grids[g];
123 | return;
124 | }
125 | }
126 |
127 | if (r == 1)
128 | {
129 | if (!ExistsItem(slotPosition, grids[g], itemData.size.height, itemData.size.width))
130 | {
131 | Item newItem = Instantiate(itemPrefab);
132 | newItem.Rotate();
133 |
134 | newItem.rectTransform = newItem.GetComponent();
135 | newItem.rectTransform.SetParent(grids[g].rectTransform);
136 | newItem.rectTransform.sizeDelta = new Vector2(
137 | itemData.size.width * InventorySettings.slotSize.x,
138 | itemData.size.height * InventorySettings.slotSize.y
139 | );
140 |
141 | newItem.indexPosition = slotPosition;
142 | newItem.inventory = this;
143 |
144 | for (int xx = 0; xx < itemData.size.height; xx++)
145 | {
146 | for (int yy = 0; yy < itemData.size.width; yy++)
147 | {
148 | int slotX = slotPosition.x + xx;
149 | int slotY = slotPosition.y + yy;
150 |
151 | grids[g].items[slotX, slotY] = newItem;
152 | grids[g].items[slotX, slotY].data = itemData;
153 | }
154 | }
155 |
156 | newItem.rectTransform.localPosition = IndexToInventoryPosition(newItem);
157 | newItem.inventoryGrid = grids[g];
158 |
159 | return;
160 | }
161 | }
162 | }
163 | }
164 | }
165 | }
166 |
167 | Debug.Log("(Inventory) Not enough slots found to add the item!");
168 | }
169 |
170 | ///
171 | /// Remove item from inventory completely.
172 | ///
173 | /// Item to be removed.
174 | public void RemoveItem(Item item)
175 | {
176 | if (item != null)
177 | {
178 | ClearItemReferences(item);
179 | Destroy(item.gameObject);
180 | }
181 | }
182 |
183 | ///
184 | /// Moves an item to a new position, first checking if the item is outside the grid or if there is an item in the desired slot.
185 | ///
186 | /// Position that will be checked as the new position of the item in the inventory.
187 | /// Item that will be moved to the new position.
188 | /// Boolean that indicates whether the object will be deselected when it finishes moving.
189 | public void MoveItem(Item item, bool deselectItemInEnd = true)
190 | {
191 | Vector2Int slotPosition = GetSlotAtMouseCoords();
192 |
193 | if (ReachedBoundary(slotPosition, gridOnMouse, item.correctedSize.width, item.correctedSize.height))
194 | {
195 | Debug.Log("Bounds");
196 | return;
197 | }
198 |
199 | if (ExistsItem(slotPosition, gridOnMouse, item.correctedSize.width, item.correctedSize.height))
200 | {
201 | Debug.Log("Item");
202 | return;
203 | }
204 |
205 | item.indexPosition = slotPosition;
206 | item.rectTransform.SetParent(gridOnMouse.rectTransform);
207 |
208 | for (int x = 0; x < item.correctedSize.width; x++)
209 | {
210 | for (int y = 0; y < item.correctedSize.height; y++)
211 | {
212 | int slotX = item.indexPosition.x + x;
213 | int slotY = item.indexPosition.y + y;
214 |
215 | gridOnMouse.items[slotX, slotY] = item;
216 | }
217 | }
218 |
219 | item.rectTransform.localPosition = IndexToInventoryPosition(item);
220 | item.inventoryGrid = gridOnMouse;
221 |
222 | if (deselectItemInEnd)
223 | {
224 | DeselectItem();
225 | }
226 | }
227 |
228 | ///
229 | /// Swaps the selected item with the item overlaid by the mouse.
230 | ///
231 | /// Overlapping item.
232 | public void SwapItem(Item overlapItem, Item oldSelectedItem)
233 | {
234 | if (ReachedBoundary(overlapItem.indexPosition, gridOnMouse, oldSelectedItem.correctedSize.width, oldSelectedItem.correctedSize.height))
235 | {
236 | return;
237 | }
238 |
239 | ClearItemReferences(overlapItem);
240 |
241 | if (ExistsItem(overlapItem.indexPosition, gridOnMouse, oldSelectedItem.correctedSize.width, oldSelectedItem.correctedSize.height))
242 | {
243 | RevertItemReferences(overlapItem);
244 | return;
245 | }
246 |
247 | SelectItem(overlapItem);
248 | MoveItem(oldSelectedItem, false);
249 | }
250 |
251 | ///
252 | /// Clears the item position references in the inventory.
253 | ///
254 | /// Item to have references removed.
255 | public void ClearItemReferences(Item item)
256 | {
257 | for (int x = 0; x < item.correctedSize.width; x++)
258 | {
259 | for (int y = 0; y < item.correctedSize.height; y++)
260 | {
261 | int slotX = item.indexPosition.x + x;
262 | int slotY = item.indexPosition.y + y;
263 |
264 | item.inventoryGrid.items[slotX, slotY] = null;
265 | }
266 | }
267 | }
268 |
269 | ///
270 | /// Clears the item position references in the inventory.
271 | ///
272 | /// Item to have references removed.
273 | public void RevertItemReferences(Item item)
274 | {
275 | for (int x = 0; x < item.correctedSize.width; x++)
276 | {
277 | for (int y = 0; y < item.correctedSize.height; y++)
278 | {
279 | int slotX = item.indexPosition.x + x;
280 | int slotY = item.indexPosition.y + y;
281 |
282 | item.inventoryGrid.items[slotX, slotY] = item;
283 | }
284 | }
285 | }
286 |
287 | ///
288 | /// Checks if there is an item in the indicated position.
289 | ///
290 | /// Position to be checked.
291 | /// Item width
292 | /// Item height
293 | /// Grid in which the verification should occur.
294 | ///
295 | public bool ExistsItem(Vector2Int slotPosition, InventoryGrid grid, int width = 1, int height = 1)
296 | {
297 | if (ReachedBoundary(slotPosition, grid, width, height))
298 | {
299 | Debug.Log("Bounds2");
300 | return true;
301 | }
302 |
303 | for (int x = 0; x < width; x++)
304 | {
305 | for (int y = 0; y < height; y++)
306 | {
307 | int slotX = slotPosition.x + x;
308 | int slotY = slotPosition.y + y;
309 |
310 | if (grid.items[slotX, slotY] != null)
311 | {
312 | return true;
313 | }
314 | }
315 | }
316 |
317 | return false;
318 | }
319 |
320 | ///
321 | /// Checks whether the indicated position is outside the inventory limit.
322 | ///
323 | /// Position to be checked.
324 | /// Item width.
325 | /// Item height.
326 | /// Grid in which the verification should occur.
327 | ///
328 | public bool ReachedBoundary(Vector2Int slotPosition, InventoryGrid gridReference, int width = 1, int height = 1)
329 | {
330 | if (slotPosition.x + width > gridReference.gridSize.x || slotPosition.x < 0)
331 | {
332 | return true;
333 | }
334 |
335 | if (slotPosition.y + height > gridReference.gridSize.y || slotPosition.y < 0)
336 | {
337 | return true;
338 | }
339 |
340 | return false;
341 | }
342 |
343 | ///
344 | /// Returns the value of an array sequence at the inventory position.
345 | ///
346 | /// Item to be checked.
347 | ///
348 | public Vector3 IndexToInventoryPosition(Item item)
349 | {
350 | Vector3 inventorizedPosition =
351 | new()
352 | {
353 | x = item.indexPosition.x * InventorySettings.slotSize.x
354 | + InventorySettings.slotSize.x * item.correctedSize.width / 2,
355 | y = -(item.indexPosition.y * InventorySettings.slotSize.y
356 | + InventorySettings.slotSize.y * item.correctedSize.height / 2
357 | )
358 | };
359 |
360 | return inventorizedPosition;
361 | }
362 |
363 | ///
364 | /// Returns the screen of the matrix the mouse is on top of.
365 | ///
366 | ///
367 | public Vector2Int GetSlotAtMouseCoords()
368 | {
369 | if (gridOnMouse == null)
370 | {
371 | return Vector2Int.zero;
372 | }
373 |
374 | Vector2 gridPosition =
375 | new(
376 | Input.mousePosition.x - gridOnMouse.rectTransform.position.x,
377 | gridOnMouse.rectTransform.position.y - Input.mousePosition.y
378 | );
379 |
380 | Vector2Int slotPosition =
381 | new(
382 | (int)(gridPosition.x / (InventorySettings.slotSize.x * InventorySettings.slotScale)),
383 | (int)(gridPosition.y / (InventorySettings.slotSize.y * InventorySettings.slotScale))
384 | );
385 |
386 | return slotPosition;
387 | }
388 |
389 |
390 | ///
391 | /// Returns an item based on the mouse position.
392 | ///
393 | ///
394 | public Item GetItemAtMouseCoords()
395 | {
396 | Vector2Int slotPosition = GetSlotAtMouseCoords();
397 |
398 | if (!ReachedBoundary(slotPosition, gridOnMouse))
399 | {
400 | return GetItemFromSlotPosition(slotPosition);
401 | }
402 |
403 | return null;
404 | }
405 |
406 | ///
407 | /// Returns an item based on the slot position.
408 | ///
409 | /// Slot position to check.
410 | ///
411 | public Item GetItemFromSlotPosition(Vector2Int slotPosition)
412 | {
413 | return gridOnMouse.items[slotPosition.x, slotPosition.y];
414 | }
415 | }
416 |
--------------------------------------------------------------------------------
/Scripts/InventoryController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | [RequireComponent(typeof(Inventory))]
5 | public class InventoryController : MonoBehaviour
6 | {
7 | public Inventory inventory { get; private set; }
8 |
9 | ///
10 | /// Awake is called when the script instance is being loaded.
11 | ///
12 | private void Awake()
13 | {
14 | inventory = GetComponent();
15 | }
16 |
17 | ///
18 | /// Update is called every frame, if the MonoBehaviour is enabled.
19 | ///
20 | private void Update()
21 | {
22 | if (Input.GetKeyDown(KeyCode.Mouse0))
23 | {
24 | // Check if mouse is inside a any grid.
25 | if (!inventory.ReachedBoundary(inventory.GetSlotAtMouseCoords(), inventory.gridOnMouse))
26 | {
27 | if (inventory.selectedItem)
28 | {
29 | Item oldSelectedItem = inventory.selectedItem;
30 | Item overlapItem = inventory.GetItemAtMouseCoords();
31 |
32 | if (overlapItem != null)
33 | {
34 | inventory.SwapItem(overlapItem, oldSelectedItem);
35 | }
36 | else
37 | {
38 | inventory.MoveItem(oldSelectedItem);
39 | }
40 | }
41 | else
42 | {
43 | SelectItemWithMouse();
44 | }
45 | }
46 | }
47 |
48 | // Remove an item from the inventory
49 | if (Input.GetKeyDown(KeyCode.Mouse1))
50 | {
51 | RemoveItemWithMouse();
52 | }
53 |
54 | // Generates a random item in the inventory
55 | if (Input.GetKeyDown(KeyCode.Space))
56 | {
57 | inventory.AddItem(inventory.itemsData[UnityEngine.Random.Range(0, inventory.itemsData.Length)]);
58 | }
59 |
60 | if (inventory.selectedItem != null)
61 | {
62 | MoveSelectedItemToMouse();
63 |
64 | if (Input.GetKeyDown(KeyCode.R))
65 | {
66 | inventory.selectedItem.Rotate();
67 | }
68 | }
69 | }
70 |
71 | ///
72 | /// Select a new item in the inventory.
73 | ///
74 | private void SelectItemWithMouse()
75 | {
76 | Item item = inventory.GetItemAtMouseCoords();
77 |
78 | if (item != null)
79 | {
80 | inventory.SelectItem(item);
81 | }
82 | }
83 |
84 | ///
85 | /// Removes the item from the inventory that the mouse is hovering over.
86 | ///
87 | private void RemoveItemWithMouse()
88 | {
89 | Item item = inventory.GetItemAtMouseCoords();
90 |
91 | if (item != null)
92 | {
93 | inventory.RemoveItem(item);
94 | }
95 | }
96 |
97 | ///
98 | /// Moves the currently selected object to the mouse.
99 | ///
100 | private void MoveSelectedItemToMouse()
101 | {
102 | inventory.selectedItem.rectTransform.position = new Vector3(
103 | Input.mousePosition.x
104 | + ((inventory.selectedItem.correctedSize.width * InventorySettings.slotSize.x) / 2)
105 | - InventorySettings.slotSize.x / 2,
106 | Input.mousePosition.y
107 | - ((inventory.selectedItem.correctedSize.height * InventorySettings.slotSize.y) / 2)
108 | + InventorySettings.slotSize.y / 2,
109 | Input.mousePosition.z
110 | );
111 | }
112 | }
--------------------------------------------------------------------------------
/Scripts/InventoryGrid.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.EventSystems;
3 |
4 | [RequireComponent(typeof(RectTransform))]
5 | public class InventoryGrid : MonoBehaviour, IPointerEnterHandler
6 | {
7 | ///
8 | /// Size of slots that the grid will have 1X1
9 | ///
10 | [Header("Grid Config")]
11 | public Vector2Int gridSize = new(5, 5);
12 |
13 | ///
14 | /// Grid main rect transform reference.
15 | ///
16 | public RectTransform rectTransform;
17 |
18 | ///
19 | /// Item array.
20 | ///
21 | public Item[,] items { get; set; }
22 |
23 | ///
24 | /// Main inventory reference.
25 | ///
26 | public Inventory inventory { get; private set; }
27 |
28 | private void Awake()
29 | {
30 | if (rectTransform != null)
31 | {
32 | inventory = GameObject.FindObjectOfType();
33 | InitializeGrid();
34 | }
35 | else
36 | {
37 | Debug.LogError("(InventoryGrid) RectTransform not found!");
38 | }
39 | }
40 |
41 | ///
42 | /// Initialize matrices and grid size.
43 | ///
44 | private void InitializeGrid()
45 | {
46 | // Set items matrices
47 | items = new Item[gridSize.x, gridSize.y];
48 |
49 | // Set grid size
50 | Vector2 size =
51 | new(
52 | gridSize.x * InventorySettings.slotSize.x,
53 | gridSize.y * InventorySettings.slotSize.y
54 | );
55 | rectTransform.sizeDelta = size;
56 | }
57 |
58 | ///
59 | /// Add grid as main mouse grid.
60 | ///
61 | ///
62 | public void OnPointerEnter(PointerEventData eventData)
63 | {
64 | inventory.gridOnMouse = this;
65 | }
66 | }
--------------------------------------------------------------------------------
/Scripts/Item.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.UI;
3 |
4 | [RequireComponent(typeof(RectTransform))]
5 | public class Item : MonoBehaviour
6 | {
7 | ///
8 | /// Data of the item referenced in the Item script
9 | ///
10 | public ItemData data;
11 |
12 | ///
13 | /// Image element responsible for showing the item icon.
14 | ///
15 | public Image icon;
16 |
17 | ///
18 | /// Image element responsible for showing the item icon background.
19 | ///
20 | public Image background;
21 |
22 | ///
23 | /// Target rotation of the
24 | ///
25 | private Vector3 rotateTarget;
26 |
27 | ///
28 | /// Boolean that indicates whether the item was rotated.
29 | ///
30 | public bool isRotated;
31 |
32 | ///
33 | /// Rotation index, used to tell the item when to rotate at the right time.
34 | ///
35 | public int rotateIndex;
36 |
37 | ///
38 | /// The indexed position is the position of the item relative to the grid in which the item is located.
39 | ///
40 | public Vector2Int indexPosition { get; set; }
41 |
42 | ///
43 | /// Reference of the main inventory to which the script communicates.
44 | ///
45 | public Inventory inventory { get; set; }
46 |
47 | ///
48 | /// Reference of the RectTransform.
49 | ///
50 | public RectTransform rectTransform { get; set; }
51 |
52 | ///
53 | /// Grid the item is currently in.
54 | ///
55 | public InventoryGrid inventoryGrid { get; set; }
56 |
57 | ///
58 | /// Correct position using the rotation ratio.
59 | ///
60 | public SizeInt correctedSize
61 | {
62 | get
63 | { return new(!isRotated ? data.size.width : data.size.height, !isRotated ? data.size.height : data.size.width); }
64 | }
65 |
66 | ///
67 | /// Start is called on the frame when a script is enabled just before
68 | /// any of the Update methods is called the first time.
69 | ///
70 | private void Start()
71 | {
72 | icon.sprite = data.icon;
73 | background.color = data.backgroundColor;
74 | }
75 |
76 | ///
77 | /// LateUpdate is called every frame, if the Behaviour is enabled.
78 | /// It is called after all Update functions have been called.
79 | ///
80 | private void LateUpdate()
81 | {
82 | UpdateRotateAnimation();
83 | }
84 |
85 | ///
86 | /// Rotates the item to the correct position the player needs.
87 | ///
88 | public void Rotate()
89 | {
90 | if (rotateIndex < 3)
91 | {
92 | rotateIndex++;
93 | }
94 | else if (rotateIndex >= 3)
95 | {
96 | rotateIndex = 0;
97 | }
98 |
99 | UpdateRotation();
100 | }
101 |
102 | ///
103 | /// Reset the rotate index.
104 | ///
105 | public void ResetRotate()
106 | {
107 | rotateIndex = 0;
108 | UpdateRotation();
109 | }
110 |
111 | ///
112 | /// Update rotation movement.
113 | ///
114 | private void UpdateRotation()
115 | {
116 | switch (rotateIndex)
117 | {
118 | case 0:
119 | rotateTarget = new(0, 0, 0);
120 | isRotated = false;
121 | break;
122 |
123 | case 1:
124 | rotateTarget = new(0, 0, -90);
125 | isRotated = true;
126 | break;
127 |
128 | case 2:
129 | rotateTarget = new(0, 0, -180);
130 | isRotated = false;
131 | break;
132 |
133 | case 3:
134 | rotateTarget = new(0, 0, -270);
135 | isRotated = true;
136 | break;
137 | }
138 | }
139 |
140 | ///
141 | /// Updates the item rotation animation.
142 | ///
143 | private void UpdateRotateAnimation()
144 | {
145 | Quaternion targetRotation = Quaternion.Euler(rotateTarget);
146 |
147 | if (rectTransform.localRotation != targetRotation)
148 | {
149 | rectTransform.localRotation = Quaternion.Slerp(
150 | rectTransform.localRotation,
151 | targetRotation,
152 | InventorySettings.rotationAnimationSpeed * Time.deltaTime
153 | );
154 | }
155 | }
156 | }
--------------------------------------------------------------------------------
/Scripts/ItemData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 |
5 | [CreateAssetMenu(fileName = "ItemData", menuName = "Inventory/ItemData")]
6 | public class ItemData : ScriptableObject
7 | {
8 | ///
9 | /// Size in width and height of the item.
10 | ///
11 | [Header("Main")]
12 | public SizeInt size = new();
13 |
14 | ///
15 | /// Item icon.
16 | ///
17 | [Header("Visual")]
18 | public Sprite icon;
19 |
20 | ///
21 | /// Background color of the item icon.
22 | ///
23 | public Color backgroundColor;
24 | }
--------------------------------------------------------------------------------