From 881a08308cc6198ff53952011b2cd22a85aa6aa6 Mon Sep 17 00:00:00 2001 From: William Edwards Date: Thu, 30 Mar 2023 23:57:05 -0700 Subject: [PATCH] fix(boxart): Run fetching of boxart in its own io thread and implement transitions in visibility manager --- core/global/boxart_manager.gd | 9 ++ core/systems/state/visibility_manager.gd | 67 ++++++++++- core/systems/threading/io_thread.tres | 8 ++ core/ui/components/transition_container.gd | 2 + core/ui/components/transition_slide_down.tscn | 112 ++++++++++++++++++ core/ui/menu/home/home_menu.gd | 8 +- core/ui/menu/home/home_menu.tscn | 19 +-- core/ui/menu/launch/game_launch_menu.gd | 10 +- core/ui/menu/launch/game_launch_menu.tscn | 9 +- 9 files changed, 222 insertions(+), 22 deletions(-) create mode 100644 core/systems/threading/io_thread.tres create mode 100644 core/ui/components/transition_slide_down.tscn diff --git a/core/global/boxart_manager.gd b/core/global/boxart_manager.gd index f2594f97..0d97d33f 100644 --- a/core/global/boxart_manager.gd +++ b/core/global/boxart_manager.gd @@ -20,6 +20,7 @@ class_name BoxArtManager ## [/codeblock] const SettingsManager := preload("res://core/global/settings_manager.tres") +const io_thread = preload("res://core/systems/threading/io_thread.tres") ## Fields required to be set by [BoxArtProvider] implementations const REQUIRED_FIELDS: Array = ["provider_id"] @@ -44,8 +45,16 @@ var _providers: Dictionary = {} var _providers_by_priority: Array = [] +func _init() -> void: + io_thread.start() + + ## Returns the boxart of the given kind for the given library item. func get_boxart(item: LibraryItem, kind: BoxArtProvider.LAYOUT) -> Texture2D: + return await io_thread.exec(_get_boxart_sync.bind(item, kind)) + + +func _get_boxart_sync(item: LibraryItem, kind: BoxArtProvider.LAYOUT) -> Texture2D: if _providers.is_empty(): logger.error("No box art providers were found!") return null diff --git a/core/systems/state/visibility_manager.gd b/core/systems/state/visibility_manager.gd index 388298e5..ecf6617a 100644 --- a/core/systems/state/visibility_manager.gd +++ b/core/systems/state/visibility_manager.gd @@ -2,6 +2,11 @@ extends Node class_name VisibilityManager +signal transition_started +signal transition_finished +signal entered +signal exited + # State machine to use @export var state_machine: StateMachine = preload( "res://assets/state/state_machines/global_state_machine.tres" @@ -9,7 +14,8 @@ class_name VisibilityManager @export var state: State @export var visible_during: Array[Resource] = [] -var logger := Log.get_logger("VisibilityManager") +var _transitions: Array[Transition] = [] +var logger := Log.get_logger("VisibilityManager", Log.LEVEL.INFO) @onready var _parent := get_parent() as CanvasItem @@ -17,6 +23,23 @@ var logger := Log.get_logger("VisibilityManager") func _ready() -> void: state_machine.state_changed.connect(_on_state_changed) visible_during.push_front(state) + + # Handle visibility transitions + var children := get_children() + for child in children: + if not child is Transition: + continue + + var transition := child as Transition + if not transition.has_animation(transition.enter_animation): + logger.warn("Transition {0} doesn't have enter animation {1}".format([transition.name, transition.enter_animation])) + continue + if not transition.has_animation(transition.exit_animation): + logger.warn("Transition {0} doesn't have exit animation {1}".format([transition.name, transition.exit_animation])) + continue + + _transitions.append(transition) + transition.root_node = "../.." func _on_state_changed(_from: State, to: State) -> void: @@ -31,13 +54,51 @@ func _transition(visibility: bool) -> void: _parent.z_index = state_machine.stack().find(state) # If the parent doesn't have any transitions, flip visibility - if not _parent.has_node("TransitionContainer"): + if not has_transitions(): _parent.visible = visibility return - # Use transitions if they exist + # Prefer transitions that are children of visibilitymanager + if _transitions.size() > 0: + if visibility: + enter() + return + exit() + return + + # Use transition containers if they exist var transition := _parent.get_node("TransitionContainer") as TransitionContainer if visibility: transition.enter() return transition.exit() + + +func has_transitions() -> bool: + if _transitions.size() > 0 or _parent.has_node("TransitionContainer"): + return true + return false + + +func enter() -> void: + for transition in _transitions: + transition.play(transition.enter_animation) + transition_started.emit() + + for transition in _transitions: + var anim = await transition.animation_finished + logger.debug("Finished playing: " + anim) + transition_finished.emit() + entered.emit() + + +func exit() -> void: + for transition in _transitions: + transition.play(transition.exit_animation) + transition_started.emit() + + for transition in _transitions: + var anim = await transition.animation_finished + logger.debug("Finished playing: " + anim) + transition_finished.emit() + exited.emit() diff --git a/core/systems/threading/io_thread.tres b/core/systems/threading/io_thread.tres new file mode 100644 index 00000000..49904b7f --- /dev/null +++ b/core/systems/threading/io_thread.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="SharedThread" load_steps=2 format=3 uid="uid://ka4mdlcnbil3"] + +[ext_resource type="Script" path="res://core/systems/threading/shared_thread.gd" id="1_qvlys"] + +[resource] +script = ExtResource("1_qvlys") +name = "IOThread" +target_tick_rate = 30 diff --git a/core/ui/components/transition_container.gd b/core/ui/components/transition_container.gd index df8cf892..0e896452 100644 --- a/core/ui/components/transition_container.gd +++ b/core/ui/components/transition_container.gd @@ -1,6 +1,8 @@ extends Node class_name TransitionContainer +## DEPRECATED - Add transitions to VisibilityManager instead + signal transition_started signal transition_finished signal entered diff --git a/core/ui/components/transition_slide_down.tscn b/core/ui/components/transition_slide_down.tscn new file mode 100644 index 00000000..f5b35e0d --- /dev/null +++ b/core/ui/components/transition_slide_down.tscn @@ -0,0 +1,112 @@ +[gd_scene load_steps=6 format=3 uid="uid://61bw8lmqymry"] + +[ext_resource type="Script" path="res://core/ui/components/transition.gd" id="1_86nlo"] + +[sub_resource type="Animation" id="Animation_d13hk"] +resource_name = "enter" +length = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0.1, 0.3), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Vector2(0, -500), Vector2(0, 0)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.1, 0.5), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:visible") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0.1), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} + +[sub_resource type="Animation" id="Animation_qrxbs"] +resource_name = "exit" +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:visible") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} + +[sub_resource type="Animation" id="Animation_5ykde"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(0, 0)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_igt3d"] +_data = { +"RESET": SubResource("Animation_5ykde"), +"enter": SubResource("Animation_d13hk"), +"exit": SubResource("Animation_qrxbs") +} + +[node name="SlideTransition" type="AnimationPlayer"] +root_node = NodePath("../..") +libraries = { +"": SubResource("AnimationLibrary_igt3d") +} +script = ExtResource("1_86nlo") diff --git a/core/ui/menu/home/home_menu.gd b/core/ui/menu/home/home_menu.gd index 9f0eea93..4609ab78 100644 --- a/core/ui/menu/home/home_menu.gd +++ b/core/ui/menu/home/home_menu.gd @@ -9,11 +9,11 @@ var main_menu_state := preload("res://assets/state/states/main_menu.tres") as St var launcher_state := preload("res://assets/state/states/game_launcher.tres") as State var poster_scene := preload("res://core/ui/components/poster.tscn") as PackedScene var _initialized := false +var recent_apps: Array -@onready var container: HBoxContainer = $MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer +@onready var container: HBoxContainer = $%PostersContainer @onready var banner: TextureRect = $SelectedBanner @onready var player: AnimationPlayer = $AnimationPlayer -var recent_apps: Array # Called when the node enters the scene tree for the first time. @@ -22,7 +22,6 @@ func _ready() -> void: for child in container.get_children(): if child.name == "LibraryPoster": continue - container.remove_child(child) child.queue_free() LibraryManager.library_reloaded.connect(_on_library_reloaded) @@ -98,7 +97,7 @@ func _on_recent_apps_updated() -> void: items[name] = library_item # Populate our grid with items - _repopulate_grid(container, items.values()) + await _repopulate_grid(container, items.values()) _grab_focus() @@ -128,6 +127,7 @@ func _build_poster(item: LibraryItem, portrait: bool) -> TextureButton: poster.layout = poster.LAYOUT_MODE.PORTRAIT else: poster.layout = poster.LAYOUT_MODE.LANDSCAPE + poster.name = item.name poster.text = item.name poster.layout_scale = 1.4 diff --git a/core/ui/menu/home/home_menu.tscn b/core/ui/menu/home/home_menu.tscn index 75184409..0f405929 100644 --- a/core/ui/menu/home/home_menu.tscn +++ b/core/ui/menu/home/home_menu.tscn @@ -21,7 +21,7 @@ [ext_resource type="Shader" path="res://assets/shaders/outline.gdshader" id="12_fa67h"] [ext_resource type="Texture2D" uid="uid://dkdoyup6wajvq" path="res://assets/images/library-grid-portrait.png" id="17_7n7ug"] -[sub_resource type="ShaderMaterial" id="ShaderMaterial_grjtv"] +[sub_resource type="ShaderMaterial" id="ShaderMaterial_31pe1"] resource_local_to_scene = true shader = ExtResource("12_fa67h") shader_parameter/on = false @@ -57,7 +57,7 @@ launch_items = [SubResource("Resource_gsruu")] tags = null categories = null -[sub_resource type="ShaderMaterial" id="ShaderMaterial_5tph2"] +[sub_resource type="ShaderMaterial" id="ShaderMaterial_meu0h"] resource_local_to_scene = true shader = ExtResource("12_fa67h") shader_parameter/on = false @@ -170,23 +170,24 @@ theme_override_constants/margin_top = 50 theme_override_constants/margin_right = 50 theme_override_constants/margin_bottom = 50 -[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer"] +[node name="PostersContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer"] +unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 16 -[node name="Button" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer" instance=ExtResource("4_k6kif")] -material = SubResource("ShaderMaterial_grjtv") +[node name="Button" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/PostersContainer" instance=ExtResource("4_k6kif")] +material = SubResource("ShaderMaterial_31pe1") custom_minimum_size = Vector2(200.2, 301) layout_mode = 2 text = "vkCube" layout = 1 layout_scale = 1.4 -[node name="Launcher" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/Button" instance=ExtResource("5_0bpdu")] +[node name="Launcher" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/PostersContainer/Button" instance=ExtResource("5_0bpdu")] library_item = SubResource("Resource_icex7") -[node name="LibraryPoster" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer" instance=ExtResource("4_k6kif")] -material = SubResource("ShaderMaterial_5tph2") +[node name="LibraryPoster" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/PostersContainer" instance=ExtResource("4_k6kif")] +material = SubResource("ShaderMaterial_meu0h") custom_minimum_size = Vector2(200.2, 301) layout_mode = 2 texture_normal = ExtResource("17_7n7ug") @@ -195,7 +196,7 @@ text = "Library" layout = 1 layout_scale = 1.4 -[node name="StateUpdater" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/LibraryPoster" instance=ExtResource("9_8yw4g")] +[node name="StateUpdater" parent="MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/PostersContainer/LibraryPoster" instance=ExtResource("9_8yw4g")] state_machine = ExtResource("10_tl8ss") state = ExtResource("11_lws2k") on_signal = "button_up" diff --git a/core/ui/menu/launch/game_launch_menu.gd b/core/ui/menu/launch/game_launch_menu.gd index 91990bd0..e6f7ac8f 100644 --- a/core/ui/menu/launch/game_launch_menu.gd +++ b/core/ui/menu/launch/game_launch_menu.gd @@ -1,10 +1,10 @@ extends Control -const SettingsManager := preload("res://core/global/settings_manager.tres") -const LaunchManager := preload("res://core/global/launch_manager.tres") -const LibraryManager := preload("res://core/global/library_manager.tres") -const NotificationManager := preload("res://core/global/notification_manager.tres") -const BoxArtManager := preload("res://core/global/boxart_manager.tres") +var SettingsManager := preload("res://core/global/settings_manager.tres") +var LaunchManager := preload("res://core/global/launch_manager.tres") +var NotificationManager := preload("res://core/global/notification_manager.tres") +var BoxArtManager := preload("res://core/global/boxart_manager.tres") +var LibraryManager := preload("res://core/global/library_manager.tres") var state_machine := ( preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine diff --git a/core/ui/menu/launch/game_launch_menu.tscn b/core/ui/menu/launch/game_launch_menu.tscn index 7a62daa6..2a619cff 100644 --- a/core/ui/menu/launch/game_launch_menu.tscn +++ b/core/ui/menu/launch/game_launch_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=21 format=3 uid="uid://bwl2icwgry4l"] +[gd_scene load_steps=22 format=3 uid="uid://bwl2icwgry4l"] [ext_resource type="Script" path="res://core/ui/menu/launch/game_launch_menu.gd" id="1_uhkt4"] [ext_resource type="Theme" uid="uid://bko0q7gp1hwjp" path="res://assets/themes/dracula.tres" id="2_q87np"] @@ -15,6 +15,7 @@ [ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="8_hg7ac"] [ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="8_jwh6r"] [ext_resource type="PackedScene" uid="uid://2tdbi1v6qb6h" path="res://core/ui/components/loading02.tscn" id="11_8fkuv"] +[ext_resource type="PackedScene" uid="uid://61bw8lmqymry" path="res://core/ui/components/transition_slide_down.tscn" id="11_ei0vn"] [ext_resource type="Resource" uid="uid://cx8u1y5j7vyss" path="res://assets/state/states/gamepad_settings.tres" id="16_0i4he"] [ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="16_d1x8e"] [ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="17_yolrd"] @@ -61,6 +62,12 @@ layout_mode = 2 size_flags_horizontal = 3 theme_override_constants/separation = 20 +[node name="VisibilityManager" parent="ScrollContainer/VBoxContainer" instance=ExtResource("2_sp632")] +state = ExtResource("3_68ilj") +visible_during = Array[Resource]([ExtResource("4_mab76"), ExtResource("5_ejgx3"), ExtResource("6_t3rni"), ExtResource("7_2y6wm"), ExtResource("8_jwh6r")]) + +[node name="SlideTransition" parent="ScrollContainer/VBoxContainer/VisibilityManager" instance=ExtResource("11_ei0vn")] + [node name="GameBanner" type="TextureRect" parent="ScrollContainer/VBoxContainer"] custom_minimum_size = Vector2(0, 440) layout_mode = 2