Mariya Mariya

Dreaman, Спасибо! =)

Dreaman Dreaman

Mariya, законченный вариант реально классно выглядит! Молодец :)

Mariya Mariya

Всем привет!
Сегодня хочу показать законченный вариант домика Сырны.

alexprey alexprey

StarPlosion: Битва с пиратами за планету

Wings' might Wings' might

Всем привет)
Добавил на этой неделе начальное окно, новую валюту и достижения:

alexprey alexprey

shadeborn, может быть они нашли способ как обойти полноценный запуск этих сервисов при запуске игры? Если так, то почему бы и не использовать единый клиент?) В любом случае, чем дальше все идет, там это все больше становится похоже на эту шутку

...
shadeborn shadeborn

Это всё круто, но...зачем? Ок есть Стим. Хорошо, теперь еще и ЕГС есть. Благо, комп не зашкварен Оригином...но у людей и он стоит. И для работы игр внутри этих сервисов нужны, собсно, эти сервисы. Так поверх этих сервисов нужно накатить еще один...

Devion Devion

согласен, есть кейсы которые без них вообще не делаются, не знать или избегать их определенно неправильно

Так точно, либо любое изменение в объекте на стороне редактора.

alexprey alexprey

Devion, только рад за дополнение, Спасибо)

хм, а вот это интересно, не пробовал никогда такой кейс.

Devion Devion

лёш, ты же не против если я дополню? )

скриптаблы, для понимания могут храниться на уровне ассета и на уровне сцены.

Mariya Mariya

Всем привет!
Начали работу над мебелью в домик Сырны, и первым сделали чайный столик с чайным сервизом. А так же продолжаем работу над анимациями.

Wings' might Wings' might

Всем привет)
За неделю в игру было добавлено меню настроек, переделана старая локация, добавлены новые враги и повышена производительности

alexprey alexprey

А мне кажется это такой особый хитрый ход, хотя с другой стороны как разработчики успеют к этому подготовиться получше 🤔

Dreaman Dreaman

Всем привет!
Для проекта "Mental State" разработано новое устройство, которое уже полностью функционирует внутри игрового мира. Оно носит название "Репульсивер". Совместно с очередными большими воротами это устройство образует новую головоломку...

...
Mariya Mariya

Всем привет!
На этой неделе мы научили Сырну летать!

Tartal Tartal

alexprey, кастомизации - создание внешности персонажа? Я всегда любил это, но не думаю, что это будет к месту в мясном шутере)
EfimovMax, да, есть немного)

Tartal Tartal

Jusper, точно не помню, уже как полгода точно) Я вроде в Дискорде немного обсуждал эту тему. Пока немножко попробовал движок - мне очень нравится. Ну, в конце концов, он идеально подходит под жанры, с которыми я хочу работать...

alexprey alexprey

Первая тема крутая очень!

Логотип проекта Unreal Engine

Муки диалоговые

Муки диалоговые

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

Предположим, что вы делаете квыст с кучей диалогов. Диалоги и без того сильно подрезаны синдромом массэффекта, потому как кое-кто решил, что пяти ответов хватит, и теперь нехочет перепиливать кучу систем под это дело, так вдобавок добавление одной фразы выглядит как-то так:

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

Теперь представьте, что за гаплык творится, когда таких диалогов становится полсотни, и в некоторых - по 15 вот такого типа блоков.

Будучи ленивой задницей, я решил, что мне надо ужать это до чего-то хотя бы вот такого:

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

Но как?

В данный момент диалоги лежат в виде индексированных текстовых файлов в папке Content\Text\<locale>\phr_<dialog_id>_<phrase_id>.txt

Чтобы пилить диалоги (и вообще всю текстовку) и не сойти с ума, пришлось запилить вот такую софтину:

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

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

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

Почему бы не повторить ее в виде своеобразного файла-манифеста, который и будет нести блок информации о фазе диалога? Сказано-сделано, на выходе имеем такой вот код на делфи для чтения/записи данных:

//Это записывает данные
procedure TForm3.Button1Click(Sender: TObject); 
var i,j:integer;
begin
  Memo1.Clear;
  //adding data
  if CheckBox1.Checked=true then Memo1.Text:=Memo1.Text+'1' else Memo1.Text:=Memo1.Text+'0';
  if CheckBox2.Checked=true then Memo1.Text:=Memo1.Text+'1' else Memo1.Text:=Memo1.Text+'0';
  if CheckBox3.Checked=true then Memo1.Text:=Memo1.Text+'1' else Memo1.Text:=Memo1.Text+'0';
  if CheckBox4.Checked=true then Memo1.Text:=Memo1.Text+'1' else Memo1.Text:=Memo1.Text+'0';
  if CheckBox5.Checked=true then Memo1.Text:=Memo1.Text+'1' else Memo1.Text:=Memo1.Text+'0';
  //adding data
  for j:=0 to 2 do
  for i:=0 to 4 do
  begin
    Memo1.Text:=Memo1.Text+StringGrid1.Cells[j,i]+';';
  end;
  Memo1.Lines.SaveToFile(contentloc+'text\phasemanifest_'+form1.edit2.text+'_'+Form1.Edit3.Text+'.txt');

