Reputation: 8991
I have a TerrainManager
which has a child node called Parts
.
I have an array of packed scenes (currently only two: Part1
and Part2
). The TerrainManager
randomly selects parts from this array and positions them sequentially.
I'm able to test this in the editor too.
When playing, the method works fine and the nodes are added to the tree; however, I'm having trouble debugging because in the editor the Parts are there, but they don't appear in the node tree.
I have added outputs to share whether the nodes are added to the tree, and have tested the path to the nodes too, which both appear to be correct - I just can't see them in the tree at all and therefore can't select them in the editor.
Terrain_Manager.gd
:
@tool
extends Node
class_name TerrainManager
@export var terrain_parts : Array[PackedScene]
@export var terrain_part_container : Node2D
const MAX_PARTS = 10
var end_position : Vector2 = Vector2(0, 0)
func _ready() -> void:
if Engine.is_editor_hint():
return
fill_container()
## Add a new segment to the terrain
func add_segment() -> void:
var random_part = terrain_parts.pick_random().duplicate() as PackedScene
var part = random_part.instantiate()
part.name="TerrainPartInstance_%s" % part.name
## Add to container
terrain_part_container.add_child(part)
## Reposition to the end of the last segment
if terrain_part_container.get_child_count() > 1:
var _part = terrain_part_container.get_child(terrain_part_container.get_child_count() - 2) as TerrainPart
end_position += _part.global_position_end - _part.global_position_start
part.position = end_position + part.global_position_start
## DEBUG TOOLS:
@export var request_refresh : bool = false : set = _empty_and_fill_container
func _empty_and_fill_container(_set : bool) -> void:
_empty_container(true)
fill_container()
@export var request_empty : bool = false : set = _empty_container
func _empty_container(_set : bool) -> void:
for child in terrain_part_container.get_children():
child.queue_free()
end_position = Vector2(0, 0)
Terrain_Part.gd
:
@tool
extends Node
class_name TerrainPart
@export var _anchor : SS2D_Shape_Anchor
@export var _shape : SS2D_Shape_Open
func _ready() -> void:
if Engine.is_editor_hint():
print("Checking that %s is in tree: %s" % [self.name, self.is_inside_tree()])
return
var global_position_start : Vector2 : get = _get_global_position_start
func _get_global_position_start() -> Vector2:
_validate()
return _get_ss2d_point_at_index(0).position
var global_position_end : Vector2 : get = _get_global_position_end
func _get_global_position_end() -> Vector2:
_validate()
return _get_ss2d_point_at_index(-1).position
func round_first_point() -> void:
var _first_point : SS2D_Point = _get_ss2d_point_at_index(0)
_first_point.position = Vector2(round(_first_point.position.x), round(_first_point.position.y))
func round_last_point() -> void:
var _last_point : SS2D_Point = _get_ss2d_point_at_index(-1)
_last_point.position = Vector2(round(_last_point.position.x), round(_last_point.position.y))
func _get_ss2d_point_at_index(_index : int) -> SS2D_Point:
return _shape._points._points[_shape._points._point_order[_index]]
# TOOLS:
@export var validate : bool = false : set = _validate
func _validate(_val : bool = false) -> void:
round_first_point()
round_last_point()
set_bezier_handles(-1, Vector2(-250,0), Vector2(250,0))
set_bezier_handles(0, Vector2(-250,0), Vector2(250,0))
func set_bezier_handles(_index : int, _value : Vector2, _value2 : Vector2) -> void:
var _point : SS2D_Point = _get_ss2d_point_at_index(_index)
_point.point_in = _value
_point.point_out = _value2
Screenshot of editor when running game (expected output - except the node names are not being changed correctly:
Output when using TerrainManager.request_refresh
in editor:
Checking that TerrainPartInstance_Part1 is in tree: true
Checking that TerrainPartInstance_Part2 is in tree: true
Checking that @Node2D@42549 is in tree: true
Checking that @Node2D@42550 is in tree: true
Checking that @Node2D@42551 is in tree: true
Checking that @Node2D@42552 is in tree: true
Checking that @Node2D@42553 is in tree: true
Checking that @Node2D@42554 is in tree: true
Checking that @Node2D@42555 is in tree: true
Checking that @Node2D@42556 is in tree: true
Screenshot of editor after running the above:
Upvotes: 1
Views: 1247
Reputation: 40295
You need to set the owner
of your nodes created from the @tool
script for them to show up in the editor (and also be saved with the scene).
Making the owner
of your nodes the root of the edited scene tells Godot that those nodes are part of that scene.
To reiterate, these are the effects of the owner
property:
Note: owner
is also mentioned in the documentation of add_child
, and in the title "Instancing Scenes" of the article Running code in the editor.
This is good if you want to generate the nodes in the editor, and not in runtime. Put another way, if you set the owner
then in runtime you will have the nodes that were created and saved in the editor (because they are part of the scene).
Now, if you do all the generation in editor, you might not want to do it in runtime.
For the benefit of people finding this answer, I'll also mention that you can detect if your code is running in the ditor with Engine.is_editor_hint()
.
Alternatively, you can check if the nodes already exist, and not create them if they are already there.
On a similar note, be aware that signal connections done from a @tool
script will be persisted too, so you might want to check if the connection is already made before making it.
See also Display scene with scripted changes in the editor.
Upvotes: 3