Как сделать диалоговое окно юнити

Обновлено: 15.05.2024

Итак, на повестке дня у нас, один из вариантов реализации системы диалогов в игре. Сразу оговоримся, что представленный проект из разряда, дешево и сердито. То есть, реализованы только базовые возможности. Идея заключается в том, что диалоги в игре разбиты на отдельные файлы. Каждый диалог, это конкретный файл, XML типа. Например, когда игрок подходит к NPC, делается запрос на диалог, связанный с этим персонажем. Происходит его загрузка и на основе полученных данных, создается интерфейс пользователя. При этом предусмотрена возможность не только вручную создавать файл, но и с помощью простенького XML генератора. Еще один важный момент в том, что такой подход позволяет создать сколько угодно языковых вариантов диалогов.

Настройка сцены. Для отображения диалога воспользуемся системой UI Юнити. Добавляем Scroll View и настраиваем его для вертикальной прокрутки. Затем добавляем еще Button, ширину кнопки подстраиваем под ширину окна прокрутки, высота будет регулироваться через скрипт. Еще нужно настроить присет трансформа кнопки, устанавливаем как показано на скриншоте:

Система диалогов

Дополнительно на кнопку вешаем скрипт:

Здесь мы указываем компоненты кнопки, для упрощенного доступа к ним в дальнейшем.

Кстати говоря, в проекта должна быть папка Resources в ней будут хранится все файлы диалогов. Внутри нее создаем подпапку, например Russian, то есть, это будет у нас язык по умолчанию. В дальнейшем можно просто сделать дубликат этой папки, обозвать ее другим языком и сделать перевод текста в файлах. Таким образом, можно сделать несколько языков в игре.

Как должен выглядеть XML:

npcText - текст который говорит NPC. Затем идут ответы игрока.
Атрибут toNode - перенаправление в другой узел диалога.
Атрибут exit закрывает диалоговое окно.

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

А к нему еще один вспомогательный:

С помощью этой парочки, можно генерировать XML. Предназначены они для работы только в редакторе. Просто заполняем массив диалога в инспекторе Юнити, настраиваем опции и жмем кнопочку Generate и готово. Файл будет создан по указанному адресу.


Далее. Что нам еще нужно? Чтение созданного файла и создание окна диалога.

За это отвечает данный скрипт:

Здесь показано как загрузить XML из ресурсов Юнити, а затем извлечь из него значения атрибутов. После загрузки, массивы будут иметь точно такой же вид, как при создании в генераторе, соответственно и в окне, узлы будут отображаться в таком же виде.


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

Для удобства я создал проект с основными ассетами(2 спрайта + шрифт), для его открытия нужна версия Unity3D 4.3+. На основе этого проекта мы будем писать систему диалогов.



I. Откройте пустую сцену "Dev”.
Создайте GameObject с именем "DialogueSystem”
В него добавьте два объекта типа "GUITexture”
Назначьте им текстуры, и соответственно переименуйте их в "Background” и "Pointer”
Теперь нужно задать Pixel Inset текстурам. Для текстуры задника это:




Не забудьте изменить расположение текстур на 0,0,0, а с указателя уберите галочку активности.
Тексту дайте имя "Text”, поставьте размер 38 и шрифт из папки "Fonts”, по желанию измените надпись, ну и отрегулируйте позицию:




Создайте пустой GameObject с именем "Answers”, и внутри добавьте три объекта типа "GUIText” (временно можно отключить объект Text, что бы не мешался)
Точно так же настройте размер, шрифт, обязательно дайте смешное значение в поле текст.
Отрегулируйте позицию всем трем текстам, что бы все было примерно как здесь:



Отключите Answers и включите Text. Сохраните сцену и проект(на всякий случай).
II. Теперь по коду. Скачаем для начала iTween, с ним даже плохие ассеты могут показывать хорошую картинку, благодаря красивым анимациям.

Зайдите в Asset Stote, в поиск введите "iTween”



Дальше довольно много переменных:

Код
public float letterDelay = 0.06f; //Задержка в написании одной буквы для красивого эффекта
public float transitionTime = 0.5f; //Время всех твинов

for( int index = 0; index < words.Length; index++)
<
string word = words[index].Trim();
if (index == 0) <
result = words[0];
letterCounter+=words[0].Length;
>

