Войти
ПрограммированиеСтатьиОбщее

MMO на Unreal Engine #2

Внимание! Этот документ ещё не опубликован.

Автор:

Добрый утро!

За окном какая то хрень, больше похоже на Осень, чем настрой под лето. Так что Вам повезло, что-то делать на улице мне совсем не хочется и я сажусь за написание продолжения серии статей.
Статьи будут выходить раз в неделю - раз в 10 дней (пока я в отпуске, немного чаще, он вот вот скоро закончится). Поэтому я буду напоминать о том, на чём мы остановились.
Я буду исходить из того что о движке вы не знаете ровным счетом ничего, так что те кто уже матерый "уе'шник", может пропускать абзацы.

Пару слов перед тем как мы начнём. Как раз вчера мне показали презентацию UrhoSharp, вот знаете...
Любой продукт, игра, или инструмент должен быть как волшеееебная шкатулка. Открывая которую, ты превращаешься в волшебника. Вот именно с этой точки зрения я всегда смотрел на новые движки, да и вообще, если бы не было "магии", я бы не стал программистом. Является ли Unreal Engine той самой волшебной шкатулкой? - да! Но я не буду писать почему так считаю. И вам не советую спорить о том, какой продукт лучше. Все мы разные и у нас разные виденье одного и того же, вы понимаете? Одного и того же, Карл 8)

Ребят, я забыл самое главное, дать название проекта: пусть он будет называться ShadowOfSpace (Космическая тень), такое немного зловещее название, с тонким намеком. (для тех кто будет читать впервые, я уже поправлю первую главу).
Поэтому если Ваш проект называется по другому, просто закройте проект, откройте проводник, удалите его папку целиком и снова создайте Blank Project на Blueprints.
Изображение
Версия значения не имеет, но я смотрю уже 4.16 идёт полным ходом. Уже доступна Preview3, а только ещё "вчера" была Preview1. Так что на всякий случай качну и её, и мы потом плавно на неё перейдем. Просто если перейти сейчас, вы ещё не раз будете обновляться до его стабильного релиза. Да и сам по себе релиз может быть не стабильным (хотя о какой стабильности речь... уж что, что, а программирование всегда было на уровне шаманства 8)).

Наш текущий уровень выглядит вот так:
Изображение

Мы собрались сделать следующее:
1) Нам надо создать общий класс карты, которая будет:
а) загружать карту
б) строить карту (из блоков)
в) в моей карте уже реализовано чтение блоков из редактора, это сделано для того чтобы мы могли прямо в редакторе строить свои карты
г) сохранять карту (возможно отложим на другой урок)
2) Cоответственно нам нужен класс блока, где его ID, будет говорить о том, какой блок. То есть блок в зависимости от своего ID, будет подгружать Static Mesh прямо в конструкторе.
3) Для отображения карты, нам потребуется класс HUD. Это типо виджета который постоянно висит на переднем плане (перед глазами) и если там ничего нет, то мы его по сути и не видим.
Карта блочная, то есть мы можем это реализовать через камеру, которая будет рисовать то что видит в текстуру, а мы её отображать, но в данном случае я покажу более интересный способ для "таких" (блочных) видов карт.

Всё это в принципе у меня почти готово и я надеюсь что мы добьем это всё сегодня. Как видим тут не строчки о том, что игра сетевая (кроме названия). Еще хотел бы Вас научить пользоваться официальной документацией, надеюсь на это хватит времени. Собственно это будет периодически когда мы будем попадать в код. Блоки в blueprins не двусмысленные, там можно лишь рассказать как мы их будем применять. А вот в коде, полным полно вариаций использования.