end;


 //а это читает данные
procedure TForm3.Button2Click(Sender: TObject);
var ms,wrd:string;
    i,j:integer;
    k:integer;
begin
  
 Memo1.Lines.LoadFromFile(contentloc+'text\phasemanifest_'+form1.edit2.text+'_'+Form1.Edit3.Text+'.txt');
  ms:=memo1.Text;
  if ms[1]='1' then CheckBox1.Checked:=true else CheckBox1.Checked:=false;
  if ms[2]='1' then CheckBox2.Checked:=true else CheckBox2.Checked:=false;
  if ms[3]='1' then CheckBox3.Checked:=true else CheckBox3.Checked:=false;
  if ms[4]='1' then CheckBox4.Checked:=true else CheckBox4.Checked:=false;
  if ms[5]='1' then CheckBox5.Checked:=true else CheckBox5.Checked:=false;
  wrd:='';
  i:=0;
  j:=0;
  for k:=6 to length(ms) do
  begin
    if ms[k]=';' then
    begin
      StringGrid1.Cells[j,i]:=wrd;
      wrd:='';
      i:=i+1;
      if i>4 then
      begin
        i:=0;
        j:=j+1;
      end;
    end
    else
    begin
      wrd:=wrd+ms[k];
    end;
  end;

end;

Будучи редкостным лентяем, вместо использования заассайненного файла, я пользовал методы сохранения/загрузки на компоненте Memo, ибо нефиг.

Интерфейс вышел вот такой:

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

Справа как раз и можно глянуть на манифест в зашифрованном виде. Галочки с надписями use отвечают за указание на то, будет ли этот кусок добавлен в массивы флагов в конечной структуре.

Отлично! Дело сделано!

Ээээ... Стопэ. А как игра будет читать это дело?

В свое время я раскопал решения для удобного чтения текстовых файлов с диска в анрыловский апи:

//ЭТО В ХЕДЕРЕ
	UFUNCTION(BlueprintCallable, Category = "save")
		static bool FileSaveString(FString SaveTextB, FString FileNameB);

	UFUNCTION(BlueprintPure, Category = "save")
		static bool FileLoadString(FString FileNameA, FString& SaveTextA);
		
//ЭТО В ЦПП ФАЙЛЕ
bool Ucppfunctions::FileSaveString(FString SaveTextB, FString FileNameB)
{
	return FFileHelper::SaveStringToFile(SaveTextB, *(FPaths::ProjectDir() + FileNameB));
}

bool Ucppfunctions::FileLoadString(FString FileNameA, FString& SaveTextA)
{
	return FFileHelper::LoadFileToString(SaveTextA, *(FPaths::ProjectDir() + FileNameA));
}

Это, конечно, чудесно - я могу без проблем прочесть манифест... Но как его проанализировать и выдернуть данные? Ну, к счастью, тип FString прописан по-людски, и потому вполне работает вот такой код:

//ЭТО В ХЕДЕРЕ
UFUNCTION(BlueprintCallable, Category = "save")
		static void ProcessPhraseManifest(FString FManText, 
			bool& use0, bool& use1, bool& use2, bool& use3, bool& use4,
			bool& rfc0, bool& rfc1, bool& rfc2, bool& rfc3, bool& rfc4,
			int& rfid0, int& rfid1, int& rfid2, int& rfid3, int& rfid4, 
			int& rphid0, int& rphid1, int& rphid2, int& rphid3, int& rphid4);

