Создание простого Drag&Drop инвентаря в Unity на C#. Часть 1
Всем привет. Представляю вашему вниманию небольшой урок посвященный созданию Drag&Drop инвентаря. Через текст тяжело все передать и обьяснить, с видео все намного проще. Но я все таки попытаюсь написать все, как можно подробней. Вот небольшое видео, где показано, как работает инвентарь.
На написание этой статьи меня подвигло то, что очень часто создать инвентарь для начинающих программистов является доволи сложным делом(для меня именно так и было). И вот я решил попробовать сделать инвентарь как можно коротким в плане кода. Конечно, что бы создать полноценный инвентарь понадобится много времени, но прочитав данный урок, я надеюсь, вы все поймете и у вас будет от чего «отталкиваться» при создании своего инвентаря. В этой части урока мы создадим простой инвентарь с возможность перемещения предметов внутри инвентаря. Способ, описанный в этом уроке, является всего лишь одним из десятков способов создания инвентаря. Так что поехали… Для начала создадим и настроем сцену. Разместим на ней Plane (создадим некую поверхность) и Directional Light.
Настроим камеру, что бы сцену было видно.
Приступаем к скриптингу. Создаем новый скрипт с именем Item, где будем хранить все характеристики предмета. Их у нас будет целых две :): название предмета и его иконка. Думаю для начала этого хватит, вы по желанию можете любые переменные и значения добавлять. Приступим к редактированию скрипта. Во-первых наш класс ничего не наследует поэтому удаляем MonoBehaviour.Создаем две переменные типа Texture2D и string, которые будут содержать иконку и название предмета. Наш скрипт выглядит так:
Код
using UnityEngine; using System.Collections;
[System.Serializable] public class Item{
public Texture2D Textura; //текстура иконки public string Name; //название }
Обязательно добавляем [System.Serializable] Создаем еще один скрипт и называем его ItemData. В нем будем хранить предметы, а так же создавать их для инвентаря. Первым делом подключаем пространство имен System.Collections.Generic, что бы можно было использовать списки. Код:
Код
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class ItemData : MonoBehaviour {
public static ItemData _ItemData; //паттерн Singelton public List<Item> Items = new List<Item>(); //списк в котором хранятся предметы
void Awake() { _ItemData = this; }
void Start () {
}
void Update () {
} }
Создадим публичный метод который возвращает Item и принимает int. И присвоим переменным значения
Код
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class ItemData : MonoBehaviour {
public static ItemData _ItemData; //паттерн Singelton public List<Item> Items = new List<Item>(); //списк в котором хранятся предметы
Вешаем наш скрипт на камеру (в принципе не важно куда его вешать) В инспекторе создаем 4 предмета. Называем их и задаем иконки и названия.
Теперь перейдем непосредственно к созданию инвентаря. Создаем скрипт и называем его Inventory. Создаем переменные отвечающие за размер ячеек, окна инвентаря и кол-во ячеек. У нас будет 6 колонок по 4 ячейки, итого 24 ячейки.
Код
using UnityEngine; using System.Collections;
public class Inventory : MonoBehaviour {
const int INVENTORY_WINDOW_ID = 1; //id окна инвентаря
public float ButtonWidth = 40; //высота ячейки public float ButtonHeight = 40; //ширина ячейки
int invRows = 6; //количество колонок int invColumns = 4; //количество столбцов Rect inventoryWindowRect = new Rect(10, 10, 170, 265); //область окна
void Start () {
}
void Update () {
} }
Добавляем еще несколько вспомогательных переменных для возможности передвигать предметы и создаем словарь содержащий наши предметы и принимающий в качестве ключа int. (не забываем добавить System.Collections.Generic для работы со словарями).
Код
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class Inventory : MonoBehaviour {
const int INVENTORY_WINDOW_ID = 1; //id окна инвентаря
public float ButtonWidth = 40; //высота ячейки public float ButtonHeight = 40; //ширина ячейки
int invRows = 6; //количество колонок int invColumns = 4; //количество столбцов Rect inventoryWindowRect = new Rect(10, 10, 230, 265); //область окна bool isDraggable; //возможно ли перемещение предмета Item selectItem; //вспомогательная переменная куда заносим предмет инвентаря Texture2D dragTexture; //текстура которая отображается при перетягивании предмета в инвентаре
Далее создаем окно и ячейки. Ячейки будем создавать через циклы. -Первый цикл необходим для дальнейшей проверки ключа в словаре. -Второй для создания ячеек по вертикале (как вы помните у нас 6 колонок) -Третий для создание ячеек по горизонтали (у нас 4 столбца) Нам необходимо пронумеровать каждую ячейку , поэтому используя теорему «По Качану» я вывел формулы для нумерования ячеек (x + y * invColumns). Собственно сам код:
Код
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class Inventory : MonoBehaviour {
const int INVENTORY_WINDOW_ID = 1; //id окна инвентаря
public float ButtonWidth = 40; //высота ячейки public float ButtonHeight = 40; //ширина ячейки
int invRows = 6; //количество колонок int invColumns = 4; //количество столбцов Rect inventoryWindowRect = new Rect(10, 10, 230, 265); //область окна bool isDraggable; //перемещается ли айтем? Item selectItem; //вспомогательная переменная куда заносим предмет инвентаря Texture2D dragTexture; //текстура которая отображается при перетягивании предмета в инвентаре
void firstInventory(int id) { for (int y = 0; y < invRows; y++) { for (int x = 0; x < invColumns; x++) { GUI.Button(new Rect(50 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), (x + y * invColumns).ToString(),"itemInfo-InfoBG"); } } } }
Вешаем наш скрипт на камеру и запускаем игру. Вы должны увидеть нечто подобное.
Продолжаем создавать наш инвентарь . Теперь мы создадим перемещение предметов внутри инвентаря и добавим парочку предметов для теста. Вот сам код. Думаю обьяснять ничего не надо, все закомментировано.
Код
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class Inventory : MonoBehaviour {
const int INVENTORY_WINDOW_ID = 1; //id окна инвентаря
public float ButtonWidth = 40; //высота ячейки public float ButtonHeight = 40; //ширина ячейки
int invRows = 6; //количество колонок int invColumns = 4; //количество столбцов Rect inventoryWindowRect = new Rect(10, 10, 170, 265); //область окна bool isDraggable; //перемещение предмета Item selectItem; //вспомогательная переменная куда заносим предмет инвентаря Texture2D dragTexture; //текстура которая отображается при перетягивании предмета в инвентаре
void firstInventory(int id) { for (int i = 0; i < 24; i++) { for (int y = 0; y < invRows; y++) { for (int x = 0; x < invColumns; x++) { if (InventoryPlayer.ContainsKey(x + y * invColumns))//проверяем содеоржится ли ключ с данным значением { if (GUI.Button(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), new GUIContent(InventoryPlayer[x + y * invColumns].Textura), "button")) { if (!isDraggable) { dragTexture = InventoryPlayer[x + y * invColumns].Textura;//присваиваем нашой текстуре которая должна отображаться при перетаскивании, текстуру предмета isDraggable = true;//возможность перемещать предмет selectItem = InventoryPlayer[x + y * invColumns];//присваиваем вспомогательной переменной наш предмет InventoryPlayer.Remove(x + y * invColumns);//удаляем из словаря предмет } } } else { if (isDraggable) { if (GUI.Button(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), "", "button")) { InventoryPlayer.Add(x + y * invColumns, selectItem);//добавляем предмет который перетаскиваем в словарь //обнуляем переменные isDraggable = false; selectItem = null; } } else { GUI.Label(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), "", "button"); } } } } }
} }
Опять запускаем игру и видим приблизительно вот такое:
Пробуем перетаскивать предметы в инвентаре. При нажатии на предмет он должен исчезать, после при нажатии на пустую ячейку он появляется в нужной ячейке. Нас это не устраивает и нам нужно, что бы было видно иконку при переносе предмета. Поэтому создадим новое окно где будет отображаться иконка, когда мы перетаскиваем предмет.
Код
using UnityEngine; using System.Collections; using System.Collections.Generic;
public class Inventory : MonoBehaviour {
const int INVENTORY_WINDOW_ID = 0; //id окна инвентаря const int INVENTORY_TEXTURE_ID = 1; //id окна с иконкой public float ButtonWidth = 40; //высота ячейки public float ButtonHeight = 40; //ширина ячейки
int invRows = 6; //количество колонок int invColumns = 4; //количество столбцов Rect inventoryWindowRect = new Rect(10, 10, 170, 265); //область окна Rect inventoryBoxRect = new Rect(); //область окна с изображением иконки bool isDraggable; //перемещение предмета Item selectItem; //вспомогательная переменная куда заносим предмет инвентаря Texture2D dragTexture; //текстура которая отображается при перетягивании предмета в инвентаре
//окно с изображением иконки void insert(int id) { GUI.BringWindowToFront(INVENTORY_TEXTURE_ID);//выводим на передний план окно с иконкой GUI.DrawTexture(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 40, 40), dragTexture);//рисуем текстуру иконки }
//окно с инвентарем void firstInventory(int id) { for (int y = 0; y < invRows; y++) { for (int x = 0; x < invColumns; x++) { if (InventoryPlayer.ContainsKey(x + y * invColumns))//проверяем содеоржится ли ключ с данным значением { if (GUI.Button(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), new GUIContent(InventoryPlayer[x + y * invColumns].Textura), "button")) { if (!isDraggable) { dragTexture = InventoryPlayer[x + y * invColumns].Textura;//присваиваем нашой текстуре которая должна отображаться при перетаскивании, текстуру предмета isDraggable = true;//возможность перемещать предмет selectItem = InventoryPlayer[x + y * invColumns];//присваиваем вспомогательной переменной наш предмет InventoryPlayer.Remove(x + y * invColumns);//удаляем из словаря предмет } } } else { if (isDraggable) { if (GUI.Button(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), "", "button")) { InventoryPlayer.Add(x + y * invColumns, selectItem);//добавляем предмет который перетаскиваем в словарь //обнуляем переменные isDraggable = false; selectItem = null; } } else { //делаем ячейки не выделяемыми GUI.Label(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), "", "button"); } } } } GUI.DragWindow(); } }
Запускаем и пробуем перемещать предметы внутри инвентаря. Все должно работать. На этом буду заканчивать первую часть урока. Вывод: В этом уроке мы создали прототип инвентаря с возможность Drag&Drop. Правда полноценным инвентарем это тяжело назвать это скорее каркас для инвентаря, при чем очень простой, который уместился в 100 строк кода. Почему я использовал именно словарь. Потому, что я очень люблю работать с ними ) Ну еще очень удобно считывать значения. Есть конечно недостатки у словарей: главная из которых НЕ возможность сериализовать словарь стандартными методами, но это легко можно обойти. Существует так же мнение, что словари работают «медленней», чем их аналоги. В принципе можно использовать вместо словаря список, хэш-таблицу или динамический массив Но как я уже говорил выше со словарями удобно работать. Мы создавали инвентарь с помощью стандартного UnityGUI, что конечно сказалось на оптимизации (37 Draw Calls). На NGUI и подобных ассетах инвентарь создается еще проще. Лично я люблю боль и поэтому пользуюсь родным GUI .Ну а если серьезно, я использую собственное решение. Заключение: В последующих уроках мы создадим всплывающие подсказки с описанием предметов, окно экипировки и разделение предметов по типу(оружие, броня и т.д), НПС с магазином, подбор предметов с земли и сундуков, конечно если вам будет это интересно. Сам исходник Клац
Категория: Создание игр | Добавил: beril (30 Апреля 2014)
| Автор: Шевченко Денис
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:
Игровые объявления и предложения:
Если вас заинтересовал материал «Создание простого Drag&Drop инвентаря в Unity на C#. Часть 1», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела.
Предлагаются такие схожие материалы:
Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.
1) Добавление эл-тов в массив через цикл(исключении ошибки выхода за пределы индекса массива, если в ItemData добавить меньше 4-х предметов(или не прописывать для большого кол-ва предметов Add)) 2)Автоматический размер окна инвентаря.
Вопрос автору. Я позаимствовал код и попытался чуть-чуть подогнать под себя. Так вот, мне нужно, чтобы при переносе итема в некоторую область, он появлялся там уже как префаб. Я объявил переменную вида Transform в item.cs. Но метод instantiate, который я вызываю из другого скрипта не срабатывает, говорит на месте префаба нет. Вот...
Код
if (InventoryPlayer.ContainsKey(x + y * invColumns))//проверяем содеоржится ли ключ с данным значением { if (GUI.Button(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), new GUIContent(InventoryPlayer[x + y * invColumns].Textura), "button")) { if (!isDraggable) { dragTexture = InventoryPlayer[x + y * invColumns].Textura;//присваиваем нашой текстуре которая должна отображаться при перетаскивании, текстуру предмета isDraggable = true;//возможность перемещать предмет selectItem = InventoryPlayer[x + y * invColumns];//присваиваем вспомогательной переменной наш предмет InventoryPlayer.Remove(x + y * invColumns);//удаляем из словаря предмет } } } else { if (isDraggable) { if (GUI.Button(new Rect(5 + (x * ButtonHeight), 20 + (y * ButtonHeight), ButtonWidth, ButtonHeight), "", "button")) { InventoryPlayer.Add(x + y * invColumns, selectItem);//добавляем предмет который перетаскиваем в словарь //обнуляем переменные isDraggable = false; selectItem = null; } else { if(Input.GetMouseButtonDown(0) && IsInRect(Input.mousePosition,Workspace)) {
Автору +, только нашол одну непонятную вещь: зачем делать 3 цикла когда 2 заглаза хватает, на сколько я понимаю первй цикл отвечает за прозрачность,наверно есть мение ресурсоёмкие способы,полностью не стал его убирать,снизил с 24 до 3,чуть-чуть просвечивает но зато фпс вместо 65 стал 75.
Да ты прав, в последнем коде 3 цикла, хотя в предыдущих вставках кода их 2. Просто изначально, было 3 цикла(я по ощибке 3 сделал), но я тоже заметил сильный упадок производительности, и 1 удалил, который случайно туда попал. Видимо не везде отредактировал
Не понимаю я этих Drag&Drop инвентарей. 90% уроков по созданию инвентаря начинается с настройки как раз графической части. И обычно дальше этого не заходит, следующих уроков и не дождешься. А мне важно было создать инвентарь списком (что удобнее, как по мне. Гораздо рациональнее, больше информации. Кому нужны иконки? Казуальщина, но это так, мысли в слух) Гдеж ты был раньше... Замечательный урок. Go on, не останавливайся (за транслит тоже наругал бы, но это совсем мелочь. Просто textura выглядит ооочень коряво, а всего-то нужно одну букву заменить)