Приступаем.
Для начала познакомимся с редактором, с его левой частью. Всё сразу конечно не охватить глазом, поэтому я распишу
Изображение
1) Для создания C++ классов, можно использовать File->New C++ Class
2) Тут указывается полный путь папки в которой мы находимся, если нам надо перейти в папку по выше, жмем на предпоследнее название.
Сейчас мы находимся в Content->Blueprints, у вас такой папки нет (мы её создадим позже). Поэтому чтобы вернуться в "корень" я нажму Content
3) Переключатель под цифрой 3, как раз переключается в режим структуры папок (туда-сюда). По нажимайте  несколько раз, оставив на том моменте как у меня.
4) Тогда 4ым будет как раз структура
5) Это как раз место под конкретный контент лежащий в этой папке.
Если глаз наметан, то увидите и в контенте и в структуре поле "Filters" в котором можно начать набирать текст и он отфильтрует все варианты. Пока структура папок небольшая, и файлов не так много в этом нет необходимости. Но надо помнить что фильтровать можно не только по имени, но и по типу.

Ну пока хватит на этом. Чтобы создать C++ класс, можно использовать два вариант, это в контенте нажать правую кнопку мыши (сокращенно ПКМ)
Изображение

Совершенно без разницы где вы будете создавать С++ класс, потому что путь нам дадут выбрать чуть позже.
Нажимаем New Class (уже нажали ведь), из вариантов выбираем Actor (картинок сегодня будет так много, что некоторые буду просто сУвать под спойлер, иначе вы сойдете с ума).

Что такое Actor в двух словах, это объект у которого может быть модель и который может быть размещен на карте (в мире, на уровне). Его также можно перемещать, он также может(то есть изначально он это делать не умеет, переделать в 1 клик) передвигаться вдоль пути, но ничего другого я удобного не нашел для нашей задачи. Я нашел тут один тэг, теперь картинки не будут такими большими, но если вы вдруг чего то не разглядите, то браузер вам в помощь
Изображение
В мобильной версии достаточно ткнуть пальцем по картинке.

Кроме того любая логика описывается двумя вещами, это внутри Actor, а также если бы это был не Actor, а к примеру Character (посмотрите на иерархию классов)
Изображение
Character идет гораздо ниже, но он унаследован как раз от Actor. То есть сынулька Character имеет гораздо больший функционал, а кроме того может иметь такую вещь в себе как AIController, это такая хрень, где всю логику можно запрограммировать блоками через Behavior Tree. Поверьте мне там и удобнее, и нагляднее, но там есть и свои минусы. Мы позже затронем эту тему. Хотел избавиться от большого количества текста, но видимо придется просто записывать видео по одной или сразу нескольким статьям, там будет ТОЛЬКО нужная инфа.

И так, жмем Next. Пишем имя CMap. Первая буква C я обозначаю код, мне так проще определять и фильтровать когда их будет просто море. Теперь жмем Create Class

Гуд, будет ли от версий разный код, думаю что да. И вот именно поэтому тот код который создаст редактор будет именно в нужном виде. Я не буду показывать его здесь. В общих чертах он создал .h (header - заголовочный файл) и .cpp (непосредственно исполняемый код, методы класса и прочее) Ну можно заметить что там создалось два метода: BeginPlay() и Tick(), ну и сам конструктор. К слову сказать в С++ я полным ноль (забавно, правда? и именно я буду Вас учить)), поэтому если что скажу не так по нему, пишите в комментариях.
BeginPlay() и Tick() есть такой блок и в блупринтах, оба они просто события.
Первый вызывается когда наш класс инициализировался успешно и игра началась (но это не дает гарантии того, что другие классы уже прошли стадию построения).
Второй, по умолчанию вызывается каждый фрейм, но мы можем это изменить на конкретное число. Важно лишь отметить что, в событии Tick() ещё есть переменная delay, которая указывает сколько времени прошло с последнего вызова этого метода именно для этого класса. Это очень удобно. Время отсчитывается в секундах в виде float, к примеру если прошло 2,5 секунды, то и delay будет равным 2.5f.

