Trying to skip animation with Input or Button?

Godot Version

4.4

Hello, I am new to coding, and I am attempting to make some dialogue for my game. The functionality I want is for it to both be animated dialogue, so it animates as it types on screen, and advances when I click a button, which I’ve already found a tutorial for.

This is the tutorial I followed for that:

What I would like to do now, is to have the ability to use either my input (i.e. left clicking on my mouse, which I’ve already set up), or my button to skip the animated dialogue section to show all of the dialogue at once. Then be able to click again and advance it to the next bit of dialogue.

I feel like this is a very common function in most video games, I know I certainly use it when I feel like the typewriter style animation is too slow. Which is why I’d like to have it implemented. But I just can’t seem to find any tutorials for this specific kind of dialogue option. I would really appreciate any help, thank you so much in advance.

This is my current code, and the specific problem area that I’m trying to get working. I figured this would have worked, but I tested this input with print() functions, and it doesnt seem to be even registering my input.

extends Control


@onready var start = $Start as Button
@export var start_game = preload("res://Scenes/Contest Scene/contest_v_01.tscn") as PackedScene

var _message_index = 0
@export var text_lines : Array[String]


func _ready() -> void:
	start.button_down.connect(on_start_pressed)
	
	$Start.hide()
	$Panel/MarginContainer/Panel/MarginContainer/Advance_Text.hide()
	%Label.text = text_lines[_message_index]
	$Scrolling_Text.play("Intro_Dialogue")
	
	if Input.is_action_just_pressed("CONTINUE") and $Scrolling_Text.is_playing():
		$Scrolling_Text.seek(.9, true)
	else:
		await $Scrolling_Text.animation_finished
	$Panel/MarginContainer/Panel/MarginContainer/Advance_Text.show()


func next_message() -> void:
	_message_index += 1
	
	if %Label.text == "My name is Barry, I am a Performand breeder, and I will be your guide in this wonderful world.":
		$AnimationPlayer.play("new_animation")
		await $AnimationPlayer.animation_finished
	else:
		pass
	
	if _message_index >= text_lines.size():
		$Panel/MarginContainer/Panel/MarginContainer/Advance_Text.hide()
		$Start.show()
	else:
		%Label.text = text_lines[_message_index]
		$Scrolling_Text.play("Intro_Dialogue")
		await $Scrolling_Text.animation_finished
		$Panel/MarginContainer/Panel/MarginContainer/Advance_Text.show()


func _on_next_button_pressed():
	$Panel/MarginContainer/Panel/MarginContainer/Advance_Text.hide()
	next_message()

func on_start_pressed() -> void:
	get_tree().change_scene_to_packed(start_game)

The problem area:

if Input.is_action_just_pressed("CONTINUE") and $Scrolling_Text.is_playing():
		$Scrolling_Text.seek(.9, true)
	else:
		await $Scrolling_Text.animation_finished

My attempt at print functions:

	if Input.is_action_just_pressed("CONTINUE") and $Scrolling_Text.is_playing():
		$Scrolling_Text.seek(.9, true)
		print("True")
	else:
		await $Scrolling_Text.animation_finished
		print("false")

This only prints the false, so my Input doesn’t seem to be registering at all?

One thing that jumps out immediately is that your ‘problem area’ code is all inside the _ready() method, which only runs once, when the node is first instantiated. I imagine you actually want it in func _process(delta), or in func _input(event).

1 Like

no. that is not a problem. he’s just doing it wrong.

	if Input.is_action_just_pressed("CONTINUE") and $Scrolling_Text.is_playing():
		$Scrolling_Text.seek(.9, true)
	else:
		await $Scrolling_Text.animation_finished

this will only run once, it has to be in _input or use a signal.
you should also understand that ready is executed before the other nodes. but if you use await, the node will be paused until the signal is emitted. so the solution is to break your code into more nodes, this will also allow you to change behaviours by swapping nodes and to extend this functionality.

signal resume#we create a custom signal

func _ready() -> void:
	#... the rest of the code here ...
	$Scrolling_text.animation_finished.connect(animation_ended)#run animation_ended when animation finishes. we can add the optional parameter CONNECT_ONE_SHOT to disconnect the signal after one use
	await resume#wait for resume to run

func animation_ended() -> void:
	resume.emit()

use a different node for input, put it as child of your other node:
input_listener.gd

extends Node

func _input(event) -> void:
	if event.is_action_pressed("CONTINUE"):
		$"../Scrolling_text".seek(.9, true)#sibling node scrolling_text
		get_parent().resume.emit()
1 Like

I’m a little confused - I know I didn’t go into much detail with my response, because I intended it as a starting point, but I did say that his ‘problem code’ should probably be in func _input(event), and then you said no, that is not a problem, then proceeded to give advice that ends with putting the exact code I was referencing into func _input(event). Can you tell me what you meant in your response to my post?

3 Likes

yeah, sorry, I didn’t phrase it right. he’s using await on a thing that must run only once, so it must go in _ready.
for UI you need to use signals, which you can connect. usually we connect buttons to functions that do things.
In this case he wanted to press a key to do a thing, and that does need _input, so putting code in _input is good, but not _process.
because the key press could be replaced with a Button node press like one that says “more”, and in that case a function that does the seek and then emits the signal could be easily implemented.
putting code that waits for a key in process would prevent new features from being implemented and make code less clear.

so, imagine that he adds a button “more” that can be pressed that does the same as pressing CONTINUE, we would just add some code to the input_listener:

extends Node

func _input(event) -> void:
	if event.is_action_pressed("CONTINUE"):
		seek_text()

func _ready() -> void:
	$"../more_button".pressed.connect(seek_text)

func seek_text():
	$"../Scrolling_text".seek(.9, true)
	get_parent().resume.emit()

if this game were to be played on a device with no keyboard, like a phone, we could remove the _input and have the game be controlled with buttons. in that case, any code in process could become problematic.

1 Like

Thank you everyone for answering, I moved it into a func _process(delta) and it works now! I really appreciate the help!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.