Hi there!
I'm getting around to setting up some cutscenes, and after setting up my first one I've got a few questions:
1) Strings in SequencerCommands
Relying on object names in SequencerCommands wigs me out a little bit. I'm scared of changing object names later on and forgetting they're being used by dialogue and then not realizing things are broken. I get that this is a writing tool so it makes sense that things are gonna be hooked up in text rather than through references, but it is very new to me!
Maybe it's just a mindset change but do you have any recommendations on best practices since object naming is important now? Or are there other ways of sequencing cutscenes that rely on object references rather than object names?
I see that you can invoke scene events in a node, but for an action like MoveTo, where I'd want to wait to proceed until that action is done (for example: MoveTo(target, mover, 1)->Message(Next); Continue()@Message(Next);), is there a way I can do that with referenced objects instead of object names?
2) Using Empty Nodes
I want to run a cutscene sequence where a character moves from one point to another, but I don't want to show any text during it. To do that I'm running a sequence in an empty node. Is that the correct way to do that? Also, I run SetContinueMode(false); at the start of the empty node to prevent skipping while there's no text. Is there a way to default to that behavior for all empty nodes, so that I can't skip the moments where there's no text? Also I have SetContinueMode(true); as the default sequence in the DialogueSystemController.
3) Changing actor portraits and also Spine
I want to set a different portrait for one of my actors on certain lines. I've added a few more portrait sprites to his actor's definition in the database. But it looks like the SetPortrait() sequencer command expects a textureName, when I'd expect to just be able to use the desired index of the sprite.
Also - in the same way you can list multiple portrait sprites in an actor's portrait list, does integrating the Spine integration allow you to do the same thing but with Spine animations?
Cutscene questions: Getting objects by name, using empty nodes, changing portraits
Re: Cutscene questions: Getting objects by name, using empty nodes, changing portraits
Hi,
Use the special keywords and markup tags as much as possible. The most useful ones are:
The first line rotates the listener to immediately face the speaker.
The second line rotates the speaker (the default subject) to face the listener over 1 second.
The third line moves the speaker (the default subject) to the listener over 2 seconds starts at the 1-second mark.
The fourth line plays an audio clip named by the dialogue entry's entrytag through the speaker's Audio Source, at the 2-second mark.
If you only want to change the portrait image for a single dialogue entry, you can use the [pic=#] markup tag in the text instead of using a sequencer command:
or:
Yes, this was a big point of debate, but ultimately text works better for many reasons such as being able to import from other writing tools such as articy:draft and Twine.
Use the special keywords and markup tags as much as possible. The most useful ones are:
- speaker: The GameObject that's speaking the current dialogue entry. (The dialogue entry's Actor dropdown.)
- listener: The GameObject that the speaker is addressing. (The dialogue entry's Conversant dropdown.)
- speakerportrait & listenerportrait: The Portrait Image UI elements of the speaker and listener.
- [var=variable]: Markup tag that gets replaced by a DS variable's value.
- [lua(code)]: Similar to the [var] tag, but for arbitrary Lua code. (You can hook your own C# code into Lua, too.)
- entrytag: Unique identifier for dialogue entry. (See: entrytags)
Code: Select all
LookAt(speaker, listener);
LookAt(listener, , 1);
MoveTo(listener, , 2)@1;
AudioWait(entrytag)@2
The second line rotates the speaker (the default subject) to face the listener over 1 second.
The third line moves the speaker (the default subject) to the listener over 2 seconds starts at the 1-second mark.
The fourth line plays an audio clip named by the dialogue entry's entrytag through the speaker's Audio Source, at the 2-second mark.
I think you might have already solved this on Discord, but yes - a node with empty text is fine. Use HidePanel() or SetDialoguePanel(false) if you need to hide other UIs.
SetPortrait() also accepts indices. Example:
Code: Select all
SetPortrait(speaker, pic=2)
- Dialogue Text: "Haha! Great joke. [pic=2]
Code: Select all
SetPortrait(speaker, pic=[var=LaughingPic])
- Dialogue Text: "Haha! Great joke. [pic=[var=LaughingPic]]
Code: Select all
AnimatorPlay(Laughing, speakerportrait)
Yes.
Re: Cutscene questions: Getting objects by name, using empty nodes, changing portraits
Thanks so much for the thorough info! Keywords and markup tags are a great tip, I wasn't using those.
Ok good to know! I'm actually not on the Discord so I think someone else and I just had the same question! If I did need to use HIdePanel() and SetDialoguePanel(false), is there a way to set that as the default behavior for any node with empty text?I think you might have already solved this on Discord, but yes - a node with empty text is fine. Use HidePanel() or SetDialoguePanel(false) if you need to hide other UIs.
Re: Cutscene questions: Getting objects by name, using empty nodes, changing portraits
A node with empty text shouldn't open a closed subtitle panel. But if the subtitle panel is already open (e.g., set to Always From Start, Until Superceded, etc.) then a node with empty text won't close it either. If you need to automatically close a subtitle panel in this case, you could add a script with an OnConversationLine(Subtitle) method to the Dialogue Manager. If it's not the START entry (id==0) and the subtitle text is blank, hide the subtitle panel. Something like:
Code: Select all
void OnConversationLine(Subtitle subtitle)
{
if (subtitle.dialogueEntry.id > 0 && string.IsNullOrEmpty(subtitle.formattedText.text))
{
DialogueActor dialogueActor;
var panel = DialogueManager.standardDialogueUI.conversationUIElements.standardSubtitleControls.GetPanel(subtitle, out dialogueActor);
panel.Close();
}
}
Re: Cutscene questions: Getting objects by name, using empty nodes, changing portraits
Thanks! I realize my issue wasn't the closed subtitle panel getting opened - it was that I wanted the continue button to automatically deactivate (and not be waited for) during lines that contain a sequence but no text. Then, when this "purely cutscene" line is done and another text line starts, I wanted the continue button to automatically reactivate. I used your advice with OnConversationLine and put this code in a script on the Dialogue Manager:
(in case it helps anyone else with a similar issue)
(in case it helps anyone else with a similar issue)
Code: Select all
private bool _thisLineHasNoText;
void OnConversationLine(Subtitle subtitle)
{
if (subtitle.dialogueEntry.id > 0 && string.IsNullOrEmpty(subtitle.formattedText.text))
{
_thisLineHasNoText = true;
}
else
{
_thisLineHasNoText = false;
}
}
void OnSequenceStart(Transform actor)
{
if (_thisLineHasNoText)
{
_thisLineHasNoText = false;
SetContinueMode(false);
}
else
{
SetContinueMode(true);
}
}
// based on code in HandleSetContinueModeInternally() in Sequencer.cs
private void SetContinueMode(bool value)
{
DialogueManager.displaySettings.subtitleSettings.continueButton = value ? DisplaySettings.SubtitleSettings.ContinueButtonMode.Always : DisplaySettings.SubtitleSettings.ContinueButtonMode.Never;
// If a conversation is open, update its continue button mode immediately:
if (DialogueManager.conversationView != null)
{
if (DialogueManager.conversationView.displaySettings.conversationOverrideSettings != null)
{
DialogueManager.conversationView.displaySettings.conversationOverrideSettings.continueButton = DialogueManager.displaySettings.subtitleSettings.continueButton;
}
DialogueManager.conversationView.SetupContinueButton();
}
}