В редакторе триггеров мне часто доводится использовать гипертекст - когда нажатие на определенное слово приводит к каким-либо действиям. Увы в стандартном арсенале движка нет инструмента для работы с подобным, потому пришлось писать его с нуля.
Хочу заметить, что это немного не тот гипертекст, который вы привыкли видеть в html - это не открытие страниц в браузере, а именно что совершение определенных действий.
Для того чтобы работать с гипертекстом добавьте следующий класс:
using System; using System.Collections.Generic; using System.Linq; using UnityEngine; public class Hypertext { public string link_null = "<null>"; public string color_normal = "black", color_link = "blue"; public GUIStyle style = new GUIStyle("label") { richText = true, fontSize = 15, fontStyle = FontStyle.Bold }; private readonly List<Text> phrases = new List<Text>(); private struct Text { public Action action; public string text; public int index; } private int GetIndexForLast(string str) { return (phrases.Count == 0 ? 0 : phrases[phrases.Count - 1].index) + str.Length; } public void Add(string str, Action action = null) { phrases.Add(new Text { text = str, index = GetIndexForLast(str), action = action}); } public void AddFormat(string str, params Action[] actions) { const char lBracket = '{', rBracket = '}'; int currAction = 0; var cursor = 0; while (true) { var rIndex = str.IndexOf(rBracket, cursor); if (rIndex == -1) break; var lIndex = str.LastIndexOf(lBracket, rIndex); if (lIndex == -1) break; var l1 = lIndex - cursor; if (l1 != 0) Add(str.Substring(cursor, l1)); var l2 = rIndex - lIndex - 1; var linktext = (l2 != 0) ? str.Substring(lIndex + 1, l2) : link_null; if (actions != null && currAction < actions.Length) Add(linktext, actions[currAction++]); else Add(linktext); cursor = rIndex + 1; } if (cursor != str.Length) Add(str.Substring(cursor, str.Length - cursor)); } private static string Color(string color, string content) { return "<color=" + color + ">" + content + "</color>"; } private string GetSimpleString() { return phrases.Aggregate("", (str, p) => str + (p.action == null || !string.IsNullOrEmpty(p.text) ? p.text : link_null)); } private string GetRichString() { return phrases.Aggregate("", (str, p) => str + (p.action == null ? Color(color_normal, p.text) : !string.IsNullOrEmpty(p.text) ? Color(color_link, p.text) : Color(color_link, link_null))); } private Text GetTextFromCursor(int index) { return phrases.FirstOrDefault(x => index < x.index); } public static void DrawLayout(GUIStyle style, string str, params Action[] actions) { var h = new Hypertext {style = new GUIStyle(style) { richText = true }}; h.AddFormat(str, actions); h.DoDrawLayout(); } public static void DrawLayout(string str, params Action[] actions) { var h = new Hypertext(); h.AddFormat(str, actions); h.DoDrawLayout(); } public void DoDrawLayout() { var textRich = GetRichString(); var textSimple = GetSimpleString(); var rect = GUILayoutUtility.GetRect(new GUIContent(textSimple), style); var index = style.GetCursorStringIndex(rect, new GUIContent(textSimple), Event.current.mousePosition - new Vector2(3, 0)); var focusText = GetTextFromCursor(index); GUI.Label(rect, textRich, style); if (Event.current.type == EventType.MouseDown && focusText.action != null) { focusText.action.Invoke(); Event.current.Use(); } } }
Примеры использования
Есть несколько способов использовать гипертекст с помощью этого класса - каждый из способов может быть удобен в определенных ситуациях.
- Формирование гипертекста "частями"
var a = new Hypertext(); a.Add("Мама "); a.Add("мыла", () => Debug.Log("click")); a.Add(" раму"); a.DoDrawLayout();
В этом случае ключевым является метод Add, и если после текста мы вводим функтор () => { /*Наши действия*/ }, то это слово считается за ссылку.
- Формирование гипертекста в одной строке
var a = new Hypertext(); a.AddFormat("Мама {мыла} раму", () => Debug.Log("click")); a.DoDrawLayout();
В этом случае ключевым является метод AddFormat, в котором все ссылки мы обособляем фигурными скобками, а после, через запятые, указываем функтор-действие на каждую ссылку.
- Статическим методом
Hypertext.DrawLayout("Мама {мыла} {раму}", () => Debug.Log("нажато 'мыла'"), () => Debug.Log("нажато 'раму'"));
Этот метод по сути работает так же как и второй способ, но позволяет сэкономить в писанине.
Скриншот как пример работы 2 способа:
Кроме того в классе есть несколько дополнительных полей, которые позволяют немного разнообразить ссылки:
style - отвечает за стиль, используемый для рисования надписи
link_null - позволяет указать текст, который будет выводиться если ссылка не имеет названия (должна же оставаться возможность нажимать на нее, верно?)
color_normal - указывает цвет обычного текста (в виде строки)
color_link - указывает цвет ссылок (в виде строки)
Указание цвета в виде строки должно соответствовать правилам указанным вот здесь
Смотрите также:
Комментарии
Есть какие-то идеи как это можно использовать? Тоесть кроме как для работы в консоли применения ему я придумать не могу. Или это скорее для неигровых приложений?
RiseD_Konst, в редакторах, например. У меня за год было уже 3 таких случая, когда текст вроде слитный, но и вроде должен содержать в себе какой-то функционал. А в целом применение широкое его было как я написал в редакторе триггеров, который я пишу как аналог редактору триггеров в варкрафте, где как раз ссылки и используются.
В еve почти все в этих гиперссылках...
RiseD_Konst, в диалоговой системе. Когда в тексте подсвечиваются слова темы, кликая на которые персонаж рассказывает об этом. Как в морровинде
Нот бэд! На этом реально будет замутить систему диалогов как в морровинде, насколько я понимаю? Вот там была реально шикарная система развития диалогов.
CollectableItemData.cs
[CreateMenuItem(fileName = "newItem", menuName = "Data/Items/Collectable", order = 51]