Создание инвентаря персонажа в Unity

Сделать инвентарь не то чтобы сложно, но есть некоторые трудности, которые мы сегодня будем решать. Конечная цель – возможность перетаскивать объекты в ячейки инвентаря, и перетаскивать их из инвентаря обратно в игровой мир. Общая логика такова. В игре есть предметы, которые можно положить, например, в рюкзак. При клике по предмету, его 3D модель – меняется на соответствующую 2D иконку, которую мы можем перемещать на экране. Иконку можно положить в ячейку рюкзака, можно переназначать ячейки, то есть перекладывать иконку с места на место. Далее, любую иконку из инвентаря можно вытащить и переместить в 3D пространство, где она будет заменена на соответствующую 3D модель. Вот, как-то так..

Примечание: есть разные варианты реализации инвентаря, с использованием загрузки из папки Resources например. Здесь несколько иной способ, с использованием id объектов. Плохо это или хорошо, решайте сами. В любом случаи это лишь пример, который можно улучшить или взять на вооружение отдельные его части.


Подготовка:
Во-первых, нарисуйте ячейку рюкзака, и именуем иконку как background. Создаем новый UI объект типа Canvas не меняя настройки по умолчанию, а в него добавляем объект типа Image, в качестве источника изображения - делаем иконку ячейки. Настраиваем размер. Теперь, дублируем эту картинку, переименуем в item, затем, делаем дубликат - дочерним оригиналу. Переходим к настройке дочернего объекта. Цепляем на него небольшой скрипт Grid:

using UnityEngine;
using System.Collections;

public class Grid : MonoBehaviour {

	public int ID;
	public int item_id;
	public int icon_id;
	public bool active;

	public void SetID()
	{
		Inventory.grid_id = ID;
	}

	public void ResetID()
	{
		Inventory.grid_id = -1;
	}
}

Он хранит информацию о номере ячейки, а так же о id массивов предметов и иконок, плюс, текущая активность. Дополнительно две функции, которые будет запускать обработчик событий, чтобы передавать информацию другому скрипту.

Продолжим. На дочерний объект добавим обработчик событий Add Component > Event > Event Trigger. В обработчик мы добавим два события PointerEnter и PointerExit. Обращаться будем к текущему скрипту Grid на этой картинке. Для PointerEnter - функция SetID, для PointerExit - функция ResetID. Как показано на скриншоте ниже:

Создание инвентаря персонажа в Unity

Настройка ячейки готова. Теперь клонируем родителя и укладываем объекты, делаем рюкзак. Делаем пустой UI, назем его Inventory и перемещаем туда все ячейки:


Внимание! В Inventory не должно быть вложено никаких других объектов, кроме ячеек.

На Inventory вешаем скрипт с таким же названием:

using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections;

public class Inventory : MonoBehaviour {

	public Transform[] allItems;
	public Sprite[] itemIcon;
	public int iconSize = 50;
	public Sprite background;
	private GameObject[] cell;
	private Texture2D tmpTexture;
	private int iconCoumt;
	private int itemCoumt;
	private GameObject tmpObject;
	private GameObject cell_tmp;
	private Vector3 curPos;
	public static int grid_id;

	void Awake ()
	{
		grid_id = -1;
		cell = new GameObject[transform.childCount];
		for(int j = 0; j < cell.Length; j++)
		{
			cell[j] = transform.GetChild(j).gameObject;
			cell[j].GetComponentInChildren<Grid>().ID = j;
		}
	}

	void Reset()
	{
		if(tmpTexture)
		{
			tmpTexture = null;
			if(grid_id == -1 && !tmpObject)
			{
				// создание объекта с добавлением высоты по Y
				GameObject clone = Instantiate(allItems[itemCoumt].gameObject, curPos + new Vector3(0,100,0), Quaternion.identity) as GameObject;
				RaycastHit hit;
				if (Physics.Linecast (curPos, clone.transform.position, out hit)) 
				{
					// корректировка высоты на поверхности, с учетом размера коллайдера
					clone.transform.position -= new Vector3(0,hit.distance,0);
				}
			}
		}
		if(tmpObject)
		{
			tmpObject.SetActive(true);
			tmpObject = null;
		}
		cell_tmp = null;
	}

	void Update () 
	{
		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		if (Physics.Raycast(ray, out hit))
		{
			curPos = hit.point;
			if(Input.GetMouseButton(0))
			{
				if(hit.transform.GetComponent<AddToInventory>() && !tmpTexture)
				{
					tmpObject = hit.transform.gameObject;
					tmpObject.SetActive(false);
					int i = hit.transform.GetComponent<AddToInventory>().itemID;
					for(int j = 0; j < itemIcon.Length; j++)
					{
						// поиск id массива иконок, относительно номера объекта
						string n = itemIcon[j].name.Split(new char[] {'_'}, StringSplitOptions.RemoveEmptyEntries)[0];
						if(n == i.ToString())
						{
							tmpTexture = itemIcon[j].texture;
							iconCoumt = j;
							for(int f = 0; f < allItems.Length; f++)
							{
								// поиск id массива предметов, относительно номера объекта
								n = allItems[f].name.Split(new char[] {'_'}, StringSplitOptions.RemoveEmptyEntries)[0];
								if(n == i.ToString()) itemCoumt = f;
							}
						}
					}
				}
			}
			else
			{
				if(!Input.GetMouseButton(0) && grid_id == -1) Reset();
			}
		}
		else
		{
			if(!Input.GetMouseButton(0) && grid_id == -1) Reset();
		}

		if(Input.GetMouseButtonUp(0) && grid_id > -1 && tmpTexture)
		{
			foreach(GameObject obj in cell)
			{
				GameObject tmp = obj.transform.GetChild(0).gameObject;

				// меняем местами иконки, внутри рюкзака
				if(tmp.GetComponent<Grid>().ID == grid_id && tmp.GetComponent<Grid>().active && cell_tmp) 
				{
					cell_tmp.GetComponent<Grid>().icon_id = tmp.GetComponent<Grid>().icon_id;
					cell_tmp.GetComponent<Grid>().item_id = tmp.GetComponent<Grid>().item_id;
					cell_tmp.GetComponent<Grid>().active = true;
					cell_tmp.GetComponent<Image>().sprite = itemIcon[tmp.GetComponent<Grid>().icon_id];

					tmp.GetComponent<Grid>().item_id = itemCoumt;
					tmp.GetComponent<Grid>().icon_id = iconCoumt;
					tmp.GetComponent<Grid>().active = true;
					tmp.GetComponent<Image>().sprite = itemIcon[iconCoumt];

					Reset();
				}

				// добавление новой иконки в инвентарь
				if(tmp.GetComponent<Grid>().ID == grid_id && !tmp.GetComponent<Grid>().active) 
				{
					tmp.GetComponent<Grid>().item_id = itemCoumt;
					tmp.GetComponent<Grid>().icon_id = iconCoumt;
					tmp.GetComponent<Grid>().active = true;
					tmp.GetComponent<Image>().sprite = itemIcon[iconCoumt];
					Destroy(tmpObject);
				}
			}
			Reset();
		}

		if(Input.GetMouseButtonDown(0) && grid_id > -1 && !tmpTexture)
		{
			foreach(GameObject obj in cell)
			{
				GameObject tmp = obj.transform.GetChild(0).gameObject;

				// выбор иконки из рюкзака
				if(tmp.GetComponent<Grid>().ID == grid_id && tmp.GetComponent<Grid>().active) 
				{
					cell_tmp = tmp;
					iconCoumt = tmp.GetComponent<Grid>().icon_id;
					itemCoumt = tmp.GetComponent<Grid>().item_id;
					tmp.GetComponent<Grid>().active = false;
					tmpTexture = tmp.GetComponent<Image>().sprite.texture;
					tmp.GetComponent<Image>().sprite = background;
				}
			}
		}
	}

	void OnGUI () 
	{
		if(tmpTexture)
		{
			// перемещение иконки на экране
			Vector2 mousePos = Event.current.mousePosition;
			GUI.depth = 999; // поверх остальных элементов
			float shift = iconSize / 2;
			GUI.Label(new Rect(mousePos.x - shift, mousePos.y - shift, iconSize, iconSize), tmpTexture);
		}
	}
}

allItems - массив префабов предметов.

itemIcon - массив спрайтов иконок для предметов.

iconSize - размер иконки во время перемещения по экрану.

background - спрайт ячейки.

Как надо собирать префаб предмета. Во-первых, любой предмет который можно положить в инвентарь должен иметь скрипт AddToInventory:

using UnityEngine;
using System.Collections;

public class AddToInventory : MonoBehaviour {

	public int itemID;
}

itemID - номер для иконки и имени этого префеба.

То есть имя должно иметь вид id_XXX, например, 57_Book, здесь 57 это номер itemID. Разделительный знак "_". При создании иконки для данного предмета, ее имя должно иметь такой же вид, например, 57_BookIcon. Добавлять префабы предметов и спрайты иконок, в массивы, можно в любом порядке, потому что их фильтрация будет делаться по имени.

Скачать и пощупать готовый проект:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.

Улучшенная версия https://null-code.ru/project/180-prodvinutyy-inventar-personazha.html

Комментариев 5

Офлайн
Andrejus 9 июля 2015
Большое спасибо за ваши уроки. Все они очень полезны.
Офлайн
Luiza 21 февраля 2017
можете...пожалуйста помочь!!!!!!! как изменить код или что надо сделать, чтобы предмет появлялся именно там где кликнули мышкой, просто когда создается обычный Object , типа куб или сфера, то все работает как на видео, но как вешаешь скрипт на другой предмет из вне, то он закидывается в инвентарий, но когда вытаскиваешь он принимает начальные позиции, и вообще исчезает,почему?и что сделать?
коллайдер который вешается на модель принимает естественно значения и размеры модели...возможно в загвостка...?потому что у создаваемых кубов там значения все по нулям не зависимо как меняешь размер
Офлайн
Light 21 февраля 2017
Luiza, без разницы какой объект, если на нем есть скрипт соответствующий и коллайдер. Плюс, нужно проверить, чтобы все элементы массивов префабов и иконок были определены.
Офлайн
vovaustimuk 12 июня 2018
А вот если есть массив имен 57_Book, 58_Book, 59_Book как удалить элемент с конкретным именем 58_Book к примеру, или изменить его.
Офлайн
Light 12 июня 2018
vovaustimuk, в улучшенной версии эта функция есть https://null-code.ru/project/180-prodvinutyy-inventar-personazha.html
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика