Only add NPC's name to their first sequential node?

Announcements, support questions, and discussion for the Dialogue System.
dantuom
Posts: 18
Joined: Fri May 06, 2022 10:06 am

Only add NPC's name to their first sequential node?

Post by dantuom »

Hello,

Is there a way to have an NPC's name only appear in front of the first sequential node they "speak"?

I'm trying to achieve an effect whereby an NPC can deliver a long monologue broken into paragraph sized chunks, with their name only appearing at the top of the very first one. A good example would be this scene in Disco Elysium where the character Joyce Messier speaks several clearly separated sequential paragraphs, but her name only appears before the first one:
Screenshot 2022-08-01 at 14.16.52.png
Screenshot 2022-08-01 at 14.16.52.png (866.14 KiB) Viewed 1093 times
These paragraphs each appear fully formed, as if they were using a version of the Typewriter Effect that distinguished by paragraph rather than character.

I'm not using a continue button but otherwise I'd like it to work the same, ie:
-The first paragraph appears in its entirety
-Then (after an amount of time determined by the Dialogue System Controller's Subtitle Chars per second) the next one appears in its entirety
-etc.

To achieve this I first tried disabling the Typewriter Effect on my Subtitle Text game object, then inserted pipe characters into the monologue.
Screenshot 2022-08-01 at 13.54.51.png
Screenshot 2022-08-01 at 13.54.51.png (86.77 KiB) Viewed 1093 times
Screenshot 2022-08-01 at 13.55.45.png
Screenshot 2022-08-01 at 13.55.45.png (93.72 KiB) Viewed 1093 times
Which just results in all the text appearing at once, albeit formatted. Meaning the player would still have to scroll this massive dense wall of text.
pipes unsplit.png
pipes unsplit.png (930.4 KiB) Viewed 1093 times

When I hit Split Pipes into Nodes > Process Conversation I achieved the timing I wanted, but now the NPC's name appears at the start of each line of dialogue.
pipes into nodes.png
pipes into nodes.png (996.72 KiB) Viewed 1093 times
This looks kind of clumsy, and will be problematic later because some of my NPCs have spatially formatted text and the indent caused by the name will disrupt it.

Further details:
I'm using the WRPG Template Standard Dialogue UI.
I've switched off "Add Speaker Name" in the Standard UI Subtitle Panel, in favour of having this be controlled by a Dialogue Actor component on each NPC. This lets me change the name colours, have the Narrator's name not appear at all, etc.

Also. incidentally:
I've found I can achieve a kind of "hacked" version of this by turning the Typewriter Effect back on, making the Characters Per Second extremely high, and replacing the pipe characters in the dialogue editor text with "\.". However this feels a little janky, is hard to time precisely (eg with longer paragraphs the per-character typewriter effect is still noticeable), and generally feels like an unsatisfactory solution.

Thanks in advance for any assistance.
User avatar
Tony Li
Posts: 21973
Joined: Thu Jul 18, 2013 1:27 pm

Re: Only add NPC's name to their first sequential node?

Post by Tony Li »

Hi,

To show text paragraph by paragraph, make each paragraph a separate node in your conversation. Remove the typewriter effect.

To add the NPC's name only to their first sequential node, UNtick the options on the Dialogue Actor and subtitle panels to prepend the actor's name, if either of those options are ticked. Instead, add an OnConversationLine() method to your Dialogue Manager, or to your dialogue UI if it's in the Dialogue Manager's hierarchy. In OnConversationLine(), check if a new NPC is speaking. If so, prepend the actor name. Example:

Code: Select all

private string currentNPCName;

public void OnConversationStart(Transform actor) { currentNPCName = ""; }

public void OnConversationLine(Subtitle subtitle)
{
    if (subtitle.speakerInfo.isNPC && !string.Equals(subtitle.speakerInfo.Name, currentNPCName))
    {
        currentNPCName = subtitle.speakerInfo.Name;
        subtitle.formattedText.text = $"{currentNPCName} - {subtitle.formattedText.text}";
    }
}
dantuom
Posts: 18
Joined: Fri May 06, 2022 10:06 am

Re: Only add NPC's name to their first sequential node?

Post by dantuom »

Thanks Tony, this makes sense.

I've spent a few days on and off tinkering with it, but have been running into problems with how to make each NPC name have its own colour etc. So I think I'll make the compromise and have their names appear at the start of each paragraph instead.

But thanks for the solution, it helped me weigh up my options.
User avatar
Tony Li
Posts: 21973
Joined: Thu Jul 18, 2013 1:27 pm

Re: Only add NPC's name to their first sequential node?

Post by Tony Li »

Hi,

Just move that script to the NPC GameObjects instead, and add a color variable. Something like:

Code: Select all

using UnityEngine;
using PixelCrushers.DialogueSystem;

public class PrependNPCName : MonoBehaviour
{
    public Color myColor = Color.white;
    
    private static string currentNPCName;

    public void OnConversationStart(Transform actor) { currentNPCName = ""; }

    public void OnConversationLine(Subtitle subtitle)
    {
        // If I'm speaking and I haven't shown my name yet, prepend it to the text:
        if (subtitle.speakerInfo.isNPC && subtitle.speakerInfo.transform == this.transform &&
            !string.Equals(subtitle.speakerInfo.Name, currentNPCName))
    {
        currentNPCName = subtitle.speakerInfo.Name;
        subtitle.formattedText.text = $"{UITools.WrapTextInColor(currentNPCName, myColor)} - {subtitle.formattedText.text}";
    }
}
dantuom
Posts: 18
Joined: Fri May 06, 2022 10:06 am

Re: Only add NPC's name to their first sequential node?

Post by dantuom »

Thank you!

I took a few weeks off worrying about the UI so that I could focus on writing some actual dialogue, but have just come back and tried this script. It works! With three snags:

1. When the NPC and player trade dialogue lines the NPC's name appears at the top of only their first line in the entire dialogue, rather than their first line each time they speak. In the image below, the text in the more elaborate font is being spoken by the NPC Sand Gremlin. As you can see, their name is listed only the very first time they speak.
Screenshot 2022-08-24 at 16.31.21.png
Screenshot 2022-08-24 at 16.31.21.png (679.71 KiB) Viewed 1049 times
Whereas it should flow as follows:
‘Sand Gremlin: Greet yes
You: I've been told to find my home
Sand Gremlin: home
te find a home’ etc

2. The paragraphs appear one by one as intended, but in the absence of the typewriter effect I can't find any way to make them autoscroll. So the player is left looking at the first paragraph unless they scroll down themselves. Is there another way to make the scroll "keep up" with the text?

3. The colon separating the Actor Name from their text has been replaced by a hyphen. I had set it to a colon using the option in Dialogue Actor. I'm pretty sure I remember changing this from elsewhere in the Dialogue Manager before I was using the Dialogue Actor component, but can't now find/remember where that is.

Finally, this is not a snag to the existing code, but I wonder if similar functionality could be added to control the font of the body text as has been done to the Actor Name color? For now I've been handling the Sand Gremlin's special font on a node-by-node basis, as in:
Screenshot 2022-08-24 at 16.09.54.png
Screenshot 2022-08-24 at 16.09.54.png (59.37 KiB) Viewed 1049 times
but it would be very nice if this could be attached to every NPC game object.
Thanks once more for all your help Tony!
User avatar
Tony Li
Posts: 21973
Joined: Thu Jul 18, 2013 1:27 pm

Re: Only add NPC's name to their first sequential node?

Post by Tony Li »

Hi,
dantuom wrote: Wed Aug 24, 2022 10:46 am1. When the NPC and player trade dialogue lines the NPC's name appears at the top of only their first line in the entire dialogue, rather than their first line each time they speak.
Change this line:

Code: Select all

if (subtitle.speakerInfo.isNPC && subtitle.speakerInfo.transform == this.transform &&
to this:

Code: Select all

if (subtitle.speakerInfo.transform == this.transform &&
dantuom wrote: Wed Aug 24, 2022 10:46 am2. The paragraphs appear one by one as intended, but in the absence of the typewriter effect I can't find any way to make them autoscroll. So the player is left looking at the first paragraph unless they scroll down themselves. Is there another way to make the scroll "keep up" with the text?
See the updated script below.
dantuom wrote: Wed Aug 24, 2022 10:46 am3. The colon separating the Actor Name from their text has been replaced by a hyphen. I had set it to a colon using the option in Dialogue Actor. I'm pretty sure I remember changing this from elsewhere in the Dialogue Manager before I was using the Dialogue Actor component, but can't now find/remember where that is.
See the updated script below. The script adds the hyphen.
dantuom wrote: Wed Aug 24, 2022 10:46 amFinally, this is not a snag to the existing code, but I wonder if similar functionality could be added to control the font of the body text as has been done to the Actor Name color?
Yes. The updated script below includes changes for the things you mentioned above, plus it adds a Font Name field. If you set this field to any value (e.g., "Luminari SDF"), it will wrap that actor's text in font codes.

Code: Select all

using System.Collections;
using UnityEngine;
using PixelCrushers.DialogueSystem;
using UnityEngine.UI;

public class PrependName : MonoBehaviour
{
    public bool prependName = true;
    public Color myColor = Color.white;
    public string fontName = string.Empty;

    private static string currentNPCName;

    private ScrollRect scrollRect;

    public void OnConversationStart(Transform actor) { currentNPCName = ""; }

    public void OnConversationLine(Subtitle subtitle)
    {
        if (subtitle.speakerInfo.transform == this.transform)
        {
            // Wrap dialogue text in font: (Move below next section if you also want name to use font.)
            if (!string.IsNullOrEmpty(fontName))
            {
                subtitle.formattedText.text = $"<font=\"{fontName}\">{subtitle.formattedText.text}</font>";
            }

            // If we just switched to this actor, prepend its name:
            if (!string.Equals(subtitle.speakerInfo.Name, currentNPCName))
            {
                currentNPCName = subtitle.speakerInfo.Name;
                if (prependName)
                {
                    subtitle.formattedText.text = $"{UITools.WrapTextInColor(currentNPCName, myColor)} - {subtitle.formattedText.text}";
                }
            }

            // Scroll to bottom at end of frame:
            StartCoroutine(ScrollToBottomAtEndOfFrame());
        }
    }

    IEnumerator ScrollToBottomAtEndOfFrame()
    {
        yield return new WaitForEndOfFrame();
        if (scrollRect == null)
        {
            scrollRect = DialogueManager.standardDialogueUI.GetComponentInChildren<ScrollRect>();
        }
        if (scrollRect != null)
        {
            scrollRect.verticalNormalizedPosition = 0;
        }
    }
}
EDIT: Added "prependName" bool. Renamed script to "PrependName".
dantuom
Posts: 18
Joined: Fri May 06, 2022 10:06 am

Re: Only add NPC's name to their first sequential node?

Post by dantuom »

That's brilliant, enormous thanks as ever Tony!
User avatar
Tony Li
Posts: 21973
Joined: Thu Jul 18, 2013 1:27 pm

Re: Only add NPC's name to their first sequential node?

Post by Tony Li »

Happy to help!
dantuom
Posts: 18
Joined: Fri May 06, 2022 10:06 am

Re: Only add NPC's name to their first sequential node?

Post by dantuom »

Tony I realise you've already put a lot of energy into this issue so if you don't have time to deal with this I understand.

But I thought I'd just leave one more comment here because I've just noticed one odd behaviour resulting from a fairly niche interaction which (unfortunately) comes up quite frequently. Maybe if another forum user happens to read this and can see the hole in the logic they can point it out.

I noticed that the solution only works if my player character also has the PrependNPCName script attached to them, which is fine. Everything works with the script attached to the player, even though they don't strictly need it as they never speak two nodes in a row.

However: I have one narrator NPC who appears in nearly every conversation, whose name should never be displayed, so I did not initially attach the script to them. However this led to a disruption in the flow, where after the narrator spoke the next NPC to speak would also not have their name displayed.

I tried assigning the PrependNPCName script to the narrator NPC, and giving the actor a blank Display Name, but this didn't work as I guess (sensibly enough) if the string is blank it reverts to the Actor name.

Eventually I wrote a custom DontPrependNPCName script just for that narrator NPC, essentially the same except one change to the line that assigns currentNPCName in the OnConversationLine() method, so instead of:

Code: Select all

if (!string.Equals(subtitle.speakerInfo.Name, currentNPCName))
            {
                currentNPCName = subtitle.speakerInfo.Name;
                subtitle.formattedText.text = $"{UITools.WrapTextInColor(currentNPCName, myColor)} {subtitle.formattedText.text}";
            }
I have

Code: Select all

if (!string.Equals(subtitle.speakerInfo.Name, currentNPCName))
            {
                currentNPCName = "";
                subtitle.formattedText.text = $"{currentNPCName}{subtitle.formattedText.text}";
            }
For a while I thought this was working perfectly, as seen here:
Screenshot 2022-08-25 at 14.05.31.png
Screenshot 2022-08-25 at 14.05.31.png (478.93 KiB) Viewed 1035 times
Until I came up against the specific case when the player character speaks directly after the narrator, which for some reason once again breaks the flow:
Screenshot 2022-08-25 at 14.07.38.png
Screenshot 2022-08-25 at 14.07.38.png (258.42 KiB) Viewed 1035 times
Screenshot 2022-08-25 at 14.58.31.png
Screenshot 2022-08-25 at 14.58.31.png (517.94 KiB) Viewed 1035 times
As you can see, when the player speaks directly after the narrator, "You:" is not displayed.
When the player speaks directly after another NPC with the PrependNPCName script—or when that NPC speaks directly after the narrator—it works perfectly.

I can't understand why this is the case. What is the functional difference between the player character and the other NPC?
User avatar
Tony Li
Posts: 21973
Joined: Thu Jul 18, 2013 1:27 pm

Re: Only add NPC's name to their first sequential node?

Post by Tony Li »

For consistency, I recommend using the PrependNPCName script on all actors. (Create an empty GameObject for the Narrator if it doesn't have one.) I just edited my previous post above to include a "prependName" bool that will add a checkbox to the inspector. You can untick this for the Narrator.

You might also want to rename the script to something like "PrependName" instead of PrependNPCName since it can be used for all actors.

When it comes to showing subtitles, there isn't much difference between NPCs and player actors. The only difference is if the current conversation state only has player nodes (i.e., blue nodes), in which case it will show a response menu.
Post Reply