Всем привет. Данный урок покажет как можно создать и использовать DataAsset для ваших нужд (например : инвентаря,какой то своей базы).Сам я программистом не являюсь,и когда возник вопрос о том как можно хранить нужные мне данные в виде базы ,средствами UE4,то наткнулся на DataAsset ,но поняв ,что нужно все-таки лезть в С++ то немного огорчился.Но на деле все оказалось довольно просто и понятно,и довольно быстро я разобрался,хотя нервишки все-таки пришлось потрепать ,полазив на "буржуйских" сайтах в поисках инфы. В этом уроке будет очень много картинок ,постараюсь все разжевать насколько это возможно,так как хотел что бы это рассказали мне ,по-этому шаг за шагом будет показано что сделано,что нажималось и что получалось. ПС:предполагается что читающий знаком с блупринтами,хотя бы в общих чертах ,потому что буду сравнивать и опираться именно на блупринты (переменные ,их тип и тд) . Так же у Вас должна быть установлена MSVisualStudio,я использовал "Microsoft Visual Studio 2013 Ultimate"
Давайте начнем :
Для хранения какой либо информации с разными данными нам понадобиться структура,для того что бы описать ,например предмет инвентаря или оружие В редакторе UE4,в ContentBrowser'е(1) мы можем создать ,например, такую структуру с именем MyStruct В нашей структуре MyStruct пусть будут несколько переменных разного типа (2): MyBool - типа Boolean; MyPicture - типа Texture2D; MyEnum - типа Enum (об этом позже); MyName - типа String; MyIntegerArray - массив типа Integer;
Наш MyEnum пусть будет ,например таким(1) :
Теперь нашу структуру MyStruct мы можем использовать в нашем блупринте. Создаем переменную типа MyStruct(1) Делаем нашу переменную массивом (2) Заполняем этот массив, добавляя какие-нибудь элементы(3) Потом ,получая доступ к нужному элементу ,используем интересующую нас информацию,например выводим на экран (4)
Конечно,таким способом можно заполнить/создать свою базу данных для своих нужд.Но редактировать ее из блупринта не очень удобно,к тому же есть возможность случайно удалить эту переменную ,или часть ее элементов потерять,кликнув не туда. Как замена данному варианту,можно использовать DataAsset. Создать DataAsset можно через правый клик мыши в ContentBrowser'е(1) ,перейти в пункт Miscellaneous(2) , а уже там нажать на Data Asset(3).
После этого появится небольшое окошко(1),в котором нам предложат только выбрать класс DataAsset'а(2) :
Как видите,по сути создать наш собственный DataAsset не получится,так как есть только возможность выбора определенного класса DataAsset'а
Собственно созданием этого самого класса DataAsset'а мы и займемся В ContentBrowser'е правым кликом мыши вызываем меню(1),в нем выбираем пункт "New C++ Class"(2)
У нас должно появится окно "Add C++ Class"(1),здесь ставим галочку напротив пункта "Show All Classes"(2)(это нужно для того ,что бы отобразился весь перечень классов,который мы можем использовать как родительский)
Теперь нам нужно найти интересующий нас класс ,а именно DataAsset,можно просто ввести его в строке поиска(1).Обязательно убедитесь ,что SelectedClass - DataAsset ,а Selected Class Source DataAsset.h(2).Если это так ,то нажимаем кнопку "Next"(3)
Здесь нажимаем на пункт "Public"(1). В поле "Name" задаем название нашего класса ,я назвал его "MyDataAsset"(2)(в поле "Path" ничего не меняем). Нажимаем кнопку "Create Class"(3)
Теперь мы увидим окошко "Adding code to project"(1),ждем когда добавится код в проект :
Далее последует компиляция кода(1),(2):
В этот момент вы конечно же ничего не делаете,а смиренно ждете,когда UE4 сделает всю необходимую работу
После компиляции запустится "Microsoft Visual Studio"(1). Автоматически откроется *.cpp файл с вашим названием класса,который вы указывали,я указал "MyDataAsset",соответственно у меня открылся "MyDataAsset.cpp"(2). Внизу(3) будет идти процесс "Parsing files in solution" ,желательно дождаться его окончания
Итак,процесс "Parsing files in solution" прошел(1). Переходим в наш *.h файл(как помним я называл свой класс "MyDataAsset" ,значит и *.h будет "MyDataAsset.h")(2). Здесь(3) уже будет код ,автоматически сгенерированный UE4 при создании класса.
Итак,на что стоит обратить внимание (хотя знать в нашем случае и не обязательно),собственно на тело класса ,который был создан:
Код:
UCLASS() class DATAASSETTUTORIAL_API UMyDataAsset : public UDataAsset { GENERATED_BODY()
};
UCLASS() - макрос,который нужен для создания класса в среде UE class DATAASSETTUTORIAL_API UMyDataAsset : public UDataAsset - тут идет объявление нашего класса ,не смущайтесь что написано "UMyDataAsset",это нужно для скрытия архитектуры от простого пользователя(или как то так :) ). public UDataAsset - говорит о том что мы наследуем или родительский класс будет UDataAsset. DATAASSETTUTORIAL_API - здесь идет название вашего проекта ,мой назывался DataAssetTutorial ,соответственно это и было добавлено генератором класса. В фигурных скобках "{" и "}" описывается тело класса. GENERATED_BODY() - это тоже макрос ,он должен идти в самом начале ,проще говоря сразу после открывающей фигурной скобки.
Опять же знать это не обязательно,как я говорил выше ,и для простых нужд можно обойтись простым копи-пастом кода.Главное быть внимательным
Давайте теперь попробуем откомпилировать наш код,который был создан автоматически,если руками ничего в коде не щупали и не добавляли,то и проблем возникнуть не должно.Один из вариантов откомпилировать код следующий : Возвращаемся в редактор UE4 ,на "Верхней панели"(1) нажимаем кнопку Compile(2).Это даст нам возможность ,не перезапуская редактор UE4 ,откомпилировать код ,и автоматически применить его изменения. (ПС:Иногда редактор крашится,вылетает в момент этой процедуры)
Теперь должна идти компиляция(1) :
После окончания компиляции вы увидите надпись "Compile Complete!"(1):
Другой вариант компилирования - компилирование из VisualStudio,но после этого желательно перезапустить редактор UE4 ,во избежании проблем: В VisualStudio переходим в Solution Explorer(1). Здесь в папке Games будет ваш проект (мой называется DataAssetTutorial)(2). Кликаем правой клавишей мыши по нему и нажимаем Build(3) Ждем окончания компиляции ,должна появится строчка на подобии этой ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========(4) - это нам скажет о том что компиляция прошла успешно.
Продолжаем в нашем редакторе UE4.Правый клик мыши в ContentBrowser'е(1) ,пункт Miscellaneous, там выбираем Data Asset(2).
Сейчас в появившемся окошке(1),мы должны видеть наш класс MyDataAsset(2).Выделяем его и нажимаем Select(3)
Теперь в ContentBrowser'е появился наш DataAsset(1)
Открываем двойным кликом наш DataAsset,и видим что это DataAsset нашего класса,но он пустой,что собственно и логично,так как мы ничего еще в него не добавили(1).
ПС:иногда у вновь созданных DataAsset отключено отображение пункта "Details",не забудьте его включить,что бы видеть изменения в дальнейшем .Для этого нажмите Windows(1) и поставьте галочку на против пункта Details(2) :
Возвращаемся в VisualStudio,убедитесь что вы находитесь в *.h файле(1). Код ваш без изменений должен быть(2).Теперь добавляем внутри скобок UCLASS слово BlueprintType(3) :
Код:
UCLASS(BlueprintType)
Это даст нам возможность получать доступ к нашему DataAsset из блупринтов
Теперь давайте попробуем "воссоздать" структуру которая была сделана в самом начале урока MyStruct,но сперва создадим enumeration MyEnum ,так как он является составляющей частью структуры MyStruct,для этого после строчек :
UENUM(BlueprintType) enum class EMyEnum : uint8 { Normal UMETA(DisplayName = "Normal"), Good UMETA(DisplayName = "Good"), Epic UMETA(DisplayName = "Epic") };
Весь код будет таким :
То что мы добавили(2) - это и есть наш MyEnum(1) воплощенный в коде :
Теперь поподробнее об этом коде :
Код:
UENUM(BlueprintType) enum class EMyEnum : uint8 { Normal UMETA(DisplayName = "Normal"), Good UMETA(DisplayName = "Good"), Epic UMETA(DisplayName = "Epic") };
UENUM(BlueprintType) - макрос необходимый при объявлении enumeration в среде UE4,BlueprintType - делает доступным этот enumeration в блупринтах enum class EMyEnum : uint8 - само объявление enumeration, EMyEnum - имя нашего enumeration ,uint8 - типа нашего enumeration,просто байт(то есть 256 значений мы можем использовать,имено этот типа нужно указывать если используется такой вариант описания enumeration) В фигурных скобка { и } мы перечисляем(задаем) значения полей MyEnum,перечесление происходит с завершающей запятой,кроме последнего элемента (который находится перед закрывающей фигурной скобкой }),в нашем случае это Epic UMETA(DisplayName = "Epic"). Что именно находится в полях которые мы хотим добавить,рассмотрим на примере Normal UMETA(DisplayName = "Normal") - здесь Normal - это значение ,а следующий за ним макрос UMETA(DisplayName = "Normal") ,говорит о том какое имя будет отображаться.То есть вы можете записать так MoeZnachenie UMETA(DisplayName = "MoeZnachenie") и так далее.Так же можно сделать часть пунктов скрытыми,для этого в скобках макроса UMETA написать hidden - UMETA(hidden). Как и в прошлый раз знать это не обязательно ,можно делать просто под копирку и менять нужное значение,например :
Главное не потеряйте фигурные скобочки и не забудьте ,что после последнего элемента не нужно ставить запятую,так же значений не должно быть больше 256
Откомпилируем то ,что у нас есть,например через UE4,для этого возвращаемся в него и так же как и в прошлый раз на "Верхней панели"(1) нажимаем кнопку Compile(2)
После компиляции,открыв любой блупринт ,создайте переменную,теперь ее тип можно поменять на созданный нами EMyEnum ,просто выберите его из списка (1),(2):
Идем дальше,возвращаемся в Visual Studio После нашего кода :
Код:
UENUM(BlueprintType) enum class EMyEnum : uint8 { Normal UMETA(DisplayName = "Normal"), Good UMETA(DisplayName = "Good"), Epic UMETA(DisplayName = "Epic") };
USTRUCT(BlueprintType) - макрос,так же как и в случае с нашим классом и нашим enumeration ,необходимый для объявления в среде UE4,BlueprintType - дает возможность использовать эту структуру в блупринтах struct FMyStruct - объявление структуры ,здесь FMyStruct - это имя нашей структуры(в редакторе будет отображаться просто как MyStruct как и в варианте с нашим классом,об этом говорилось выше) В фигурных скобках { и } - идет тело структуры GENERATED_USTRUCT_BODY() - макрос необходимый в среде UE4 при создании тела структуры,по аналоги с классом (где указывалось GENERATED_BODY() ) должен идти сразу же после открывающей скобки {. Теперь :переменные в полях которые можно редактировать из редактора,или блупринта объявляются в С++ после макроса UPROPERTY()(возможно неправильно выразился) .После этого макроса идет тип переменной и собственно сама переменная(например : bool MyBool - булевая переменная),после переменной должна ставится точка с запятой ;.Так же в макросе UPROPERTY() ,можно указать будет ли доступна данная переменная для редактирования,видна ли она будет из блупринтов,можно ли будет ее изменить в блупринте и считавать,в принципе по началу достаточно будет добавлять в UPROPERTY() - EditAnywhere и BlueprintReadWrite ,здесь EditAnywhere разрешает изменять переменную откуда угодно,а BlueprintReadWrite - разрешает записывать и читать переменную в блупринте(если нужно сделать доступной только для чтения ,то нужно написать BlueprintReadOnly) Продолжаем рассматривать нашу структуру :
Идет макрос UPROPERTY() для возможности использования в редакторе и блупринте ,с полями EditAnywhere, BlueprintReadWrite - то есть даем добро на изменение,а также чтение и запись через блупринт bool MyBool; - объявляем что наша переменная будет булевой
Аналогичный макрос ,макросу выше с теми же параметрами UTexture2D* MyPicture; - это ссылка на тип UTexture2D* ,в данном случае имеется ввиду "картинка" .Обратите внимание что после UTexture2D стоит * - это обязательно в данном случае.Так же это будет необходимо и при других вариантах использования данных которые вы хотите,в конце урока напишу список,того что скорее всего будет использоваться вами,
А вот и наш enumeration ,который мы описывали перед структурой.Здесь такой же макрос как и выше. EMyEnum MyEnum; - создаем переменную типа EMyEnum и даем ей имя MyEnum
Здесь макрос такой же как и выше . TArray<int32> MyIntegerArray; - тут мы объявляем массив типа int32 и даем ей имя MyIntegerArray .Массив объявляется через ключевое словоTArray ,а в следующих за ним < > указывается тип переменной из которых будет состоять массив.В нашем случае это int32 ,то есть целочисленное ,которое может принимать как положительное так и отрицательное значение. Если необходимо объявить переменную другого типа,то можно сделать по аналогии выше ,например :
Давайте опять откомпилируем то ,что у нас получилось,также через редактор UE4,для этого возвращаемся в него и так же как и в прошлый раз на "Верхней панели"(1) нажимаем кнопку Compile(2)
После компиляции,открыв любой блупринт ,создаем новую переменную,теперь ее тип можно поменять на созданную нами структуруMyStruct ,выберите ее из списка (1),(2):
"Откомпилировав" блупринт(1),мы увидим,что все наши элементы структуры присутствуют(2)
Итак,мы на финишной прямой,дело за малым.Возвращаемся в Visual Studio Здесь в теле нашего класса(1) после :
Код:
GENERATED_BODY()
вставляем (2) :
Код:
public : UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray<FMyStruct> ArrayOfMyStruct;
Весь код MyDataAsset будет следующим:
Давайте рассмотри то ,что мы добавили,хотя уже скорее всего все понятно:
Код:
public : UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray<FMyStruct> ArrayOfMyStruct;
public : - паблик с двоеточием ,после него идет объявление переменных доступных(видимых) из этого класса,проще сказать,что пИшете public : ,а после него указываете те переменные ,которые должны быть доступны для использования.(после public : не должна идти закрывающая фигурная скобка,если ничего не добавляете из переменных,то лучше public : убрать) В данном случае это у нас :
UPROPERTY(EditAnywhere, BlueprintReadWrite) - такой же макрос как и то что мы указывали в структуре ,с ключевыми словами EditAnywhere, BlueprintReadWrite ,что дает нам возможность редактировать данные где угодна и то что они доступны для чтения и записи из блупринта(пс:посоветовал бы вам для своего DataAsset ключ BlueprintReadWrite заменить на BlueprintReadOnly,чтобы случайно не изменить данные в ваше базе из блупринта) TArray<FMyStruct> ArrayOfMyStruct; - аналогично ,как мы объявляли массив int32 в структуре ,здесь мы объявляем массив ArrayOfMyStruct типа FMyStruct.То есть ,то к чему в итоге и шли.
Ну и настало время последней компиляции ,идем в редактор UE4,опять как и в прошлый раз на "Верхней панели"(1) нажимаем кнопку Compile(2)
Теперь давайте проверим ,произошло ,что то или нет.Открываем наш DataAssetCompile(1) двойным кликом мыши по нему в ContentBrowser'e,и видим,что здесь теперь появился массив,в который мы можем добавить данныеCompile(2):
Давайте что нибудь добавим,в общем заполним нашу базу данных какой нибудь информацией(1),(2),(3) и сохраним через Save:
Переходим в какой-нибудь блупринт,создаем новую переменную(1),меняем ее типа на MyDataAsset(объект,а не класс берем)(2) :
В поле нашей переменной,в DefaultValue напротив нашего названия переменной MyDataAsset нажимаем на выпадающий список(1).И выбираем нашу "базу данных" ,наш DataAsset(2),в которую мы недавно добавляли 3 элемента.
Теперь перетащим из списка нашу переменную(1) в EventGraph через get(2).Теперь потянем за "пин",из выпадающего списка в поиске напишем get и выберем GetArrayOfMyStruct(3),таким образом мы получили доступ к массиву данных нашего DataAsset:
Ну и с полученным массивом мы уже можем работать стандартными средствами блупринта (1):
Давайте попробуем вывести на экран значение MyName первого элемента массива (1):
Запускаем игру из редактора(1),и теперь по каждому нажатию кнопки "E" мы видим как появляется текст(2):
Проверяем действительно ли это так,открываем наш DataAsset(1),и действительно в 0 элементе в строке MyName указано именно ,то значение которое мы видели на экране(2) :
Собственно на этом урок закончен.
ПС:То что может вам понадобиться,как и обещал,для переменных разного типа:
Спасибо. Очень доступно. Я правильно понимаю, что использование DataAsset по сравнению со структурой (рассмотренной в начале) лучше только в плане "случайно не удалить" или есть какие то преимущества?
_________________ Мои работы. Youtube Channel Форум по геймдеву.
Спасибо. Очень доступно. Я правильно понимаю, что использование DataAsset по сравнению со структурой (рассмотренной в начале) лучше только в плане "случайно не удалить" или есть какие то преимущества?
Ну вообще это простой пример,а так можно добавить функции поиска,сортировки и тд ,что бы получить более удобный доступ к информации.Что то скрыть от глаз пользователя и тд.Ну и вообще удобнее хранить в отдельном независимом файле.
Какая замечательная интернет страница получилась, скачал с большой радостью.
Рад это слышать :) .Была идея еще написать про функции для использования в блупринтах с IN и OUT значениями ,но пока не уверен нужно ли это,в принципе материала там очень мало получится.
Кстати вопрос. А почему нельзя использовать DataTable, который уже встроен в уе? Или его нельзя редактировать в рантайме?
P.S. статью выложу на главную чуть позже, сейчас мало времени(
_________________ Просьба, не писать вопросы по движку в ЛС. Я не единственный, кто знает UE4, и поэтому пишите на форум или в группу.
Кстати вопрос. А почему нельзя использовать DataTable, который уже встроен в уе? Или его нельзя редактировать в рантайме?
P.S. статью выложу на главную чуть позже, сейчас мало времени(
DataTable глубоко не проникался,немножко почитал,но в ней насколько я понял просто таблица с данными так сказать :голые цифры или текст.Видел что можно применять как вариант для локализации на нескольких языках.Ну и примеры с уровнями экспы . В реалтайме можешь спокойно редактировать ее,тут проблем нет + для наглядности простой инфы удобнее.Но вот например вместо наглядной миниатюры модельки или текстуры у тебя будет в DataTable путь к ней.
Какая замечательная интернет страница получилась, скачал с большой радостью.
Рад это слышать :) .Была идея еще написать про функции для использования в блупринтах с IN и OUT значениями ,но пока не уверен нужно ли это,в принципе материала там очень мало получится.
Вообще-то очень нужно. И впредь прошу не задумываться о таких вещах, а сразу же оформлять в хоть и не большую, но очень полезную статью. Спасибо еще раз за труды для народа.
_________________ Мои работы. Youtube Channel Форум по геймдеву.
Продолжение, про функции для использования в блупринтах с IN и OUT значениями, хотелось бы увидеть)
Там по сути рассказывать нечего ,если в функции для булпринта перед переменной стоит const - то это будет IN ,если не стоит ,то OUT . Еще так же булпринтпюре , там только выход. Если есть вопросы и конкретные затруднения или вопросы ,то можешь в личку задать в ВК или тему тут создать. Я просто сейчас не представляю как нормально дополнить урок ,материала мало для IN и OUT что бы рассказывать :)