Godot Version
4.4.1 and Dialogue Manager 3.
Question
Hi all,
I am new to Godot, with a game idea that I try to kneade while learning. It is highly character driven, and some focus is on NPC:s talking with the player. That is, text in focus, not via audio files. I use Dialogue Manager by Nathan Hoad (v3) to handle the dialogues.
I work in 2D. To make the characters a bit more vivid, I animate the faces with eye movement, mouth movement and some more little graphical tricks. All those are made with ordinary AnimationPlayer nodes, under a AnimatedSprite2D that holds the character together. I use signals from Dialogue Manager to activate mouth animations, which liven the character up a bit. It starts and stops (almost) the mouth movement along with the stream of letters that is written in the speech balloon. But it is fairly primitive, and the mouth movement repeats in a mechanical way, that could be better.
So I wonder if anyone may help me to tweak this a bit further. My idea is to send each letter from the Dialogue Manager to the mouth animation and decide which frame should be visible.
For anyone curious on how I sync text and mouth animation, here it goes:
- In Dialogue manager, the balloon.gd:
Place this early on:
signal talkingNPC
And in the same file, I use a function focused on the audio player (that plays a short sound for each letter(, to send the signal talkingNPC. As you see, I have also checked with print(letter) to see if each letter can be signalled out of there.
func _on_dialogue_label_spoke(letter: String, letter_index: int, speed: float) → void:
print(letter)
if not letter in[" ", “,”, “.”]:
talk_sound.pitch_scale = randf_range(0.9, 1.4)
talk_sound.play()
emit_signal(“talkingNPC”)
- In dialogue_manager.gd I secured that the speech bubble works, with this:
Early in the script, just under “extends Node”:
var last_balloon: Node = null
And then, in func show_dialogue_balloon_scene, just a little tweak (most of it is identical to the original):
if balloon_scene is String:
balloon_scene = load(balloon_scene)
if balloon_scene is PackedScene:
balloon_scene = balloon_scene.instantiate()
var balloon: Node = balloon_scene
last_balloon = balloon
_start_balloon.call_deferred(balloon, resource, title, extra_game_states)
return balloon
-
The balloon with text is copied and graphically modified from one of the examples that comes with Dialogue manager. That makes it a separate scene. The text frame in there, called DialogueLabel, needs to have a node signal attached, so I use the _on_dialogue_label_spoke attached to this. That makes audio work (via an audio player of course), and is a foundation for my experiment with text to mouth movement.
-
Lastly, in my character scene (each character has its own scenes), I first made sure the standard attachments with Dialogue manager was in palce. Then I had to work around the signalling, since the dialogue balloon is not loaded in the standard tree, but is called dynamically when needed. So under func_ready(), this is placed:
await get_tree().create_timer(2.0).timeout
var resource = load(“res://dialogue/archive_dialogue1.dialogue”)
DialogueManager.show_dialogue_balloon(resource, “start”)Wait a moment for the balloon to be created
await wait_for_balloon()
var balloon = DialogueManager.last_balloon
if balloon:
balloon.connect(“talkingNPC”, Callable(self, “handleTalk”))
else:
push_error(“No balloon found!”)
After that, this:
func wait_for_balloon() → void:
var attempts := 0
while DialogueManager.last_balloon == null and attempts < 20:
await get_tree().process_frame
attempts += 1
And then, which of course need an already created animation for the mouth:
func handleTalk():
$ArchivistSprite2D/MouthSpritesheet.visible = true
$ArchivistSprite2D/Mouthanimation.play(“mouthanimation”)
This results in an animation playing while text is written out in the speech ballon. It stops as the text finishes, but if the animation is long, it will continue to the end of it. I have tried 3- 17 frames of mouth movements, which all have their pros and cons. Shorter animations is probably preferred with this technique, but as I mentioned, the repetitions make them seem mechanical. I have also tried to start the animation in different places, and also with a randomizer. I just added this row. Tried it both under and over the .play-row, with different results:
$ArchivistSprite2D/Mouthanimation.seek(randi() %17, true)
Now for the question:
I am curious to try how mouth images can be triggered by each letter in the stream from the dialogue balloon. Since I can use the readymade “letter” in func _on_dialogue_label_spoke as seen in print(letter), I would like to send it on in the emit signal to my character scene, and try it against an animation. There I would like certain letters to lead to a certain frame. But I can’t figure out how to do it. If anyone has tips, or tweaks on what I have done so far, it is most welcome.
/Pelle