//ЭТО В ЦППШКЕ
void Ucppfunctions::ProcessPhraseManifest(FString FManText,
	bool& use0, bool& use1, bool& use2, bool& use3, bool& use4,
	bool& rfc0, bool& rfc1, bool& rfc2, bool& rfc3, bool& rfc4,
	int& rfid0, int& rfid1, int& rfid2, int& rfid3, int& rfid4,
	int& rphid0, int& rphid1, int& rphid2, int& rphid3, int& rphid4)
{
	bool u0;
	bool u1;
	bool u2;
	bool u3;
	bool u4;
	if (FManText[0] == '0')
	{
		u0 = false;
	}
	else
	{
		u0 = true;
	}
	if (FManText[1] == '0')
	{
		u1 = false;
	}
	else
	{
		u1 = true;
	}
	if (FManText[2] == '0')
	{
		u2 = false;
	}
	else
	{
		u2 = true;
	}
	if (FManText[3] == '0')
	{
		u3 = false;
	}
	else
	{
		u3 = true;
	}
	if (FManText[4] == '0')
	{
		u4 = false;
	}
	else
	{
		u4 = true;
	}

	use0 = u0;
	use1 = u1;
	use2 = u2;
	use3 = u3;
	use4 = u4;

	int i = 5;
	int ph = 0;
	int itm = 0;
	int ll = FManText.FString::Len();

	FString wrd;

	while (i < ll - 1)
	{
		if (FManText[i] == ';')
		{
			if (ph == 0)
			{
				if (itm == 0)
				{
					if (wrd == "0") { rfc0 = false; }
					else { rfc0 = true; }
				}
				if (itm == 1)
				{
					if (wrd == "0") { rfc1 = false; }
					else { rfc1 = true; }
				}
				if (itm == 2)
				{
					if (wrd == "0") { rfc2 = false; }
					else { rfc2 = true; }
				}
				if (itm == 3)
				{
					if (wrd == "0") { rfc3 = false; }
					else { rfc3 = true; }
				}
				if (itm == 4)
				{
					if (wrd == "0") { rfc4 = false; }
					else { rfc4 = true; }
				}
			}
			if (ph == 1)
			{
				if (itm == 0)
				{
					rfid0=FCString::Atoi(*wrd);
				}
				if (itm == 1)
				{
					rfid1 = FCString::Atoi(*wrd);
				}
				if (itm == 2)
				{
					rfid2 = FCString::Atoi(*wrd);
				}
				if (itm == 3)
				{
					rfid3 = FCString::Atoi(*wrd);
				}
				if (itm == 4)
				{
					rfid4 = FCString::Atoi(*wrd);
				}
			}
			if (ph == 2)
			{
				if (itm == 0)
				{
					rphid0 = FCString::Atoi(*wrd);
				}
				if (itm == 1)
				{
					rphid1 = FCString::Atoi(*wrd);
				}
				if (itm == 2)
				{
					rphid2 = FCString::Atoi(*wrd);
				}
				if (itm == 3)
				{
					rphid3 = FCString::Atoi(*wrd);
				}
				if (itm == 4)
				{
					rphid4 = FCString::Atoi(*wrd);
				}
			}
			
			wrd = "";
			
			itm++;
			if (itm == 5) { itm = 0; ph++; }
		}
		else
		{
			wrd += FManText[i];
		}

		i++;
	}

	return;
	
}

Окай, дело. Громоздко, но работает, гном счастлив. Теперь остается добавить немного соли, сыра и сахару, в виде уже блюпринтовой функции, которая будет это дело обрабатывать. А точнее, даже двух. Первая читает данные из манифеста и пишет их в структуру:

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

Вторая ей помогает, формируя массивы на основе набора флагов:

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

Запускаем @ проверяем!

Муки диалоговые — Unreal Engine — DevTribe: Разработка игр (blueprints, C++, ue4, разработка игр, укрупнение, диалоговаясистема)

И вот таким вот образом я никоим образом не поправил себе состояние башки, но зато немного упростил жизнь с формированием блоков диалогов в моем magnum opus. Всем мира и корзинок!



Что то мне подсказывает что можно было бы обойтись более простой кровью... В виде набора хелпкрных функций для блупринта формировать граф диалога и потом использовать его. Хотя я не уверен (вообще без понятия как работает блюпринт) поэтому возможно это единственное решение.
Но я помню тогда еще в далекие времена моддинга вара диалоговая система у меня строилась на каком то таком коде

Int dialogId = CreateDialogForUnit(...)
AnswerId1 = CreateAnswerForDialog(dialogId,...)
Answer_AddOpenDialogAction(AnswerId, anotherDialogId)
AnswerId2 = CreateAnswerForDialog(dialogId,...)
Answer_AddAction(Answer2,...)

И потом оно все автоматически работало. Может быть можно было бы это обернуть в удобные хелперы для блюпринта. Единственный минус обязательно рекомпилить для фикса диалогов

Хех, там что так что эдак рекомпиляция понадобится, каждый диалог сейчас нуждается в индивидуальной настройке узловой структуры, в которую заливаются фразы. Ну и, как ни крути, задача выросла из попытки привести в чувство уже существующую, но до ужаса субоптимальную систему, не ломая ее - потому что на нее уже завязано немало контента. Как-то так =)

Дарин, ну т.е. сделать такой граф диалогов в блюпринте впринципе возможно?

Вполне можно, через ту же структуру. Хотя, наверное, удобнее было бы реализовывать это дело с помощью C++-класса вынесенного в отдельный объект-контроллер диалогов.

Дарин, ну не всегда удобно писать код для определения диалогов и настройки связей, особенно если есть возможно прокликивать и перетаскивать нужные блоки