Свою разработку я часто веду с кем то, поэтому есть необходимость в использовании удаленного репозитория. Это мы тоже затронем, в частности Git + Atlassian SourceTree (я дооолго пересаживался с консоли, но всё течет и всё меняется) при чём мы будем использовать построение git flow, но чуть позже, ввести Git можно в любой момент как только в нём появилась необходимость.

Сейчас нам надо настроить чтение из файла, пока из текстового, в дальнейшем переделаем под бинарный файл.
Для этого идем в заголовочный файл CMap.h и добавляем пару строк, если вы аккуратно раскроете дерево, то получится примерно так (смотрите ниже).  Этот файл, как описание структуры класса, то есть прежде чем написать тело какого то метода, мы должны указать в заголовке, что он вообще имеет место быть:
Изображение
Теперь нам надо в нём объявить метод, делается это так. Дважды кликаем по нему, после открытия в public добавляем (если вдруг public отсутствует, добавляем его сами). public, private, и прочие я буду описывать тыды, когда мы будем их касаться. Public означает публичный, то есть если написать после этого метод, этот метод можно будет вызвать при работе с этим классом, и именно такие методы или переменные можно будет форвардить в блупринты (чтобы и внутри блупринтов, можно было вызвать данный метод, для этого им потребуется дополнительный идентификатор). Обратите внимание там два public, добавляем в нижний.
Должно получится так:

public:  
  void LoadMap(FString filename);
Мы с Вами будем постепенно улучшать функционал, то есть хочу развить Вашу логику. Чтобы Вы не просто копировали и вставляли код (а лучше вообще писать его самому), а чтобы Ваши мозги думали ещё и "а нафига мы это делаем щас?".
Раз мы добавили код в header, и просто скомпилим, вылетит ошибка. Потому что метод мы объявили, а не описали. Так что давайте его добавим его и в .cpp. Точно также открываем файлик и... всегда придерживаюсь такой тактики, всё новое - я добавляю в конец. Так сразу видно откуда и что родилось. Таким образом, любой кто прочтет мой код, примерно поймет ход моих мыслей, а это очень важно для понимания кода, особенно чужого. Да и забросив код на несколько недель, не всегда сразу вспоминаешь что и где, в общем это удобно с любой стороны.
Открыли CMap.cpp, гуд, ползем в самый низ и там добавляем:
void ACMap::LoadMap(FString filename)
{
  //
}
Cишники наверное уже нервно смотрят в мою сторону, а я Джавик, я довольно часто буду использовать именно имена переменных с маленькой буквы, привычка.
А теперь чёж мы такое написали.
FString - это обычный string (то бишь строка), мы будем передавать в этот метод для удобство лишь короткое имя файла, там ... к примеру: level_01.smap, а весь остальной путь метод сам добавит так чтобы загрузить файл из Content/Levels/level_01.smap.
Я бы с радостью использовал для карт xml, и мы будем его использовать в дальнейшем (дополнительный файл карты), но сейчас я не готов для этого.
Unreal может читать файлы из текстовых файлов, а также из бинарных. Возрадуйтесь, вы можете править их как хотите, не изменяя код самой игры. То есть Ваша игра сможет работать с внешними файлами карт, конфигурационными файлами настроек, модами или ещё чем. Но скорее всего я оставлю это для "кота в мешке", отдельным архивом для особо-одаренных. Ну а все остальные смогут с этим разобраться сами, всё таки мы пишем на С++, а интернет кишит документацией.

Плин, гугл-док показывает уже пятую страницу, а мы так толком ничего не сделали, надо что-то менять...
Для того чтобы собрать всё это дело, надо нажать Ctrl+Shift+B.

Если вдруг у Вас Visual Studio Community на русском, просто ориентируйтесь на сочетания клавишь, они везде одинаковы. Также вы можете переключить язык студии на нужный, вот здесь. Tools->Options

А если английского там нет, то необходимо запустить установочный файл и выбрать там langauge pack EN.
После чего она загрузит и поставит сама, ну и попросит перезапуск студии.

