Page 1 of 1

Run OnConversationStart and scripts for Conversant only

Posted: Mon Dec 05, 2022 3:55 pm
by Jamish
EDIT: I am mistaken about the problems; please see follow-up reply!

------------------------------


Hey,

I've got a generic NPC.cs script that I reuse between a lot of NPC prefabs. The NPC.cs script is attached to my "NPC Prefab" GameObject. I want each NPC to be able to detect when the player is talking to them (to activate their Cinemachine camera, turn to face the player, etc). I also have animation triggers set up to play animations for various emotions (Neutral, Happy, Sad) that I want to trigger in specific conversation nodes.

1. I defined some OnConversationStart and OnConversationEnd public methods in NPC.cs, and wired them up via a Dialogue System Events component on the NPC Prefab GameObject.
2. I defined some Lua script handlers using Templates>Scripts>TemplateCustomLua, following your tutorial on YouTube (Dialogue System for Unity 2.x - Connecting Your C# Methods to Lua), and call a script like Neutral() or Happy() in various conversation nodes.

The problem I am running into is that when I talk to one NPC, the OnConversationStart and emotion scripts execute for ALL NPCs, rather than just the Conversant.

I already have a workaround for setting the emotions using a Scene Event (instead of a Script) to point to the specific scene instance of the NPC Prefab GameObject to call the proper method, but that require more work because I have to assign the GameObject for every node that I want to trigger an event.

I already have the Conversation Conversant is set to the parent NPC Prefab GameObject transform in the Dialogue System Trigger. One solution might be to get filter calls in NPC.cs by getting the current conversation's conversant, and check if Conversation Conversant == transform.

Is there a better way to achieve this? Thanks!

-----------

Here's how I wired up OnConversationStart and OnConversationEnd (the view is my NPC prefab hierarchy):
Screenshot 2022-12-05 123829.png
Screenshot 2022-12-05 123829.png (63.03 KiB) Viewed 204 times
And here's the relevant code from NPC.cs:

Code: Select all


    public void OnConversationStart() {
        print("Conversation started");
        closeupCamera.SetActive(true);
    }

    public void OnConversationEnd() {
        print("Conversation ended");
        closeupCamera.SetActive(false);
    }

    public void SetEmotion(Emotion emotion) {
        print("Setting emotion!");
        animator.SetTrigger(emotion.ToString());
    }

    #region Register with Lua
    void OnEnable() {
        Lua.RegisterFunction("Neutral", this, SymbolExtensions.GetMethodInfo(() => SetEmotionNeutral()));
        Lua.RegisterFunction("Sad", this, SymbolExtensions.GetMethodInfo(() => SetEmotionSad()));
    }

    void OnDisable() {
        Lua.UnregisterFunction("Neutral");
        Lua.UnregisterFunction("Sad");
    }

    public void SetEmotionNeutral() {
        SetEmotion(Emotion.Neutral);
    }

    public void SetEmotionSad() {
        SetEmotion(Emotion.Sad);
    }
    #endregion

Re: Run OnConversationStart and scripts for Conversant only

Posted: Mon Dec 05, 2022 6:55 pm
by Jamish
Ah, looks like I'm mistaken about BOTH of my questions.

First, my OnConversationStart is not being called on all NPCs. I was hooking the event up wrong. It turns out the OnConversationStart handler was being double-invoked for my NPC. I only had two NPCs in the scene, and I was seeing two "Conversation Started" debug messages, so I mistakenly thought it was being called for all NPCs. It looks like I don't actually have to manually wire up the OnConversationStart/OnConversationEnd functions. I removed the handlers from the Dialogue System Events component, but kept the component itself. It looks like Dialogue System is automatically calling my OnConversationStart/OnConversationEnd functions in NPC.cs only once now.

Second, it looks like my custom Lua-registered functions aren't being invoked on all NPCs, they're actually just being invoked on one specific NPC. My guess is that each NPC's OnEnable will overwrite the previously-registered Lua function handlers. I think I can get around this issue by having a single NPCController script that registers the emotion functions once. Within NPCController, I can look up the target NPC using

Code: Select all

DialogueManager.currentConversant
and call that NPC's SetEmotion function.

I think that answers all my questions. Sorry to waste anyone's time!

Re: Run OnConversationStart and scripts for Conversant only

Posted: Mon Dec 05, 2022 7:05 pm
by Tony Li
Yes, you're absolutely correct on both counts. The Dialogue System Events component will only run its events on the Dialogue Manager and the two GameObjects being used as the conversation's participants. It won't run on other GameObjects. (See Character GameObject Assignments for more info about which GameObjects are used.)

And Lua functions are always registered globally. You can't register the same function name (e.g., Neutral()) on two different GameObjects/script instances. Here are some other ways you could implement it:

- Register the Lua functions globally (e.g., in a single global script, not on every NPC). In the functions, use the Animator component that's on DialogueManager.currentConversant:

Code: Select all

void Neutral()
{
    DialogueManager.currentConversant.GetComponent<Animator>().SetTrigger("Neutral");
}
- Or, instead of Lua functions, use a custom sequencer command:

Code: Select all

public class SequencerCommandEmotion : SequencerCommand
{
    void Awake()
    {
        // Syntax: Emotion(emotionTrigger, [subject]) -- use speaker if subject is omitted.
        string emotionTrigger = GetParameter(0);
        Transform subject = GetSubject(1, speaker);
        subject.GetComponent<Animator>().SetTrigger(emotionTrigger); // Should really check for null, etc.
        Stop();
    }
}
- Or put the emotion in the dialogue text, such as "That's great news! [happy]". Use an OnConversationLine method to extract the emotion tag ("[happy]") and set the trigger:

Code: Select all

void OnConversationLine(Subtitle subtitle)
{
    // Just an example. You'll want to do this in a more general way.
    if (subtitle.formattedText.text.Contains("[happy]"))
    {
        subtitle.formattedText.text = subtitle.formattedText.text.Replace("[happy]", "");
        subtitle.speakerInfo.transform.GetComponent<Animator>().SetTrigger("happy");
    }
}
(If you're satisfied with your current solution, please continue with that. I just finished typing this up as you made your last post, so I figured I'd post this anyway. :-) )

Re: Run OnConversationStart and scripts for Conversant only

Posted: Mon Dec 05, 2022 7:40 pm
by Jamish
Thanks for the reply. I like your solution using OnConversationLine! I think I'll try that one out.

Thanks again!