diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/README.md b/README.md index 025e634..abfe7ff 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,80 @@ -# AsyncScene +## AsyncScene - Asynchronous Scene Loader + This Godot tool provides a simple way to load scenes asynchronously, improving your game's loading times and user experience. + +### Features + +- Load scenes in the background without freezing the main thread. +- Replace the current scene or add the loaded scene additively. +- Choose between immediate or manual scene switching after loading. +- Track loading progress with a percentage value. +- Receive notifications upon successful or failed scene loading. + +### Installation + +1. Copy the `AsyncScene.gd` script into your project. +2. Optionally, you can create a new resource type for easier access: + - Go to **Project > Project Settings > Plugins > Resource** and click "Create a Resource Type". + - Choose a name (e.g., "AsyncScene") and link it to the `AsyncScene.gd` script. + +### Usage + +**1. Loading a scene:** + +```gdscript +extends Node2D + +var scene : AsyncScene + +func _ready() -> void: + # Replace the current scene immediately after loading: + scene = AsyncScene.new( "res://path/to/your/scene.tscn", AsyncScene.LoadingSceneOperation.ReplaceImmediate) + + # Replace the current scene manually after loading (call scene.ChangeScene() later): + # scene = AsyncScene.new("res://path/to/your/scene.tscn", AsyncScene.LoadingSceneOperation.Replace) + + # Add the loaded scene to the current scene tree: + # scene = AsyncScene.new("res://path/to/your/scene.tscn", AsyncScene.LoadingSceneOperation.Additive) + + # Connect to the OnComplete signal to get notified when loading is finished: + scene.OnComplete.connect(on_scene_load_complete) + +func on_scene_load_complete(): + # Do something after the scene is loaded, e.g., hide loading screen. + pass +``` + +**2. Manually switching to the loaded scene (if using Replace mode):** + +```gdscript +func _process(delta): + if scene and scene.isCompleted: + scene.ChangeScene() +``` + +**3. Accessing loading progress:** + +```gdscript +func _process(delta): + if scene: + print("Loading progress: ", scene.progress, "%") +``` + +**4. Unloading the loaded scene:** + +```gdscript +scene.UnloadScene() +``` + +**5. Getting the loading status:** + +```gdscript +var status = scene.GetStatus() # Returns a string like "THREAD_LOAD_IN_PROGRESS", "THREAD_LOAD_LOADED", etc. +``` + +### Example + +Check the provided `example.tscn` scene for a practical demonstration of how to use the AsyncScene tool. + + +This readme provides a basic overview of the AsyncScene tool and its usage. You can further customize and extend this tool to suit your specific needs. diff --git a/addons/AsyncSceneManager/AsyncScene.gd b/addons/AsyncSceneManager/AsyncScene.gd new file mode 100644 index 0000000..c47f13b --- /dev/null +++ b/addons/AsyncSceneManager/AsyncScene.gd @@ -0,0 +1,101 @@ +extends Node +class_name AsyncScene + + +enum LoadingSceneOperation { + ReplaceImmediate, ## Replaces scene as soon as it loads + Replace, ## Doesn't Replace scene immediate, and need to call ChangeScene(using the same path) + Additive ## Adding to the tree another scene +} +var status_names = { + ResourceLoader.THREAD_LOAD_IN_PROGRESS: "THREAD_LOAD_IN_PROGRESS", + ResourceLoader.THREAD_LOAD_FAILED: "THREAD_LOAD_FAILED", + ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: "THREAD_LOAD_INVALID_RESOURCE", + ResourceLoader.THREAD_LOAD_LOADED: "THREAD_LOAD_LOADED" +} + +var timer : Timer = Timer.new() +signal OnComplete +var packedScenePath : String = "" +var myRes : PackedScene = null +var currentSceneNode : Node = null +var progress : float = 0 +var isCompleted : bool = false +var typeOperation : LoadingSceneOperation = LoadingSceneOperation.ReplaceImmediate + +func _init(tscnPath : String, setOperation : LoadingSceneOperation = LoadingSceneOperation.ReplaceImmediate ) -> void: + packedScenePath = tscnPath + typeOperation = setOperation + if not ResourceLoader.exists(tscnPath): + printerr("Invalid scene path " + tscnPath) + return + ResourceLoader.load_threaded_request(tscnPath,"",true) + call_deferred("_setupUpdateSeconds") + + + +func ChangeScene() -> void: + if not isCompleted: + printerr("Scene hasn't been loaded yet") + return + Engine.get_main_loop().root.get_tree().change_scene_to_packed(myRes) + currentSceneNode = Engine.get_main_loop().root.get_tree().current_scene + +func GetStatus() -> String: + return status_names.get(_getStatus()) + +#region Private + +func _additiveScene() -> void: + currentSceneNode = myRes.instantiate() + Engine.get_main_loop().root.call_deferred("add_child",currentSceneNode) + + +## Unloading +func UnloadScene() -> void: + if not isCompleted: + printerr("Scene hasn't been loaded yet") + return + currentSceneNode.queue_free() + queue_free() + + +func _setupUpdateSeconds() -> void: + Engine.get_main_loop().root.add_child(timer) + timer.one_shot = false + timer.autostart = true + timer.set_wait_time(0.1) + timer.timeout.connect(_check_status) + timer.start() + + +func _getStatus() -> ResourceLoader.ThreadLoadStatus: + return ResourceLoader.load_threaded_get_status(packedScenePath) + + +func _check_status() -> void: + if isCompleted : return + if _getStatus() == ResourceLoader.THREAD_LOAD_LOADED: + myRes = ResourceLoader.load_threaded_get(packedScenePath) + if typeOperation == LoadingSceneOperation.ReplaceImmediate: + ChangeScene() + elif typeOperation == LoadingSceneOperation.Additive: + _additiveScene() + _complete() + elif _getStatus() == ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: + _complete() + elif _getStatus() == ResourceLoader.THREAD_LOAD_FAILED: + _complete() + elif _getStatus() == ResourceLoader.THREAD_LOAD_IN_PROGRESS: + var progressArr : Array = [] + ResourceLoader.load_threaded_get_status(packedScenePath,progressArr) + progress = progressArr.front() * 100 + + + +func _complete() -> void: + timer.queue_free() + OnComplete.emit() + isCompleted = true + progress = 100 +#endregion diff --git a/addons/AsyncSceneManager/Examples/SceneChanging.gd b/addons/AsyncSceneManager/Examples/SceneChanging.gd new file mode 100644 index 0000000..c4f2971 --- /dev/null +++ b/addons/AsyncSceneManager/Examples/SceneChanging.gd @@ -0,0 +1,19 @@ +extends Node2D + +var scene : AsyncScene = null +@export var scenePath : String = 'res://addons/AsyncSceneManager/Examples/scene_to_load.tscn' + + +func _ready() -> void: + #scene = AsyncScene.new(scenePath,AsyncScene.LoadingSceneOperation.Replace) loading and later changing using scene.ChangeScene() + #scene = AsyncScene.new(scenePath,AsyncScene.LoadingSceneOperation.ReplaceImmediate) Immediately changing the scene after loading + scene = AsyncScene.new(scenePath,AsyncScene.LoadingSceneOperation.Additive) #Loading Scene additively to another scene + scene.OnComplete.connect(complete) #Binding to signal after complete loading + +func complete() -> void: + #scene.UnloadScene() Unloading scene + #scene.ChangeScene() Changing the main scene manually + print("Loading complete") + pass + + diff --git a/addons/AsyncSceneManager/Examples/scene_main.tscn b/addons/AsyncSceneManager/Examples/scene_main.tscn new file mode 100644 index 0000000..5d94655 --- /dev/null +++ b/addons/AsyncSceneManager/Examples/scene_main.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=2 format=3 uid="uid://ca76hnnn8ums7"] + +[ext_resource type="Script" path="res://addons/AsyncSceneManager/Examples/SceneChanging.gd" id="1_bldvc"] + +[node name="scene_main" type="Node2D"] +script = ExtResource("1_bldvc") + +[node name="Label" type="Label" parent="."] +offset_left = 452.0 +offset_right = 1152.0 +offset_bottom = 261.0 +theme_override_font_sizes/font_size = 191 +text = "Scene 1" +horizontal_alignment = 1 +vertical_alignment = 1 diff --git a/addons/AsyncSceneManager/Examples/scene_to_load.tscn b/addons/AsyncSceneManager/Examples/scene_to_load.tscn new file mode 100644 index 0000000..afb67b5 --- /dev/null +++ b/addons/AsyncSceneManager/Examples/scene_to_load.tscn @@ -0,0 +1,12 @@ +[gd_scene format=3 uid="uid://dqojfek3b58la"] + +[node name="scene_to_load" type="Node2D"] + +[node name="Label" type="Label" parent="."] +offset_top = 289.0 +offset_right = 700.0 +offset_bottom = 550.0 +theme_override_font_sizes/font_size = 191 +text = "Scene 2" +horizontal_alignment = 1 +vertical_alignment = 1 diff --git a/addons/AsyncSceneManager/Icon.jpg b/addons/AsyncSceneManager/Icon.jpg new file mode 100644 index 0000000..d147a98 Binary files /dev/null and b/addons/AsyncSceneManager/Icon.jpg differ diff --git a/addons/AsyncSceneManager/Icon.jpg.import b/addons/AsyncSceneManager/Icon.jpg.import new file mode 100644 index 0000000..db32f09 --- /dev/null +++ b/addons/AsyncSceneManager/Icon.jpg.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bbtg7trios1jk" +path="res://.godot/imported/Icon.jpg-f9230da019f0fcfc56881806ec8dc408.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/AsyncSceneManager/Icon.jpg" +dest_files=["res://.godot/imported/Icon.jpg-f9230da019f0fcfc56881806ec8dc408.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..88bd518 --- /dev/null +++ b/project.godot @@ -0,0 +1,18 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="AsyncSceneManager" +config/features=PackedStringArray("4.3", "Forward Plus") + +[debug] + +gdscript/warnings/untyped_declaration=2