Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add switch_scene resource #298

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions addons/block_code/blocks/communication/switch_scene.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dnc2555wnobks"]

[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_rnqd5"]

[resource]
script = ExtResource("1_rnqd5")
name = &"switch_scene"
target_node_class = ""
description = "Stop playing the current scene, and switch to a different one. You might use this to switch to a new level."
category = "Communication | Methods"
type = 2
variant_type = 0
display_template = "switch the scene to {file_path: STRING}"
code_template = "get_tree().change_scene_to_file({file_path})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People more expert in Godot than me: is there some way we can refer to the other scene other than by file path?

Copy link
Member

@wjt wjt Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm imagining here is: can we make it possible to drag a tscn file onto the canvas and then have a block representing a different scene? Or a dropdown list of all tscn files in the project, except the current one and scenes that are part of the plugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another way to do is to export a PackedScene variable packed scene. It will allows the user to assign the scene in the inspector. Should we update and use this approach instead? (Although I'm not the expert)
During my research, I found three ways to switch scenes: T35706. Hopefully, we can get more input from the experts!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is right to do this in the block coding canvas (as you have done) not to go to the inspector. I was hoping we could do something similar to how we can drop nodes into the canvas to get a block that represents that node.

I did some brief experiments with this... If you have a node that exports a variable of type PackedScene, then assign a scene to it, it gets represented as (wrapped for legibility):

[ext_resource type="PackedScene"
              uid="uid://c2pcat3ejl74o"
              path="res://Scene2.tscn"
              id="2_g4qc4"]

I feel like it ought to be possible to define a value block that gets serialised in a way that refers to an external scene in this way. Then this switch_scene block would accept a parameter of type PackedScene. And for bonus points we would give it a dropdown of all scenes in the project, like we do for parameters of type node or group.

Unfortunately block parameter types are expressed as Variant.Type enum members and a PackedScene is just an OBJECT. There's no analogue to NODE_PATH.

As well as the drag-and-drop idea, another reason it may be nice to store the reference to another scene in a way that Godot itself understands to be a reference to a file rather than just a string is that Godot knows how to update all such references if you rename the scene.

Anyway... What I'm suggesting is of course all way more work than adding a switch_scene block whose parameter is a string. And having the block at all would make a whole world of things possible which currently aren't. So all my suggestions here should probably be considered further work for another time…

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm imagining here is: can we make it possible to drag a tscn file onto the canvas and then have a block representing a different scene? Or a dropdown list of all tscn files in the project, except the current one and scenes that are part of the plugin?

Yeah, in the same way as the more sophisticated alternative to drag&drop sounds that I described in #166 (comment)

Unfortunately block parameter types are expressed as Variant.Type enum members and a PackedScene is just an OBJECT. There's no analogue to NODE_PATH.

I think that we should add a class_name field to the block definition, that will pair with the variant_type field for the case where the variant is of type OBJECT. This is the same Godot has for typed arrays:

Then when dragging from the Filesystem to the block canvas:

  • If the resource is a sound, add a value block of variant type OBJECT and class name AudioStream.
  • If the resource is a tscn or scn, add a value block of variant type OBJECT and class name PackedScene.

In the same way, the holes or slots can be enhaced to allow a specific class name when the variant type is OBJECT. Then we could do:

  • "play sound" block: Has a slot that supports a value block of variant type OBJECT and class name AudioStream.
  • "switch scene" block: Has a slot that supports a value block of variant type OBJECT and class name PackedScene.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, a PackedScene is a Resource. SceneTree (what you get from get_tree()) provides 2 methods for changing scenes - from a file path, and from a PackedScene.

In code you might do it like this:

func change_scene(scene: Variant):
    if scene is String:
        get_tree().change_scene_to_file(scene)
    elif scene is PackedScene:
        get_tree().change_scene_to_packed(scene)
    else:
        push_error("Can only change scene from String or PackedScene")

With blocks we kinda need to be specific about what kind of Variant the parameter is, so we'd need to do one or the other. @manuq has some suggestions on handling that if we want to use PackedScenes.

I like the idea of loading a PackedScene with change_scene_to_packed, but then you'd have to tackle the whole part of creating a scene with a block using load. It would be very similar to the get_node block, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think #305 helps get_tree().change_scene_to_file(scene) :)

"
defaults = {}
signal_name = ""
scope = ""