Added dialogs with subtitles

This commit is contained in:
SchimmelSpreu83 2026-03-17 00:18:19 +01:00
parent 26e707f0e1
commit 478c859d33
14 changed files with 298 additions and 1 deletions

View File

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://bu3mcagsgxth"
path="res://.godot/imported/Deadbird_Alright_listen_here.mp3-3cc73b7ad94c9ac3c61368e7a863ddc9.mp3str"
[deps]
source_file="res://_development/ayuroo/voicelines/conductor/Deadbird_Alright_listen_here.mp3"
dest_files=["res://.godot/imported/Deadbird_Alright_listen_here.mp3-3cc73b7ad94c9ac3c61368e7a863ddc9.mp3str"]
[params]
loop=false
loop_offset=0.0
bpm=0.0
beat_count=0
bar_beats=4

View File

@ -0,0 +1,10 @@
[gd_resource type="Resource" script_class="DialogEntry" format=3 uid="uid://ju7es8xi1gpc"]
[ext_resource type="Script" uid="uid://crem3vn3eyf38" path="res://src/core/dialog/dialog_entry.gd" id="1_41r8w"]
[resource]
script = ExtResource("1_41r8w")
voiceline_path = "uid://bu3mcagsgxth"
subtitle_strings = PackedStringArray("Alright listen here, lass.", "Yer in big trouble.", "If yer helpin\' crooked", "[wave]D[/wave]", "[wave]DJ[/wave]", "[wave]DJ Gro[/wave]", "[wave]DJ Grooves[/wave]", "rig the awards-,", "And I can\'t take ya to jail,", "then yer got to help me even the score.")
subtitle_timings = PackedFloat32Array(0, 2.6, 4.2, 6, 6.5, 7, 7.35, 8, 9.6, 11)
metadata/_custom_type_script = "uid://crem3vn3eyf38"

View File

@ -0,0 +1,41 @@
@tool
class_name DialogEntry
extends Resource
@export_file("*.tres", "*.res", "*.ogg", "*.wav", "*.mp3")
var voiceline_path: String
@export var precache_vo_stream: bool = false
@export var subtitle_strings: PackedStringArray = []
@export var subtitle_timings: PackedFloat32Array = []
var _precached_stream: AudioStream
func _init() -> void:
if precache_vo_stream and not Engine.is_editor_hint():
_precached_stream = load(voiceline_path)
func get_voiceline_stream() -> AudioStream:
return _precached_stream if precache_vo_stream else load(voiceline_path)
func get_subtitle_string_from_elapsed_time(elapsed_time: float) -> String:
var subtitle_string: String = ""
for time_index: int in range(subtitle_timings.size()):
if subtitle_timings.get(time_index) >= elapsed_time:
break
subtitle_string = subtitle_strings.get(time_index)
return subtitle_string
# old (search from reverse)
#var timings: PackedFloat32Array = subtitle_timings.duplicate()
#timings.reverse()
#for time_index: int in range(timings.size()):
#var time: float = timings.get(time_index)
#if time <= elapsed_time:
#return subtitle_strings.get((timings.size() - time_index) - 1)

View File

@ -0,0 +1 @@
uid://crem3vn3eyf38

View File

@ -0,0 +1,13 @@
class_name DialogEntryPlayback
var dialog_entry: DialogEntry
var playback_position: float = 0.0
func _init(entry: DialogEntry) -> void:
dialog_entry = entry
func get_dialog_subtitle() -> String:
return dialog_entry.get_subtitle_string_from_elapsed_time(playback_position)

View File

@ -0,0 +1 @@
uid://byt7cdieik05m

View File

@ -0,0 +1,91 @@
class_name DialogPlayer
extends Node
signal dialog_finished(dialog_key: StringName)
static var active_dialog_playbacks: Array[DialogEntryPlayback] = []
@export_node_path("AudioStreamPlayer", "AudioStreamPlayer2D", "AudioStreamPlayer3D")
var audio_player: NodePath: set = set_audio_player
@export var dialogs: Dictionary[StringName, DialogEntry] = {}
var _audio_player: Node
var _current_dialog_key: StringName
var _current_dialog_playback: DialogEntryPlayback
func _ready() -> void:
set_process(false)
set_audio_player(audio_player)
func _process(_delta: float) -> void:
if is_instance_valid(_audio_player) and is_instance_valid(_current_dialog_playback):
_current_dialog_playback.playback_position = _audio_player.get_playback_position()
func set_audio_player(audio_player_path: NodePath) -> void:
if is_instance_valid(_audio_player):
_audio_player.finished.disconnect(_on_dialog_finished)
audio_player = audio_player_path
_audio_player = get_node_or_null(audio_player)
if not is_instance_valid(_audio_player):
set_process(false)
return
if (
not _audio_player.has_method(&"play")
or not _audio_player.has_method(&"get_playback_position")
or not &"playing" in _audio_player
or not _audio_player.has_signal(&"finished")
):
_audio_player = null
push_error("audio_player is not a valid player.")
return
set_process(_audio_player.playing)
if not _audio_player.finished.is_connected(_on_dialog_finished):
_audio_player.finished.connect(_on_dialog_finished)
func play(entry_key: StringName, from_position: float = 0.0) -> void:
var dialog_entry: DialogEntry = dialogs.get(entry_key)
if is_instance_valid(dialog_entry):
stop_dialog()
_audio_player.stream = dialog_entry.get_voiceline_stream()
_current_dialog_key = entry_key
_current_dialog_playback = DialogEntryPlayback.new(dialog_entry)
active_dialog_playbacks.append(_current_dialog_playback)
_audio_player.play(from_position)
set_process(true)
func get_current_dialog_entry() -> DialogEntry:
return dialogs.get(_current_dialog_key)
func get_playback_position() -> float:
return _audio_player.get_playback_position() if is_instance_valid(_audio_player) else 0.0
func stop_dialog() -> void:
active_dialog_playbacks.erase(_current_dialog_playback)
_current_dialog_key = &""
_current_dialog_playback = null
_audio_player.stream = null
func _on_dialog_finished() -> void:
var entry: DialogEntry = dialogs.get(_current_dialog_key)
stop_dialog()
set_process(false)
dialog_finished.emit(entry)

View File

@ -0,0 +1 @@
uid://blxophwhpim0l

View File

@ -14,6 +14,7 @@
[ext_resource type="Script" uid="uid://dyoa3tnirv7wh" path="res://src/core/camera/camera_effect_target.gd" id="7_ghuot"] [ext_resource type="Script" uid="uid://dyoa3tnirv7wh" path="res://src/core/camera/camera_effect_target.gd" id="7_ghuot"]
[ext_resource type="Script" uid="uid://gvuvxikepb2r" path="res://src/gameplay/characters/player/hud.gd" id="11_y2ktv"] [ext_resource type="Script" uid="uid://gvuvxikepb2r" path="res://src/gameplay/characters/player/hud.gd" id="11_y2ktv"]
[ext_resource type="Texture2D" uid="uid://cwvfdx2v41k0h" path="res://src/ui/hud/crosshair.png" id="13_qx1cj"] [ext_resource type="Texture2D" uid="uid://cwvfdx2v41k0h" path="res://src/ui/hud/crosshair.png" id="13_qx1cj"]
[ext_resource type="PackedScene" uid="uid://ct2u3f03lwsnr" path="res://src/ui/hud/hud_subtitle.tscn" id="15_hqe5n"]
[sub_resource type="Resource" id="Resource_vq0uu"] [sub_resource type="Resource" id="Resource_vq0uu"]
script = ExtResource("2_1ixuj") script = ExtResource("2_1ixuj")
@ -289,4 +290,5 @@ scale = Vector2(0.25, 0.25)
sprite_frames = SubResource("SpriteFrames_p81y6") sprite_frames = SubResource("SpriteFrames_p81y6")
animation = &"to_hollow" animation = &"to_hollow"
frame = 5 frame = 5
frame_progress = 1.0
[node name="HudSubtitle" parent="Hud" unique_id=123148710 instance=ExtResource("15_hqe5n")]

View File

@ -0,0 +1,12 @@
class_name HudSubtitle
extends Control
@onready var rich_text_label: RichTextLabel = %RichTextLabel
func _process(_delta: float) -> void:
var parts: PackedStringArray = []
for playback: DialogEntryPlayback in DialogPlayer.active_dialog_playbacks:
parts.append(playback.get_dialog_subtitle())
rich_text_label.text = "\n".join(parts)

View File

@ -0,0 +1 @@
uid://ssyyrfvcmird

View File

@ -0,0 +1,37 @@
[gd_scene format=3 uid="uid://ct2u3f03lwsnr"]
[ext_resource type="Script" uid="uid://ssyyrfvcmird" path="res://src/ui/hud/hud_subtitle.gd" id="1_07tug"]
[node name="HudSubtitle" type="Control" unique_id=123148710]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
script = ExtResource("1_07tug")
metadata/_custom_type_script = "uid://ssyyrfvcmird"
[node name="MarginContainer" type="MarginContainer" parent="." unique_id=441028605]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="RichTextLabel" type="RichTextLabel" parent="MarginContainer" unique_id=1994554868]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2
theme_override_constants/outline_size = 4
bbcode_enabled = true
text = "Example subtitle"
horizontal_alignment = 1
vertical_alignment = 2

View File

@ -0,0 +1,68 @@
[gd_scene format=3 uid="uid://bqx6t4agunl7h"]
[ext_resource type="PackedScene" uid="uid://ct2u3f03lwsnr" path="res://src/ui/hud/hud_subtitle.tscn" id="1_koq4j"]
[ext_resource type="Script" uid="uid://blxophwhpim0l" path="res://src/core/dialog/dialog_player.gd" id="2_4qqmy"]
[ext_resource type="Script" uid="uid://crem3vn3eyf38" path="res://src/core/dialog/dialog_entry.gd" id="3_j86jr"]
[ext_resource type="Resource" uid="uid://ju7es8xi1gpc" path="res://_development/ayuroo/voicelines/conductor/test_dialog.tres" id="4_pnxnv"]
[sub_resource type="GDScript" id="GDScript_j86jr"]
script/source = "extends Control
@export var dialog_key: StringName
@export_range(0.0, 1.0, 0.001, \"or_greater\", \"suffix:s\") var from_position: float = 0.0
@export_range(0.0, 1.0, 0.001, \"or_greater\", \"suffix:s\") var inital_delay: float = 0.5
@onready var dialog_player: DialogPlayer = $DialogPlayer
@onready var replay_button: Button = %ReplayButton
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
replay_button.pressed.connect(_on_replay_button_pressed)
await get_tree().create_timer(inital_delay).timeout
_on_replay_button_pressed()
func _on_replay_button_pressed() -> void:
dialog_player.play(dialog_key, from_position)
"
[node name="DialogTestScene" type="Control" unique_id=1728136061]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_j86jr")
dialog_key = &"listen_here"
[node name="HudSubtitle" parent="." unique_id=123148710 instance=ExtResource("1_koq4j")]
layout_mode = 1
[node name="ReplayButton" type="Button" parent="." unique_id=283603298]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -30.0
offset_top = -15.5
offset_right = 30.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
text = "Replay"
[node name="DialogPlayer" type="Node" parent="." unique_id=928993916]
script = ExtResource("2_4qqmy")
audio_player = NodePath("AudioStreamPlayer")
dialogs = Dictionary[StringName, ExtResource("3_j86jr")]({
&"listen_here": ExtResource("4_pnxnv")
})
metadata/_custom_type_script = "uid://blxophwhpim0l"
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="DialogPlayer" unique_id=1284459]