Page 1 of 2
Dialogue System 1.8.2 Released!
Posted: Mon May 21, 2018 5:08 pm
by Tony Li
Version 1.8.2 is now available on the Pixel Crushers customer download site and should be on the Unity Asset Store in a few days.
Version 2.0 is on track for release next Monday.
This is intended to be the last release in the 1.x line. Highlights are:
- Emerald AI support
- More than 4 emphasis tags
- Multiedit fields in dialogue nodes
Version 1.8.2
Core
- Improved: Can now multiedit fields when selecting multiple dialogue entry nodes.
- Improved: Can now define more than 4 emphasis tags.
- Improved: Unity UI Dialogue UI animation handler improvements.
- Improved: MoveTo() with no duration now moves rigidbody, not just transform.
- Fixed: Bug with Continue() at time 0 not clearing previous subtitle.
- Fixed: Build bug with Timeline() sequencer command.
Third Party Support
- Adventure Creator: Start Conversation action can now specify an entry ID.
- articy:draft: Added support for getProp() and getObj() functions.
- Corgi Platformer / Inventory Engine: Inventory panel now refreshed when replacing items during conversations.
- Emerald AI 2.0: Added support.
- Inventory Third Person Controller: Updated for Invector 2.3.0.
- PlayMaker: GetLocalizedText now falls back to Dialogue Manager's text table.
Re: Dialogue System 1.8.2 Released!
Posted: Tue May 22, 2018 10:25 am
by Abelius
Great! I was looking forward to this one.
Thank you for your work!
Re: Dialogue System 1.8.2 Released!
Posted: Fri May 25, 2018 7:20 pm
by Racoon7
Thank you for the update!
I look forward to version 2.0. Finally I won't have to assign actors and conversants all the time, right?
And If you're still looking for feedback on the current version, I noticed a few things.
Ever since I use Dialogue System, the typewriter sound effect always imports like this for me:
Perhaps other people don't have this problem, but I generally have to delete the file to avoid errors.
In scenes
without a Dialogue Manager, these warnings pop up in playmode (entering and quitting).
I might not always want to have a DM in a scene.
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.
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.
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, i.e. like this or similarly?
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.
(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
}
}
(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
}
}
Re: Dialogue System 1.8.2 Released!
Posted: Fri May 25, 2018 8:40 pm
by Tony Li
Wow, thank you for all those details, @Racoon7! I'll try to make sure they're all fixed / included in 2.0.
Re: Dialogue System 1.8.2 Released!
Posted: Sat May 26, 2018 9:39 am
by Tony Li
@Racoon7 - I have a question about this one:
In scenes without a Dialogue Manager, these warnings pop up in playmode (entering and quitting).
"Dialogue System: Quest Log couldn't access Lua Item[] table..."
What is trying to access the Quest Log? Would you please paste the full stack trace for that warning? (Click on the line in the Console window, press Ctrl+C, then paste into a reply post.)
Re: Dialogue System 1.8.2 Released!
Posted: Sat May 26, 2018 4:32 pm
by nathanj
@Tony and @ Racoon7
Is that because there is a docked Dialogue Editor? I believe I’ve had this message before and that was the reason. Removing the editor removes the message, but a way to have the editor always present with or without the dialogue Manager in the active scene would be ideal.
Nathan
Re: Dialogue System 1.8.2 Released!
Posted: Sat May 26, 2018 4:50 pm
by Tony Li
nathanj wrote: ↑Sat May 26, 2018 4:32 pmIs that because there is a docked Dialogue Editor? I believe I’ve had this message before and that was the reason. Removing the editor removes the message, but a way to have the editor always present with or without the dialogue Manager in the active scene would be ideal.
Nathan
Good point! The Watches tab can report this warning since it can't access the quests that it wants to watch. I'll make sure that's addressed.
Re: Dialogue System 1.8.2 Released!
Posted: Sat May 26, 2018 5:13 pm
by nathanj
Ah, the Watches. Of course. That makes total sense.
Re: Dialogue System 1.8.2 Released!
Posted: Sun May 27, 2018 5:35 am
by Racoon7
Oh yeah,
@nathanj is absolutely right, it only happens when there's a docked Dialogue Editor window.
Here's the stack trace if you still need it.
Short
Code: Select all
Dialogue System: Quest Log couldn't access Lua Item[] table. Has the Dialogue Manager loaded a database yet?
UnityEngine.Debug:LogWarning(Object)
PixelCrushers.DialogueSystem.QuestLog:GetAllQuests(QuestState, Boolean, String)
PixelCrushers.DialogueSystem.QuestLog:GetAllQuests(QuestState)
PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:CatalogueWatchableQuestNames()
PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:RefreshWatchableQuestList()
PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:ResetWatchSection()
PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:HandlePlayModeStateChanged()
PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:OnPlaymodeStateChanged()
UnityEditor.EditorApplication:Internal_PlayModeStateChanged(PlayModeStateChange)
Full
Code: Select all
Dialogue System: Quest Log couldn't access Lua Item[] table. Has the Dialogue Manager loaded a database yet?
0x00000001414F00A9 (Unity) StackWalker::GetCurrentCallstack
0x00000001414F66B6 (Unity) StackWalker::ShowCallstack
0x000000014138F54B (Unity) GetStacktrace
0x0000000140888ADC (Unity) DebugStringToFile
0x0000000141F886A8 (Unity) DebugLogHandler_CUSTOM_Internal_Log
0x000000003FA942A0 (Mono JIT Code) (wrapper managed-to-native) UnityEngine.DebugLogHandler:Internal_Log (UnityEngine.LogType,string,UnityEngine.Object)
0x000000003FA94173 (Mono JIT Code) [DebugLogHandler.cs:10] UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
0x000000003FA939F5 (Mono JIT Code) [Logger.cs:48] UnityEngine.Logger:Log (UnityEngine.LogType,object)
0x000000003FA9368C (Mono JIT Code) [Debug.bindings.cs:149] UnityEngine.Debug:LogWarning (object)
0x000000003FA9203B (Mono JIT Code) PixelCrushers.DialogueSystem.QuestLog:GetAllQuests (PixelCrushers.DialogueSystem.QuestState,bool,string)
0x000000003FA91A5B (Mono JIT Code) PixelCrushers.DialogueSystem.QuestLog:GetAllQuests (PixelCrushers.DialogueSystem.QuestState)
0x000000003FA91883 (Mono JIT Code) PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:CatalogueWatchableQuestNames ()
0x000000003FA916E3 (Mono JIT Code) PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:RefreshWatchableQuestList ()
0x000000003FA58833 (Mono JIT Code) PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:ResetWatchSection ()
0x000000003FA586C3 (Mono JIT Code) PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:HandlePlayModeStateChanged ()
0x000000003FA58323 (Mono JIT Code) PixelCrushers.DialogueSystem.DialogueEditor.DialogueEditorWindow:OnPlaymodeStateChanged ()
0x000000003FA581D1 (Mono JIT Code) [EditorApplication.cs:281] UnityEditor.EditorApplication:Internal_PlayModeStateChanged (UnityEditor.PlayModeStateChange)
0x000000003EE28DDE (Mono JIT Code) (wrapper runtime-invoke) <Module>:runtime_invoke_void_int (object,intptr,intptr,intptr)
0x00007FFA1707A1AB (mono-2.0-bdwgc) [mini-runtime.c:2809] mono_jit_runtime_invoke
0x00007FFA17001A42 (mono-2.0-bdwgc) [object.c:2915] do_runtime_invoke
0x00007FFA1700AA2F (mono-2.0-bdwgc) [object.c:2962] mono_runtime_invoke
0x0000000140BD4CF7 (Unity) CallStaticMonoMethod
0x0000000140BD4A7E (Unity) CallStaticMonoMethod
0x00000001413C6770 (Unity) PlayerLoopController::SetIsPlaying
0x00000001413CA05D (Unity) Application::TickTimer
0x00000001415C42ED (Unity) MainMessageLoop
0x00000001415C65DC (Unity) WinMain
0x000000014271FDA7 (Unity) __scrt_common_main_seh
0x00007FFA568B1FE4 (KERNEL32) BaseThreadInitThunk
0x00007FFA5940F061 (ntdll) RtlUserThreadStart
Re: Dialogue System 1.8.2 Released!
Posted: Sun May 27, 2018 8:37 am
by Tony Li
Thanks! I'll fix this in the next release so the Watches tab doesn't try to catalogue the available quests if there's no Dialogue Manager.