if (index > 0 ) <
if (letterCounter + word.Length + 1 < maxLetters)
<
result += " " + word;
letterCounter+=word.Length + 1;
>
else if (numberOfLines < maxLines)
<
result += "\n" + word;
letterCounter = word.Length;
numberOfLines += 1;
nextText = "";
>
else if (numberOfLines == maxLines)
<
nextText = currentText;
nextText = nextText.Remove(0, result.Length);
break;
>
>
>
return result;
>


Тут же и функция "TypeText”, которая будет рисовать текст по одной букве:
Код
IEnumerator TypeText () <
formattedText = FormatString (currentText);
foreach (var letter in formattedText.ToCharArray()) <
text.guiText.text += letter;
yield return new WaitForSeconds(letterDelay);
if (text.guiText.text == formattedText)
<
break;
>
>
>

Функция Update:
Код
void Update () <
if (tweenCompleted) <
if (answersMode == false) <
if (Input.GetKeyDown (KeyCode.Space))
<
if (text.guiText.text != formattedText) <
text.guiText.text = formattedText;
>
else if (text.guiText.text == formattedText && nextText != "") <
Reset(true);
>
else if (text.guiText.text == formattedText && nextText == "")
<
ShowAnswers();
>
>
>
else <
if (Input.GetKeyDown (KeyCode.DownArrow)) <
if (selectedAnswer + 1 < testAnswers.Count) <
selectedAnswer++;
>
else if (selectedAnswer + 1 >= testAnswers.Count) <
selectedAnswer = 0;
>
iTween.Stop(pointer);
ResetPointer();
>
if (Input.GetKeyDown (KeyCode.UpArrow)) <
if (selectedAnswer + 1 > 1) <
selectedAnswer--;
>
else if (selectedAnswer + 1 selectedAnswer = testAnswers.Count - 1;
>
iTween.Stop(pointer);
ResetPointer();
>
if (Input.GetKeyDown (KeyCode.Space)) <
SelectAnswer();
>
if (iTween.Count(pointer) == 0) StartPointerTween();
>
>
>

Мы почти готовы. Код связанный с ответами и анимацией указателя:
Код
private void ResetPointer () <
pointer.transform.position = answers.GetComponentsInChildren() [selectedAnswer].transform.position;
pointer.guiTexture.pixelInset = new Rect(((answers.GetComponentsInChildren() [selectedAnswer].transform.position.x/Screen.width) +
answers.GetComponentsInChildren() [selectedAnswer].GetScreenRect().width) * 1.05f,
pointer.guiTexture.pixelInset.y,pointer.guiTexture.pixelInset.width,pointer.guiTexture.pixelInset.height);
>

private void StartPointerTween() <
iTween.MoveAdd(pointer,iTween.Hash("name","pointer","x",0.01f,"time",0.4f, "looptype", iTween.LoopType.pingPong, "easetype", iTween.EaseType.easeInOutCirc));
>

void SelectAnswer () <
tweenCompleted = false;
pointer.SetActive(false);
Reset();

void ShowAnswers()
<
iTween.FadeFrom (textBackground, 0, transitionTime/2);
iTween.MoveFrom (textBackground,iTween.Hash("y",-1f,"time",transitionTime/2,"delay",0,"oncomplete","OnAnswersTweenComplete","onCompleteTarget", gameObject));

tweenCompleted = false;
answersMode = true;
text.SetActive (false);
>


Ну и много, много коллбеков:
Код
private void OnPointerAppearingTweenComplete () <
tweenCompleted = true;
StartPointerTween ();
>

private void OnAppearingTweenComplete () <
tweenCompleted = true;
StartCoroutine(TypeText());
>

private void OnDisappearingTweenComplete () <
tweenCompleted = true;
gameObject.SetActive (false);
>

private void OnFirstAnswerTweenComplete () <
selectedAnswer = 0;
pointer.SetActive (true);
ResetPointer ();

iTween.MoveFrom (pointer, iTween.Hash("x",1f,"time",transitionTime/4,"delay",0,"oncomplete","OnPointerAppearingTweenComplete","onCompleteTarget", gameObject));
iTween.FadeFrom (pointer, 0, transitionTime/2);
>

private void OnAnswersTweenComplete () <
answers.SetActive (true);
pointer.guiTexture.color = new Color (pointer.guiTexture.color.r,pointer.guiTexture.color.g,pointer.guiTexture.color.b,1f);

foreach (Transform child in answers.transform)
<
child.gameObject.SetActive(true);
>

float modifier = 0.5f;
Component[] answersFields = answers.GetComponentsInChildren();

for (int i = 0; i < 3; i++) <
if (i < testAnswers.Count) <
modifier++;
answersFields[i].guiText.text = testAnswers[i];
if (i == 0) <
iTween.MoveFrom (answersFields[i].gameObject, iTween.Hash("y",-1f - modifier,"time",transitionTime/2,"delay",0,
"oncomplete","OnFirstAnswerTweenComplete","onCompleteTarget", gameObject));
>
else <
iTween.MoveFrom (answersFields[i].gameObject, iTween.Hash("y",-1f - modifier,"time",transitionTime/2,"delay",0));
>
iTween.FadeFrom (answersFields[i].gameObject, 0, transitionTime/2);
>
else <
answersFields[i].gameObject.SetActive(false);
>
>
tweenCompleted = false;
>

С кодом таки все. Поставьте теперь Дилей в районе 0.06 и запускайте:

Вы можете создать любое количество пользовательских окон в вашем приложении. Они ведут себя точно так же как и инспектор, Scene View или любое другое встроенное окно. Это отличный способ добавить интерфейс в подсистему вашей игры.

Пользовательский интерфейс редактора за авторством Serious Games Interactive, используемый для скриптинга событий кат-сцен

Пользовательский интерфейс редактора за авторством Serious Games Interactive, используемый для скриптинга событий кат-сцен

Создание пользовательского окна редактора (Editor Window) включает в себя следующие шаги:

  • Создание скрипта, являющегося производным от EditorWindow.
  • Использование кода для включения отображения окна.
  • Реализация кода интерфейса для вашего инструмента.

Вывод из EditorWindow

Для того, чтобы сделать своё окно редактора, ваш скрипт должен храниться внутри папки, названной “Editor”. Создайте в этом скрипте класс, который будет производным от EditorWindow. Затем пропишите ваши элементы управления интерфейса во внутренней OnGUI функции.

MyWindow.js - помещается в папку ‘Editor’ внутри вашего проекта.

Отображение окна

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

Стандартным поведением в Unity будет использование различных окон, закрывая одни и открывая другие (таким образом, повторный выбор элемента меню покажет существующие окна. Это выполняется используя функцию EditorWindow.GetWindow. Вот так:

Отображение окна MyWindow

Это создаст стандартное закрепляемое окно редактора, которое сохраняет свою позицию между вызовами, может быть использовано в пользовательских лейаутах и т.д. Чтобы получить больше контроля над тем, что создаётся, вы можете использовать GetWindowWithRect.

Реализация интерфейса вашего окна

Само содержимое окна рендерится при реализации функции OnGUI. Вы можете использовать те же UnityGUI классы, что используете для игрового интерфейса ( GUI и GUILayout ). В дополнение мы предоставляем некоторые дополнительные элементы управления интерфейса, расположенные в editor-only (только для редактора) классах EditorGUI и EditorGUILayout . Эти классы добавляют элементы управления уже доступные в обычных классах, так что вы можете совмещать и комбинировать как захотите.

В итоге этот пример создаёт окно, которые выглядит вот так:

Пользовательское окно редактора, созданное при помощи предоставленного примера.

Пользовательское окно редактора, созданное при помощи предоставленного примера.

Для дополнительной информации изучите примеры и документацию на странице EditorWindow.

Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable.

Submission failed

For some reason your suggested change could not be submitted. Please try again in a few minutes. And thank you for taking the time to help us improve the quality of Unity Documentation.

Declaration

Parameters

title The title of the message box.
message The text of the message.
ok Label displayed on the OK dialog button.
cancel Label displayed on the Cancel dialog button.

Returns

bool Returns true if the user clicks the OK button. Returns false otherwise.

Description

This method displays a modal dialog.

Use it for displaying message boxes in the Editor.

ok and cancel are labels to be displayed on the dialog buttons. If cancel is empty (the default), then only one button is displayed. DisplayDialog returns true if ok button is pressed.

For dialog boxes that might be shown repeatedly, consider using an overload of this method that takes a DialogOptOutDecisionType as described in the below the code example.



Dialog box that shows info on the number of objects to be placed on the surface.

Declaration

public static bool DisplayDialog (string title , string message , string ok , DialogOptOutDecisionType dialogOptOutDecisionType , string dialogOptOutDecisionStorageKey );

Declaration

public static bool DisplayDialog (string title , string message , string ok , string cancel = "", DialogOptOutDecisionType dialogOptOutDecisionType , string dialogOptOutDecisionStorageKey );

Parameters

title The title of the message box.
message The text of the message.
ok Label displayed on the OK dialog button.
cancel Label displayed on the Cancel dialog button.
dialogOptOutDecisionType The type of opt-out decision a user can make.
dialogOptOutDecisionStorageKey The unique key setting to store the decision under.

Returns

bool true if the user clicks the ok button, or previously opted out. Returns false if the user cancels or closes the dialog without making a decision.

Description

This method displays a modal dialog that lets the user opt-out of being shown the current dialog box again.

Use this method to display dialog boxes in the Editor that might be shown repeatedly. Choose which DialogOptOutDecisionType to use based on how often you think users encounter this message and how often you want to remind them of it.

If the user opts-out of seeing the dialog box associated with the provided dialogOptOutDecisionStorageKey , Unity doesn't show the dialog box and this method immediately returns true .

ok and cancel are labels displayed on the dialog buttons. If cancel is empty, the button displays as "Cancel". This is the default setting. DisplayDialog returns true if the user presses the ok button.

If the user opts-out of the dialog box, Unity stores this decision. If dialogOptOutDecisionType is set to DialogOptOutDecisionType.ForThisMachine Unity stores the decision via EditorPrefs.SetBool. If dialogOptOutDecisionType is set to DialogOptOutDecisionType.ForThisSession Unity stores the decision via SessionState.SetBool. In both cases Unity stores the decision under the key provided as dialogOptOutDecisionStorageKey .

If you want to the let the user change the decision that is stored in EditorPrefs, you can add this to the Editor Preferences with a SettingsProvider.

Use DialogOptOutDecisionType.ForThisSession to show a dialog before a user performs a destructive action that might lose some of their work. If you think the user might see this dialog too often, you can add an option to the Editor Preferences with a SettingsProvider by using EditorPrefs and query that setting before showing the dialog.

Вы можете создать любое количество пользовательских окон в вашем приложении. Они ведут себя точно так же как и инспектор, Scene View или любое другое встроенное окно. Это отличный способ добавить интерфейс в подсистему вашей игры.

Custom Editor Interface by Serious Games Interactive used for scripting cutscene actions

Custom Editor Interface by Serious Games Interactive used for scripting cutscene actions

Создание пользовательского окна редактора (Editor Window) включает в себя следующие шаги:

  • Создание скрипта, являющегося производным от EditorWindow.
  • Использование кода для включения отображения окна.
  • Реализация кода интерфейса для вашего инструмента.

Вывод из EditorWindow

Для того, чтобы сделать своё окно редактора, ваш скрипт должен храниться внутри папки, названной “Editor”. Создайте в этом скрипте класс, который будет производным от EditorWindow. Затем пропишите ваши элементы управления интерфейса во внутренней OnGUI функции.

MyWindow.js - помещается в папку ‘Editor’ внутри вашего проекта.

Отображение окна

In order to show the window on screen, make a menu item that displays it. This is done by creating a function which is activated by the MenuItem property.

Стандартным поведением в Unity будет использование различных окон, закрывая одни и открывая другие (таким образом, повторный выбор элемента меню покажет существующие окна. Это выполняется используя функцию EditorWindow.GetWindow. Вот так:

Отображение окна MyWindow

Это создаст стандартное закрепляемое окно редактора, которое сохраняет свою позицию между вызовами, может быть использовано в пользовательских лейаутах и т.д. Чтобы получить больше контроля над тем, что создаётся, вы можете использовать GetWindowWithRect.

Реализация интерфейса вашего окна

Само содержимое окна рендерится при реализации функции OnGUI. Вы можете использовать те же UnityGUI классы, что используете для игрового интерфейса (GUI и GUILayout). В дополнение мы предоставляем некоторые дополнительные элементы управления интерфейса, расположенные в editor-only (только для редактора) классах EditorGUI и EditorGUILayout. Эти классы добавляют элементы управления уже доступные в обычных классах, так что вы можете совмещать и комбинировать как захотите.

В итоге этот пример создаёт окно, которые выглядит вот так:

Custom Editor Window created using supplied example.

Custom Editor Window created using supplied example.

Для дополнительной информации изучите примеры и документацию на странице EditorWindow.

Читайте также: