Re: Dialogue System 1.8.2 Released!
Posted: Wed Jun 20, 2018 11:23 am
Hi @Racoon7,
I'm replying to this thread because it contains your original post, but this applies to version 2.0.
It imports into the Dialogue System like this:
Is that what you're doing, too?
I'm replying to this thread because it contains your original post, but this applies to version 2.0.
This is fixed in version 2.0.1 (coming soon).Racoon7 wrote: ↑Fri May 25, 2018 7:20 pmOne-time dialogue
I have a bunch of "opening" dialogues I want to trigger only the first time a location is visited.
In Articy, I check a condition in the input pin of the dialogue and mark it "finished" in the output pin. However, DS does this in reverse, first setting the variable to false, and then checking whether it's true – therefore no conversation is triggered.
I'm going to leave this as a known issue with earlier versions of articy:draft because I don't want to implement +/- tolerance values that may apply unintended priorities.Racoon7 wrote: ↑Fri May 25, 2018 7:20 pmConnection colours and priority
DS allows you to set link priority by changing the colour of the connections.
Unfortunately, there was a bug in articy:draft which caused all colour values and even the colour table itself to change slightly every time you saved (rounding error).
I reported it and it should be fixed in version 3.1.12, but it might still be an issue in earlier versions, and in current multi-user versions unless the new colour format is enforced, so I thought I'd let you know.
I can't reproduce this. Here is an example:Racoon7 wrote: ↑Fri May 25, 2018 7:20 pmNested groups list
First of all, Nested Conversation Groups setting is amazing! Thank you for listening to my input back then – it makes organizing my dialogue much easier.
Just a suggestion though. My conversations exported from articy:draft end up in this list:
The actual conversations are listed at the top and the groups which are inside them are all listed below.
Would it be possible to put them in the submenus where they actually belong?
It imports into the Dialogue System like this:
Is that what you're doing, too?
I'm working on incorporating this into the converter as an option.Racoon7 wrote: ↑Fri May 25, 2018 7:20 pmArticy markup
This is not a feedback per se, just a little script for anyone who might find it useful in any way.
I use italics quite a lot and didn't want to write <i>xxx</i> or emphasis tags all the time, so I wrote a script which (hopefully ) converts exported Articy:draft markup inside dialogue fragments into Unity's rich text – which should be enabled in the settings.
Accessible via right-click on an exported XML (use before attempting to convert it to a database).
It supports bold, italic and colourful text.
I wrote it in C#6 which can be turned on in Editor ► Project Settings ► Player / Scripting Runtime Version – but here's a C#4 version as well.
C#6
(Edit: Updated this to work with multiple paragraphs.)
Code: Select all
using System; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; using UnityEditor; using UnityEngine; using static System.Environment; /// <summary>Parses the Articy markup in dialogue fragments and prepares it for use in Dialogue System with rich text – which must be supported by your UI.</summary> public static class ArticyMarkupConverter { const string XmlExtension = ".xml"; const string DialogueFragmentName = "DialogueFragment"; const string TextElementName = "Text"; const string LocalizedStringName = "LocalizedString"; static string defaultNamespace; const RegexOptions Options = RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Singleline; static readonly Regex StylesRegex = new Regex(@"<style>(?<styles>.*?)</style>", Options); // Get the part of text dealing with styles static readonly Regex StyleRegex = new Regex(@"#(?<id>s[1-9]\d*) {(?<style>.*?)}", Options); // The first style "s0" is always a paragraph // Check a specific style for these static readonly Regex BoldRegex = new Regex(@"font-weight\s*?:\s*?bold", Options); static readonly Regex ItalicRegex = new Regex(@"font-style\s*?:\s*?italic", Options); static readonly Regex ColorRegex = new Regex(@"color\s*?:\s*?(?<color>#\w{6})", Options); // Apply the styles to the actual text. The style tags never overlap, so they can be processed in order. static readonly Regex TextRegex = new Regex(@"<p id=""s0"">(?<text>.*?)</p>", Options); static readonly Regex PartsRegex = new Regex(@"<span id=""(?<id>s[1-9]\d*)"">(?<text>.*?)</span>", Options); // Style id : Pure text [MenuItem("Assets/Tools/Replace Articy markup with rich text", true)] public static bool IsXmlFile() { // Validation string relativeFilePath = AssetDatabase.GetAssetPath(Selection.activeObject); return Path.GetExtension(relativeFilePath)?.ToLower() == XmlExtension; } [MenuItem("Assets/Tools/Replace Articy markup with rich text")] public static void ReplaceArticyMarkup() { string projectFolder = Application.dataPath.Remove(Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal)); string filePath = projectFolder + AssetDatabase.GetAssetPath(Selection.activeObject); XDocument articyXml = XDocument.Load(filePath); defaultNamespace = articyXml.Root?.GetDefaultNamespace().NamespaceName ?? string.Empty; var dialogueFragments = articyXml.Descendants(XName.Get(DialogueFragmentName, defaultNamespace)); // For all texts in dialogue fragments int total = 0; foreach (XElement fragment in dialogueFragments) { XElement localizedString = fragment.Element(XName.Get(TextElementName, defaultNamespace)) ?.Element(XName.Get(LocalizedStringName, defaultNamespace)); ReplaceMarkup(ref localizedString); total++; } Debug.Log($"Converted {total} dialogue fragments to rich text."); articyXml.Save(filePath); } static void ReplaceMarkup(ref XElement localizedString) { if (localizedString == null) return; string text = localizedString.Value; localizedString.Value = string.Empty; // Clear to avoid duplicating values string editedText = ConvertToRichText(text); // TODO: Check for ']]>' in the string and optionally split in in two CDATA sections ']]]]><![CDATA[>' localizedString.Add(new XCData(editedText)); // = Don't escape special characters } /// <summary>Parses given text and converts the Articy markup to rich text.</summary> static string ConvertToRichText(string dialogueLine) { dialogueLine = dialogueLine.Replace(@"'", "'"); // Apostrophe // Get styles if (!StylesRegex.IsMatch(dialogueLine)) return dialogueLine; // No styles, pure text // TODO: Purge styles from Dialogues and Entities string stylesText = StylesRegex.Match(dialogueLine).Groups["styles"].Value; var numberedStyles = StyleRegex.Matches(stylesText) .Cast<Match>() .Select(match => new { Id = match.Groups["id"].Value, Style = match.Groups["style"].Value }); var styles = numberedStyles.Select(style => new { style.Id, Bold = BoldRegex.IsMatch(style.Style), Italic = ItalicRegex.IsMatch(style.Style), Color = ColorRegex.Match(style.Style).Groups["color"].Value }); // Get texts var paragraphs = TextRegex.Matches(dialogueLine) .Cast<Match>() .Select(match => match.Groups["text"].Value); string finalDialogueLine = string.Join($"{NewLine}{NewLine}", paragraphs.Select(paragraph => { var innerTexts = PartsRegex.Matches(paragraph) .Cast<Match>() .Select(match => new { StyleId = match.Groups["id"].Value, Text = match.Groups["text"].Value }); // Apply the styles to the texts var editedParts = innerTexts.Select(text => { var currentStyle = styles.First(style => style.Id == text.StyleId); return ApplyStyle( innerText: text.Text, bold: currentStyle.Bold, italic: currentStyle.Italic, color: currentStyle.Color ); }); string editedLine = string.Join(string.Empty, editedParts); return editedLine; } )); return finalDialogueLine; } /// <summary>Wraps a given text in rich text tags.</summary> static string ApplyStyle(string innerText, bool bold, bool italic, string color) { var builder = new StringBuilder(innerText); if (bold) WrapInTag(ref builder, "b"); if (italic) WrapInTag(ref builder, "i"); if (color != string.Empty) WrapInTag(ref builder, "color", color); return builder.ToString(); } static void WrapInTag(ref StringBuilder builder, string tag, string value = "") { builder.Insert(0, (value != string.Empty) // opening tag ? $@"<{tag}={value}>" // the tag has a value : $@"<{tag}>"); // no value builder.Append($@"</{tag}>"); // closing tag } }
C#4
(Note: Unlike the C#6 version, this doesn't work when the dialogue line consists of more than one paragraph.)
Code: Select all
using System; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; using UnityEditor; using UnityEngine; /// <summary>Parses the Articy markup in dialogue fragments and prepares it for use in Dialogue System with rich text – which must be supported by your UI.</summary> public static class ArticyMarkupConverterC4 { const string XmlExtension = ".xml"; const string DialogueFragmentName = "DialogueFragment"; const string TextElementName = "Text"; const string LocalizedStringName = "LocalizedString"; const string LangAttribute = "Lang"; static string defaultNamespace; const RegexOptions Options = RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase; static readonly Regex StylesRegex = new Regex(@"<style>(?<styles>.*?)</style>", Options); // Get the part of text dealing with styles static readonly Regex StyleRegex = new Regex(@"#(?<id>s[1-9]\d*) {(?<style>.*?)}", Options); // The first style "s0" is always a paragraph // Check a specific style for these static readonly Regex BoldRegex = new Regex(@"font-weight\s*?:\s*?bold", Options); static readonly Regex ItalicRegex = new Regex(@"font-style\s*?:\s*?italic", Options); static readonly Regex ColorRegex = new Regex(@"color\s*?:\s*?(?<color>#\w{6})", Options); // Apply the styles to the actual text. The style tags never overlap, so they can be processed in order. static readonly Regex TextRegex = new Regex(@"<p id=""s0"">(?<text>.*?)</p>", Options); static readonly Regex PartsRegex = new Regex(@"<span id=""(?<id>s[1-9]\d*)"">(?<text>.*?)</span>", Options); // Style id : Pure text [MenuItem("Assets/Tools/Replace Articy markup with rich text (C#4)", true)] public static bool IsXmlFile() { // Validation string relativeFilePath = AssetDatabase.GetAssetPath(Selection.activeObject); return relativeFilePath != null && Path.GetExtension(relativeFilePath).ToLower() == XmlExtension; } [MenuItem("Assets/Tools/Replace Articy markup with rich text (C#4)")] public static void ReplaceArticyMarkup() { string projectFolder = Application.dataPath.Remove(Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal)); string filePath = projectFolder + AssetDatabase.GetAssetPath(Selection.activeObject); XDocument articyXml = XDocument.Load(filePath); defaultNamespace = (articyXml.Root != null) ? articyXml.Root.GetDefaultNamespace().NamespaceName : string.Empty; var dialogueFragments = articyXml.Descendants(XName.Get(DialogueFragmentName, defaultNamespace)); // For all texts in dialogue fragments int total = 0; foreach (XElement fragment in dialogueFragments) { var textElement = fragment.Element(XName.Get(TextElementName, defaultNamespace)); XElement localizedString = (textElement != null) ? textElement.Element(XName.Get(LocalizedStringName, defaultNamespace)) : null; ReplaceMarkup(ref localizedString); total++; } Debug.Log(string.Format("Converted {0} dialogue fragments to rich text.", total)); articyXml.Save(filePath); } static void ReplaceMarkup(ref XElement localizedString) { if (localizedString == null) return; string text = localizedString.Value; localizedString.Value = string.Empty; // Clear to avoid duplicating values string editedText = ConvertToRichText(text); localizedString.Add(new XCData(editedText)); // = Don't escape special characters } /// <summary>Parses given text and converts the Articy markup to rich text.</summary> static string ConvertToRichText(string dialogueLine) { dialogueLine = dialogueLine.Replace(@"'", "'"); // Apostrophe // Get styles if (!StylesRegex.IsMatch(dialogueLine)) return dialogueLine; // No styles, pure text string stylesText = StylesRegex.Match(dialogueLine).Value; var numberedStyles = StyleRegex.Matches(stylesText) .Cast<Match>() .Select(match => new { Id = match.Groups["id"].Value, Style = match.Groups["style"].Value }); var styles = numberedStyles.Select(style => new { style.Id, Bold = BoldRegex.IsMatch(style.Style), Italic = ItalicRegex.IsMatch(style.Style), Color = ColorRegex.Match(style.Style).Groups["color"].Value }); // Get texts var fullText = TextRegex.Match(dialogueLine).Value; // The dialogue text with <span> tags var innerTexts = PartsRegex.Matches(fullText) .Cast<Match>() .Select(match => new { StyleId = match.Groups["id"].Value, Text = match.Groups["text"].Value }); // Apply the styles to the texts var editedParts = innerTexts.Select(text => { var currentStyle = styles.First(style => style.Id == text.StyleId); return ApplyStyle( innerText: text.Text, bold: currentStyle.Bold, italic: currentStyle.Italic, color: currentStyle.Color ); }).ToArray(); string editedLine = string.Join(string.Empty, editedParts); return editedLine; } /// <summary>Wraps a given text in rich text tags.</summary> static string ApplyStyle(string innerText, bool bold, bool italic, string color) { var builder = new StringBuilder(innerText); if (bold) WrapInTag(ref builder, "b"); if (italic) WrapInTag(ref builder, "i"); if (color != string.Empty) WrapInTag(ref builder, "color", color); return builder.ToString(); } static void WrapInTag(ref StringBuilder builder, string tag, string value = "") { builder.Insert(0, (value != string.Empty) // opening tag ? string.Format(@"<{0}={1}>", tag, value) // the tag has a value : string.Format(@"<{0}>", tag)); // no value builder.Append(string.Format(@"</{0}>", tag)); // closing tag } }