[HOWTO] How To: Color Actor Name and Indent Text

Announcements, support questions, and discussion for the Dialogue System.
User avatar
Tony Li
Posts: 22886
Joined: Thu Jul 18, 2013 1:27 pm

[HOWTO] How To: Color Actor Name and Indent Text

Post by Tony Li »

This script is a replacement for StandardUISubtitlePanel. It does the following:
  • Prepends the speaker's name using the actor's Node Color as set in the Dialogue Editor window. (You must set the actor's Node Color.)
  • Indents the text after the actor's name.
  • Combines sequential lines spoken by the same actor into the same paragraph instead of separating them with a newline.
  • Sets older lines' alpha color values to semitransparent.
It requires using TextMesh Pro, and you will probably need to fix a bug in TextMesh Pro as described in How To: Indent Text.

Code: Select all

using UnityEngine;
using PixelCrushers.DialogueSystem;

public class ContinueParagraphSubtitlePanel : StandardUISubtitlePanel
{

    private int previousSpeakerID;
    private bool firstChange;
    private string originalWebColor;

    public override void OnConversationStart(Transform actor)
    {
        base.OnConversationStart(actor);
        previousSpeakerID = -1;
        addSpeakerName = false; // We'll do it manually.
        firstChange = true;
    }

    protected override void Awake()
    {
        base.Awake();
        originalWebColor = Tools.ToWebColor(subtitleText.color);
    }

    protected override void SetSubtitleTextContent(Subtitle subtitle)
    {
        // Change old text <color=#rrggbbff> tags to alpha aa: // Change "aa" to a lower hex number if you want darker.
        accumulatedText = accumulatedText.Replace("ff>", "aa>");

        if (subtitle.speakerInfo.id != previousSpeakerID &&
            !string.IsNullOrEmpty(subtitle.formattedText.text))
        {
            // This is a new speaker, so prepend actor name and indent:

            // Prepend actor name with color:
            var actor = DialogueManager.masterDatabase.GetActor(subtitle.speakerInfo.id);
            if (actor != null)
            {
                var nodeColor = DialogueLua.GetActorField(actor.Name, "NodeColor").asString;
                if (!string.IsNullOrEmpty(nodeColor))
                {
                    var richTextColor = Tools.ToWebColor(NodeColorStringToColor(nodeColor));
                    var closeIndent = (previousSpeakerID != -1) ? "</indent>\n" : (firstChange && previousSpeakerID != -1 ? "\n" : "");
                    if (previousSpeakerID != -1 && firstChange)
                    {
                        closeIndent += "\n";
                        firstChange = false;
                    }
                    subtitle.formattedText.text = $"{closeIndent}<color={richTextColor}>{subtitle.speakerInfo.Name}: </color><color={originalWebColor}><indent=10%> {subtitle.formattedText.text}</color>";
                }
            }
            base.SetSubtitleTextContent(subtitle);
        }
        else
        {
            // If the same speaker, add to paragraph.
            // This code is similar to the base SetContent() except it doesn't
            // add a line break.
            TypewriterUtility.StopTyping(subtitleText);
            var previousText = accumulateText ? accumulatedText : string.Empty;
            subtitle.formattedText.text = $"<color={originalWebColor}>{subtitle.formattedText.text}</color>";
            if (accumulateText && !string.IsNullOrEmpty(subtitle.formattedText.text))
            {
                if (numAccumulatedLines < maxLines)
                {
                    numAccumulatedLines += (1 + NumCharOccurrences('\n', subtitle.formattedText.text));
                }
                else
                {
                    // If we're at the max number of lines, remove the first line from the accumulated text:
                    previousText = RemoveFirstLine(previousText);
                }
            }
            var previousChars = accumulateText ? UITools.StripRPGMakerCodes(Tools.StripTextMeshProTags(Tools.StripRichTextCodes(previousText))).Length : 0;
            SetFormattedText(subtitleText, previousText, subtitle);
            if (accumulateText) accumulatedText = UITools.StripRPGMakerCodes(subtitleText.text);
            if (scrollbarEnabler != null && !HasTypewriter())
            {
                scrollbarEnabler.CheckScrollbarWithResetValue(0);
            }
            else if (delayTypewriterUntilOpen && !hasFocus)
            {
                DialogueManager.instance.StartCoroutine(StartTypingWhenFocused(subtitleText, subtitleText.text, previousChars));
            }
            else
            {
                TypewriterUtility.StartTyping(subtitleText, subtitleText.text, previousChars);
            }
        }

        previousSpeakerID = subtitle.speakerInfo.id;
    }

    public static Color NodeColor_Orange = new Color(1f, 0.5f, 0);
    public static Color NodeColor_Gray = new Color(0.9f, 0.9f, 0.9f);
    public static Color NodeColor_Blue = new Color(0.4f, 0.6f, 1f);
    public static Color NodeColor_Green = new Color(0, 1f, 0);
    public static Color NodeColor_Red = new Color(1f, 0.1f, 0.1f);

    private Color NodeColorStringToColor(string s)
    {
        switch (s)
        {
            case "Aqua":
                return Color.cyan;
            case "Blue":
                return NodeColor_Blue;
            case "Gray":
                return NodeColor_Gray;
            case "Green":
                return NodeColor_Green;
            case "Grey":
                return Color.gray;
            case "Orange":
                return NodeColor_Orange;
            case "Red":
                return NodeColor_Red;
            case "Yellow":
                return Color.yellow;
            default:
                return Tools.WebColor(s);
        }
    }
}
User avatar
Tony Li
Posts: 22886
Joined: Thu Jul 18, 2013 1:27 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by Tony Li »

If you want to do this and gray old text, here's another script and example:

DS_ContinueParagraph_2024-04-09.unitypackage
Eldritch_Horror
Posts: 28
Joined: Sun May 28, 2023 8:30 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by Eldritch_Horror »

Where does this C# script go/attach as component?
Apologies if this is a daft question.
User avatar
Tony Li
Posts: 22886
Joined: Thu Jul 18, 2013 1:27 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by Tony Li »

Hi,

It's a replacement for the StandardUISubtitlePanel component. For example, if you're starting with a copy of the Scrolling Dialogue UI prefab, you'd replace the Subtitle Panel Info GameObject's StandardUISubtitlePanel with this subclass. To replace it in-place and retain UI element assignments, see How To: Replace Script with Subclass and Keep Field Assignments.
mind creates
Posts: 3
Joined: Wed Apr 17, 2024 10:30 am

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by mind creates »

I am receiving a console error (outlined below) when following the workflow outlined here:
For example, if you're starting with a copy of the Scrolling Dialogue UI prefab, you'd replace the Subtitle Panel Info GameObject's StandardUISubtitlePanel with this subclass. To replace it in-place and retain UI element assignments, see How To: Replace Script with Subclass and Keep Field Assignments.
Console Error:
Assets/Test/ContinueParagraphSubtitlePanel.cs(25,29): error CS0115: 'ContinueParagraphSubtitlePanel.SetSubtitleTextContent(Subtitle)': no suitable method found to override


Notes:
  • TextMesh Pro is installed.
  • I copy/pasted the provided code from this thread and received the error.
  • I did assume user error on my part, so I downloaded the example package (it contains the error also).
  • (I haven't ruled out user error, but may need to be pointed in the correct direction.)

Code: Select all

using UnityEngine;
using PixelCrushers.DialogueSystem;

public class ContinueParagraphSubtitlePanel : StandardUISubtitlePanel
{

    private int previousSpeakerID;
    private bool firstChange;
    private string originalWebColor;

    public override void OnConversationStart(Transform actor)
    {
        base.OnConversationStart(actor);
        previousSpeakerID = -1;
        addSpeakerName = false; // We'll do it manually.
        firstChange = true;
    }

    protected override void Awake()
    {
        base.Awake();
        originalWebColor = Tools.ToWebColor(subtitleText.color);
    }

    protected override void SetSubtitleTextContent(Subtitle subtitle)
    {
        // Change old text <color=#rrggbbff> tags to alpha aa: // Change "aa" to a lower hex number if you want darker.
        accumulatedText = accumulatedText.Replace("ff>", "aa>");

        if (subtitle.speakerInfo.id != previousSpeakerID &&
            !string.IsNullOrEmpty(subtitle.formattedText.text))
        {
            // This is a new speaker, so prepend actor name and indent:

            // Prepend actor name with color:
            var actor = DialogueManager.masterDatabase.GetActor(subtitle.speakerInfo.id);
            if (actor != null)
            {
                var nodeColor = DialogueLua.GetActorField(actor.Name, "NodeColor").asString;
                if (!string.IsNullOrEmpty(nodeColor))
                {
                    var richTextColor = Tools.ToWebColor(NodeColorStringToColor(nodeColor));
                    var closeIndent = (previousSpeakerID != -1) ? "</indent>\n" : (firstChange && previousSpeakerID != -1 ? "\n" : "");
                    if (previousSpeakerID != -1 && firstChange)
                    {
                        closeIndent += "\n";
                        firstChange = false;
                    }
                    subtitle.formattedText.text = $"{closeIndent}<color={richTextColor}>{subtitle.speakerInfo.Name}: </color><color={originalWebColor}><indent=10%> {subtitle.formattedText.text}</color>";
                }
            }
            base.SetSubtitleTextContent(subtitle);
        }
        else
        {
            // If the same speaker, add to paragraph.
            // This code is similar to the base SetContent() except it doesn't
            // add a line break.
            TypewriterUtility.StopTyping(subtitleText);
            var previousText = accumulateText ? accumulatedText : string.Empty;
            subtitle.formattedText.text = $"<color={originalWebColor}>{subtitle.formattedText.text}</color>";
            if (accumulateText && !string.IsNullOrEmpty(subtitle.formattedText.text))
            {
                if (numAccumulatedLines < maxLines)
                {
                    numAccumulatedLines += (1 + NumCharOccurrences('\n', subtitle.formattedText.text));
                }
                else
                {
                    // If we're at the max number of lines, remove the first line from the accumulated text:
                    previousText = RemoveFirstLine(previousText);
                }
            }
            var previousChars = accumulateText ? UITools.StripRPGMakerCodes(Tools.StripTextMeshProTags(Tools.StripRichTextCodes(previousText))).Length : 0;
            SetFormattedText(subtitleText, previousText, subtitle);
            if (accumulateText) accumulatedText = UITools.StripRPGMakerCodes(subtitleText.text);
            if (scrollbarEnabler != null && !HasTypewriter())
            {
                scrollbarEnabler.CheckScrollbarWithResetValue(0);
            }
            else if (delayTypewriterUntilOpen && !hasFocus)
            {
                DialogueManager.instance.StartCoroutine(StartTypingWhenFocused(subtitleText, subtitleText.text, previousChars));
            }
            else
            {
                TypewriterUtility.StartTyping(subtitleText, subtitleText.text, previousChars);
            }
        }

        previousSpeakerID = subtitle.speakerInfo.id;
    }

    public static Color NodeColor_Orange = new Color(1f, 0.5f, 0);
    public static Color NodeColor_Gray = new Color(0.9f, 0.9f, 0.9f);
    public static Color NodeColor_Blue = new Color(0.4f, 0.6f, 1f);
    public static Color NodeColor_Green = new Color(0, 1f, 0);
    public static Color NodeColor_Red = new Color(1f, 0.1f, 0.1f);

    private Color NodeColorStringToColor(string s)
    {
        switch (s)
        {
            case "Aqua":
                return Color.cyan;
            case "Blue":
                return NodeColor_Blue;
            case "Gray":
                return NodeColor_Gray;
            case "Green":
                return NodeColor_Green;
            case "Grey":
                return Color.gray;
            case "Orange":
                return NodeColor_Orange;
            case "Red":
                return NodeColor_Red;
            case "Yellow":
                return Color.yellow;
            default:
                return Tools.WebColor(s);
        }
    }
}
[/list]
User avatar
Tony Li
Posts: 22886
Joined: Thu Jul 18, 2013 1:27 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by Tony Li »

Hi,

Are you using a very old version of the Dialogue System? Can you back up your project and update to the current version?
willoneill
Posts: 18
Joined: Sun Oct 25, 2015 4:06 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by willoneill »

Hi Tony (or anybody else who might be able to help!),

Sorry for raising this thread from the dead, but thought it might be helpful since it concerns the exact script discussed here (ContinueParagraphSubtitlePanel.cs) that I've downloaded and implemented.

All the name color, indent and grey-fade-out features are working perfectly (thank you!) and I've made a few other small tweaks (lower indent %, swapping the colon after the name for an em-dash, etc.) but there are a few things that don't work quite how they should - and I can't figure out why.

Will post each with screenshots.

Issue 1 - Weird Gap After First Line (And only first line?)

Here's a screenshot - https://ibb.co/x8JVQ275 - this extra space only seems to happen after the first line. I've double-checked that there isn't an extra line break in the content or something like that. As you can see, after the first line the spacing goes back to being regular / nice.

Issue 2 - No Spacing At All After Player Choice

Here are screenshots - https://ibb.co/hxv6KM4f > https://ibb.co/vvZNLzVq - As you can see, there are two choices. After I select one, it displays that choice (working as intended) it only does a single line break, it doesn't treat it as a new line. Is this because the Player actor isn't technically an 'actor'? Is there a way to indicate that they are the player but still treat them as an actor, have it show their name, etc.?

Issue 3 - No Spacing On New Conversation

Here's a screenshot - https://ibb.co/w2DjdkG - in the options, I've got 'Clear Text On Conversation Start' deselected since this is the way I want it, but it isn't displaying correctly. In the screenshot, the 'Actor One' line at the end is the result of me restarting the conversation for a second time. The window opens again and continues on as it should, but two problems with how it looks: It doesn't create space from the end of the previous conversation, and the actor name is now indenting as well.

Sorry for tossing all three of these at once, but it's so close to perfect...! Thanks again for this great asset and awesome prefabs.

EDIT: I'm also bridging DS with Adventure Creator in case that matters, but in this case I'm not sure why it would.
User avatar
Tony Li
Posts: 22886
Joined: Thu Jul 18, 2013 1:27 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by Tony Li »

Hi,

Bridging with AC shouldn't have any issue here.
willoneill wrote: Tue Jan 28, 2025 4:35 pmIssue 1 - Weird Gap After First Line (And only first line?)
If you reproduce this in the Unity editor's play mode and inspect the Subtitle Text GameObject, what does the raw text of the TextMeshProUGUI component's Text field look like? Is it adding an extra line break? (Maybe the script is incorrectly adding two line breaks on conversation start.)

willoneill wrote: Tue Jan 28, 2025 4:35 pmIssue 2 - No Spacing At All After Player Choice
I assume it's because the script attempts to continue text into the same paragraph if it's the same actor. Is Actor One the player?
willoneill wrote: Tue Jan 28, 2025 4:35 pmIssue 3 - No Spacing On New Conversation
Try adding an OnConversationStart(Transform) method to your script, and add spacing in it.
willoneill
Posts: 18
Joined: Sun Oct 25, 2015 4:06 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by willoneill »

Tony Li wrote: Tue Jan 28, 2025 7:56 pmIf you reproduce this in the Unity editor's play mode and inspect the Subtitle Text GameObject, what does the raw text of the TextMeshProUGUI component's Text field look like? Is it adding an extra line break? (Maybe the script is incorrectly adding two line breaks on conversation start.)
Fixed! I found a rogue \n on line 54 that was causing it - hopefully deleting that doesn't cause something unintended, but it seems alright. Thanks!
Tony Li wrote: Tue Jan 28, 2025 7:56 pmI assume it's because the script attempts to continue text into the same paragraph if it's the same actor. Is Actor One the player?
Actor One was not the player, but as it turns out this was being caused by the player character not having the same metadata (text color / portrait / display name) as the other, non-player actors. Doing that somehow seemed to fix the problem, though I'm not sure why.
Tony Li wrote: Tue Jan 28, 2025 7:56 pmTry adding an OnConversationStart(Transform) method to your script, and add spacing in it.
This worked! This is what I added to the existing OnConversationStart method to add the space and preserve the proper indenting in case anyone is interested:

Code: Select all

if (subtitleText != null)
    {
        // Remove any existing indent tags that might be affecting the header
        accumulatedText = accumulatedText.Replace("<indent=5%>", "").Replace("</indent>", "");
        accumulatedText = accumulatedText + "\n";
        subtitleText.text = accumulatedText;
    }
Thanks again for your help and suggestions, Tony! I'm all good now.
User avatar
Tony Li
Posts: 22886
Joined: Thu Jul 18, 2013 1:27 pm

Re: [HOWTO] How To: Color Actor Name and Indent Text

Post by Tony Li »

Glad to help!
Post Reply