После компиляции будет такой звук, который кстати издает UE.
Во время компиляции исходного кода лучше никуда не переключаться, так быстрее скомпилируется код.

Пойдем дальше улучшать.
Как я уже говорил, UE может читать лишь текстовые файлы и бинарные, а что с ними делать, мы должны описать сами.
Но для начала нам надо знать как воспользоваться этим инструментом внутри кода (эти методы доступны и в blueprint)
А отвечает за это всё FFileHelper https://docs.unrealengine.com/latest/INT/API/Runtime/Core/Misc/FF… er/index.html
(не ленимся, залезаем и смотрим что он умеет, наверное это единственный класс в котором от силы 6 методов).

Посмотрели, гуд, что заметили? Правильно, для чтения и записи файлов у него всего 4 метода.
Ах-ха-ха, шучу, шучу. На самом деле там есть очень интересный такой метод как
LoadANSITextFileToStrings

LoadANSITextFileToStrings
(
    const TCHAR* InFilename,
    IFileManager * InFileManager,
    TArray < FString >& OutStrings
)
И если кликнуть на IFileManager мы случайно обнаруживаем что можем работать с файлами на полную катушку. Перемещение, копирование, чтение файлов в каталоге, ну и пр. пр. пр.
Честно говоря для своих целей использовал другой метод, так что и я чему то научился новому 8)
Давайте посмотрим как правильно применять поиск, а нам сейчас надо найти как использовать этот метод у себя
забиваем в гугл http://ru.lmgtfy.com/?q=unreal+engine+4+LoadANSITextFileToStrings
unreal engine 4 LoadANSITextFileToStrings
жирненьким я выделил самое важное словосочетания букв, используйте его по чаще в своих запросах, можно ещё к нему добавить конкретно с++, чтобы ответ действительно был в коде.
И так, третий результат содержит нужный нам ответ (Read and Write data from text file - UE4 AnswerHub - Unreal Engine): эт скорее для любопытных(пытливых) https://answers.unrealengine.com/questions/88289/read-and-write-d… l?sort=oldest
Давайте добавим в наш CMap, два дополнительных метода, которые и будут вызывать методы FFileHelper. Это LoadStringFromFile() и SaveStringToFile(), естественно в CMap.h
void LoadStringFromFile(FString filename, TArray<FString> &StringArray);
  void SaveStringToFile(FString filename);
Ну и добавим соответственно описание методов в CMap.cpp.
void ACMap::LoadStringFromFile(FString filename, TArray<FString> &StringArray)
{
  IFileManager* FileManager = &IFileManager::Get();
  FFileHelper::LoadANSITextFileToStrings(*(FPaths::GameDir() + filename), FileManager, StringArray);
}

void ACMap::SaveStringToFile(FString filename)
{
  //
}
Замечательно. Функцию (метод) сохранения карты мы договорились оставить на потом. // это просто комментарий, туда можете писать хоть свои автографы, но лучше это делать так
/*
 * author: Salamandr
 * email: 
 * create: 23.05.2017 (для анг. лучше использовать слова) 23 May 2017, потому что у них наоборот, в начале месяц, потом число, затем год.
 **/
Поправьте, поправьте эти данные, с чего то же надо начинать быть программистом 8). Суют это дело в любое место, но лучше в самом верху CMap.h, потому что обычно именно он, распространяется для линковки, если что.

Теперь добавим код, скорее отладочный, именно этому я сейчас Вас научу. Вы начали использовать новый UE'шный метод, и не знаете как с ним работать. А для этого лучшим вариантом будем именно отладка.
Прежде всего убедитесь в том, что Ваш код действительно рабочий, соберите его. Ctrl+Shift+B. Если всё удачно, в конце мы увидим следующее

2>Total build time: 3,08 seconds (Local executor: 2,29 seconds)

Страницы: 1 2 3 4 Следующая »

#Blockmap, #debug, #MMO, #Tilemap, #Unreal Engine

23 мая 2017 (Обновление: 28 мая 2020)

Комментарии [5]