diff --git a/src/ui/decorations/text_effects/typewriter/typewriter_effect.gd b/src/ui/decorations/text_effects/typewriter/typewriter_effect.gd index 4c876db..f266b00 100644 --- a/src/ui/decorations/text_effects/typewriter/typewriter_effect.gd +++ b/src/ui/decorations/text_effects/typewriter/typewriter_effect.gd @@ -5,14 +5,18 @@ extends RichTextEffect ## Tag params: ## - speed - Speed at which text is displayed, in characters per second. ## - delay - Delay before displaying first character, in seconds. +## - factor - Transition animation timescale factor. signal typing -signal on_frame_process_start + +@export var scale_curve: Curve +@export var translation_curve: CurveXYZTexture # To use this effect: # - Enable BBCode on a RichTextLabel. # - Instead of instantiating this effect directly, use a `TypewriterLabel` node. -# - Use [type speed=10.0 delay=0.0]hello[/type] in text. +# - Animation curves are exported by `TypewriterLabel`. +# - Use [type speed=10.0 delay=0.0 factor=1.0]hello[/type] in text. var bbcode: String = "type" var _force_visible := false @@ -27,10 +31,40 @@ func _process_custom_fx(char_fx: CharFXTransform) -> bool: if not _force_visible: var speed: float = char_fx.env.get("speed", Game.settings.default_text_speed) var delay: float = char_fx.env.get("delay", 0.0) + var factor: float = char_fx.env.get("factor", 1.0) - char_fx.visible = (char_fx.elapsed_time - delay) * speed >= char_fx.relative_index + var server := TextServerManager.get_primary_interface() + var glyph_size := server.font_get_glyph_size(char_fx.font, Vector2i.ONE, char_fx.glyph_index) + var pivot := glyph_size * Vector2(-1, 1) - if not char_fx.visible: + var rel_time := ( + (speed * (char_fx.elapsed_time - delay) - char_fx.relative_index) / factor + ) + + var scale := Vector2.ONE + if scale_curve: + scale *= scale_curve.sample_baked(rel_time) + + var translation := Vector2.ZERO + if translation_curve: + if translation_curve.curve_x: + translation.x = translation_curve.curve_x.sample_baked(rel_time) + if translation_curve.curve_y: + translation.y = translation_curve.curve_y.sample_baked(rel_time) + + char_fx.transform = ( + char_fx + . transform + . translated_local(-pivot) + . scaled_local(scale) + . translated_local(pivot) + . translated_local(translation) + ) + + + char_fx.visible = rel_time > 0 + + if rel_time < 1: typing.emit() return true diff --git a/src/ui/decorations/text_effects/typewriter/typewriter_label.gd b/src/ui/decorations/text_effects/typewriter/typewriter_label.gd index 3490b50..ec91f8a 100644 --- a/src/ui/decorations/text_effects/typewriter/typewriter_label.gd +++ b/src/ui/decorations/text_effects/typewriter/typewriter_label.gd @@ -11,15 +11,28 @@ signal typing_finished ## Useful for advancing skippable dialogue boxes. signal force_visible +## Scale curve for animated typewriter character transition. +@export var scale_curve: Curve + +## Translation curve for animated typewriter character transition. +## The Z curve will not be used. +@export var translation_curve: CurveXYZTexture + var _finished: bool = true var _typing: bool = false +@onready var _current_text: String = text -func _init() -> void: + +func _ready() -> void: bbcode_enabled = true var effect := TypewriterEffect.new(force_visible) + effect.scale_curve = scale_curve + effect.translation_curve = translation_curve effect.typing.connect(_on_typing) install_effect(effect) + if text: + _finished = false ## Is the typewriter effect finished? @@ -33,12 +46,17 @@ func reset() -> void: _finished = false -## Clear the text box and set a new line of text. +## Restart the existing typing effect. +func restart() -> void: + display_text(_current_text) + + ## The `finished` signal will be emitted when the text is done displaying. -func display_text(text: String) -> void: +func display_text(new_text: String) -> void: reset() clear() - append_text(text) + append_text(new_text) + _current_text = new_text func _process(_delta: float) -> void: