godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.14k stars 96 forks source link

Add arguments to `get_tree().change_scene_to_*()` #10293

Open Germenzi opened 2 months ago

Germenzi commented 2 months ago

Describe the project you are working on

Multiplayer project where TCP should be used, so High-level multiplayer API isn't my choice.

Describe the problem or limitation you are having in your project

The problem is in switching between several scenes saving connection StreamPeerTCP instance. Some kind of autoload help to solve this problem, but this solution have some issues:

And as for me, I need more than one connection and/or server (chat server/client, matchmaker, room server etc.) in one process. In my test project I wrote TCPNetworker manager (which is autoload and keeps all those problems), but it still doesn't fit me and that's off-topic. (Code is so messy, you know)

Describe the feature / enhancement and how it helps to overcome the problem or limitation

I propose to add parameter(s) to get_tree().change_scene_to_* which could be accepted via get_tree().get_scene_argument, simple concept like cmdline arguments.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Example of changing scene when button pressed:

func _on_button_pressed():
    # here 100 - player hp for example, while 7635 is player score
    get_tree().change_scene_to_file("res://level-2.tscn", 100, 7635)

And in level-2 scene those arguments can be accepted as below:

# any node in scene (or in a tree) after changing scene
func _enter_tree():
    print(get_tree().get_scene_argument(0)) # prints player hp in scene 'initialization'

If this enhancement will not be used often, can it be worked around with a few lines of script?

Yes, it can be done using custom SceneTree like below:

extends SceneTree

class_name MySceneTree

var _params : Dictionary = {}
var _new_scene_with_params : bool = false

func _initialize() -> void:
    node_added.connect(_on_node_added)

func change_scene_to_file_parameterized(file:String, parameters:Dictionary={}) -> Error:
    _new_scene_with_params = true
    _params = parameters
    return change_scene_to_file(file)

func get_scene_parameters() -> Dictionary:
    return _params

func _on_node_added(node:Node) -> void:
    if node == current_scene:
        current_scene.tree_exited.connect(_on_scene_deleted)

func _on_scene_deleted() -> void:
        # if we uses default change_scene_to_* one think there is no arguments, so it should be automatically cleared
    if not _new_scene_with_params:
        _params = {}
    else:
        _new_scene_with_params = false

Is there a reason why this should be core and not an add-on in the asset library?

Generally - no. But being core feature it will be a lot more readable and reliable. Also custom SceneTree doen't support code autocompletion. Another reason is that scene files may contain 'neccesary' arguments which will be checked if this scene used in change_scene_to_*. And this will avoid using autoloads just to throw data to the next scene, removing unnecessary in other scenes nodes.

AThousandShips commented 2 months ago

Essentially the same as:

But with specific case where it would be used

Germenzi commented 2 months ago

Essentially the same as:

But with specific case where it would be used

Not exactly, I think. Passing parameters to PackedScene still doesn't allow you to pass parameters into get_tree().change_scene_to_*, because you don't call PackedScene.instantiate directly.

AThousandShips commented 2 months ago

It would be part of achieving it easily, by just passing those arguments when calling the instantiate

However the workaround is trivial enough that I don't see this being necessary, it isn't something that's hard to do in your project with your own specific needs, and with your specific setup you're probably better off with a custom solution