-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.gd
748 lines (621 loc) · 20.2 KB
/
main.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
extends Node
var scene1 = preload("scene1.tscn")
var scene2 = preload("scene2.tscn")
var scene3 = preload("scene3.tscn")
func _ready():
# the basic usage
# tutorial_basics()
# bulk functions
# tutorial_bulk()
# change the default values for function calls
tutorial_defaults()
# details on usage of deferred argument
# tutorial_deferred()
# the sync feature
# tutorial_sync()
# the nested feature
# tutorial_nested()
# the pack feature
# tutorial_pack()
# the benchmark code that was used for the README
# running all tests at once might take forever, i dont know why
# tutorial_time()
# tutorial_readme()
pass
func tutorial_basics():
# gives instance of XScene with x.root = $"World"
# x adds and removes direct children of x.root
# it takes a Node which has to be in the scenetree
var x = XScene.new($World)
# x is only concerned with the direct children of root.
# x adds itself to the tree below root
# this is good because it will be freed automatically when root is freed
# however you have to keep in mind, that when iterating over the children of root
# x will be among them
# add_scene takes a PackedScene or a Node
# it defaults to key=count, method=ACTIVE, deferred=false, recursive_owner=false
# count is an int that is automatically incremented and starts at 1
x.add_scene(scene1)
# print(x.scenes)
# -> {1:{scene:[Node2D:1235], state:0}}
x.add_scene(scene1, "a", x.HIDDEN)
# print(x.scenes["a"])
# -> {scene:[Node2D:1239], state:1}
x.add_scene(scene1, "stopped_s1", x.STOPPED)
# print(x.scenes)
# ->
# {1:{scene:[Node2D:1235], state:0},
# a:{scene:[Node2D:1239], state:1},
# stopped_s1:{scene:[Node2D:1243], state:2}}
# get_node("/root").print_tree_pretty()
# ┖╴root
# ┖╴Main
# ┠╴Gui
# ┃ ┖╴ColorRect
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@@2 <- x
# ┃ ┠╴@Node2D@3 <- 1
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@4 <- "a"
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# "stopped_s1" is not in the tree
x.remove_scene(1, x.STOPPED)
# get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@4 <- "a" still hidden but in tree
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# 1 is no longer in the tree
# print(x.scenes[1])
# -> {scene:[Node2D:1235], state:2}
# print(x.stopped)
# -> [1, stopped_s1]
for s in x.stopped:
x.show_scene(s)
# print(x.get_scenes()["stopped_s1"])
# -> {stopped_s1:{scene:[Node2D:1243], state:0}
# get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@Node2D@4
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@Node2D@3 <- 1 active again
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@5 <- "stopped_s1" active again
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# frees active "stopped_s1", and makes "a" active. before that, it was hidden
# it uses show_scene and remove_scene under the hood
x.x_scene("a", "stopped_s1", x.FREE)
# print(x.scenes)
# -> {1:{scene:[Node2D:1235], state:0}, a:{scene:[Node2D:1239], state:0}}
# instances scene2 as x.count which is 2 and makes it active, if key_from is null,
# the default, the last active scene is used, in this case "a", it is freed
# it uses add_scene and remove_scene under the hood
x.x_add_scene(scene2, x.count, null, x.ACTIVE, x.FREE)
# print(x.scenes)
# -> {1:{scene:[Node2D:1235], state:0}, 2:{scene:[Node2D:1247], state:0}}
# hides 2 externally
x.scenes[2].scene.hide()
# this is a quicker and safer syntax to access ("x"ess) the scene directly
x.x(2).hide()
# print(x.scenes)
# -> {1:{scene:[Node2D:1235], state:0}, 2:{scene:[Node2D:1247], state:1}}
# stoppes 2 externally
$"World".remove_child(x.x(2))
# print(x.stopped)
# -> [2]
# frees 1 externally
x.x(1).free()
# print(x.scenes)
# -> {2:{scene:[Node2D:1247], state:2}}
# all these external changes to indexed scenes will be (lazily) synced when
# accessing the scenes in any way
# this is a faster syntax to get all node references in an array
# print(x.xs())
# -> [[Node2D:1246]]
# same but only for stopped scenes, also works for ACTIVE and HIDDEN
# print(x.xs(x.STOPPED))
# -> [[Node2D:1246]]
# frees all scenes
for s in x.scenes:
x.remove_scene(s)
func tutorial_access():
var x = XScene.new($World)
x.add_scene(scene1)
# this is the proper way to access single nodes in x.scenes,
# it returns null if the index doesnt exist
x.x(1)
# this is the proper way to access multiple nodes in x.scenes,
# it returns an empty array if no keys of the state still exist
x.xs()
x.xs(x.ACTIVE) # etc
# this is the proper way to access keys of a specific state, these are all arrays
x.active
x.hidden
x.stopped
# or all of them
x.scenes.keys()
# should normally not be necessary
x.scenes
# should be avoided, because the node at index may have been freed, and if so,
# the dictionary entry is erased. this means you can get an access error from this
x.scenes[1]
# same goes for the arrays
x.active[0]
# there is no save way to access the state of a specific key yet, because i
# didnt deem it necessary. if you disagree, open an issue
func tutorial_bulk():
var x = XScene.new($World)
# there are also convenience functions to add/show/remove multiple scenes at once,
# this is especially useful when using x.active/x.hidden/x.stopped
# adds 3 scenes indexed with count
x.add_scenes([scene1, scene2, scene3])
# print(x.scenes)
# -> {1:{scene:[Node2D:1249], state:0}, 2:{scene:[Node2D:1253], state:0}, 3:{scene:[Node2D:1255], state:0}}
# frees all active scenes
x.remove_scenes(x.active)
# print(x.scenes)
# -> {}
# adds 3 stopped scenes indexed with array
x.add_scenes([scene1, scene2, scene3], ["a", "b", "c"], x.STOPPED)
# print(x.scenes)
# -> {a:{scene:[Node2D:1257], state:2}, b:{scene:[Node2D:1261], state:2}, c:{scene:[Node2D:1263], state:2}}
# show all scenes
x.show_scenes(x.scenes.keys())
# print(x.scenes)
# -> {a:{scene:[Node2D:1257], state:0}, b:{scene:[Node2D:1261], state:0}, c:{scene:[Node2D:1263], state:0}}
func tutorial_defaults():
# the defaults of add/show/remove can be changed
var x = XScene.new($World)
# deferred is used in add/show/remove
# recursive_owner is only used in add
# changing count_start will only have an effect, if passed when initializing
# these are the actual defaults
# you can change them all at once
x.defaults = {
deferred = false,
recursive_owner = false,
method_add = x.ACTIVE,
method_remove = x.FREE,
count_start = 1
}
# you can assign a partial dictionary, this will only override the specified keys
# and leave the others in tact
var d = {deferred = true, count_start = 0}
x.defaults = d
print(x.defaults)
# -> {count_start:0, deferred:True, method_add:0, method_remove:3, recursive_owner:False}
# or you can change them individually like this
x.defaults.deferred = true
# x.defaults["deferred"] = true
# note that count hasnt changed though
print(x.count)
# -> 1
# the only way to apply the change to count_start is this:
# you can pass the defaults when initializing as a dictionary
var x1 = XScene.new($World, false, d)
print(x1.count)
# -> 0
# this will reset the defaults
x1.defaults = x1._original_defaults
print(x.defaults)
# {count_start:1, deferred:False, method_add:0, method_remove:3, recursive_owner:False}
# but it won't reset count
# this will be called deferred now because we changed the default to true
x.add_scene(scene1)
# could also be done like this without changing the default
# x.add_scene(scene1, x.count, x.ACTIVE, true)
# print(x.scenes)
# -> {}
yield(get_tree(), "idle_frame")
# print(x.scenes)
# -> {1:{scene:[Node2D:1234], state:0}}
# if you try to do give it wrong keys or wrong values it will throw an error directly
# so you can catch typos and semantic mistakes early
# these all throw errors
# x.defaults.deferre = true # invalid key
# x.defaults.deferred = 1 # wrong type
# x.defaults.method_add = x.FREE # add doesnt take FREE
# x.defaults = {method_remove = 0.0} # wrong type
# var x2 = XScene.new($World, false, {method_remove = 0.0})
func tutorial_deferred():
var x = XScene.new($World)
# when deferred is true, every tree change will be call_deferred() or queue_free()
# this does not include hide() and show(), these are always done immediately
# make an issue on github if you want this changed
x.defaults.deferred = true
x.add_scene(scene1)
# print(x.scenes)
# -> {}
yield(get_tree(), "idle_frame")
# print(x.scenes)
# -> {1:{scene:[Node2D:1234], state:0}}
x.remove_scene(1, x.STOPPED)
# despite deferred call, its state is changed immediately
# print(x.scenes)
# -> {1:{scene:[Node2D:1234], state:2}}
# get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@3 <- still in the tree
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
yield(get_tree(), "idle_frame")
# print(x.scenes)
# -> {1:{scene:[Node2D:1234], state:2}}
# get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# not in the tree any longer
x.show_scene(1)
# print(x.scenes)
# -> {1:{scene:[Node2D:1234], state:2}}
yield(get_tree(), "idle_frame")
# print(x.scenes)
# -> {1:{scene:[Node2D:1234], state:0}}
# uses queue_free()
x.remove_scene(1)
# despite deferred call, it is removed from scenes immediately
# print(x.scenes)
# -> {}
# get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@3 <- still in the tree
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
yield(get_tree(), "idle_frame")
# print(x.scenes)
# -> {}
# get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# not in the tree any longer
func tutorial_sync():
# WARNING the sync feature can be slow, see ## Caveats in the README.md
# the sync indexes all external additions to the tree under x.root
var x = XScene.new($World, true)
# this scene was already added in the editor
# because sync is on for this XScene Instance, it is added to scenes
# and count is used as the key
# print(x.scenes)
# -> {1:{scene:[Node2D:1227], state:0}}
var w = x.root
var two = scene2.instance()
var three = scene3.instance()
w.add_child(two)
w.add_child(three)
# print(x.scenes)
# -> {1:{scene:[Node2D:1227], state:0}, 2:{scene:[Node2D:1235], state:0}, 3:{scene:[Node2D:1237], state:0}}
# externally hide 2 and stop 3
# for operations that can be done with remove_scene(), the sync feature
# is not necessary. The sync is always done for indexed scenes when accessing them (lazily)
two.hide()
w.remove_child(three)
# print(x.scenes)
# -> {1:{scene:[Node2D:1226], state:0}, 2:{scene:[Node2D:1234], state:1}, 3:{scene:[Node2D:1236], state:2}}
# adds a child under Main, which isn't the x.root, so it's not indexed
add_child(scene1.instance())
# get_node("/root").print_tree_pretty()
#┖╴root
# ┠╴XSceneManager
# ┃ ┖╴@@2
# ┖╴Main
# ┠╴Gui
# ┃ ┖╴ColorRect
# ┠╴World
# ┃ ┠╴Node2D <- 1
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@Node2D@3 <- 2
# ┃ ┃ ┖╴icon
# ┠╴Test
# ┖╴Node2D <- not indexed
# ┖╴Node2D
# ┖╴Node2D
# ┖╴icon
x.add_scene(scene1, "internal")
# print(x.scenes)
# -> {1:{scene:[Node2D:1226], state:0}, 2:{scene:[Node2D:1234], state:1}, 3:{scene:[Node2D:1236], state:2}, internal:{scene:[Node2D:1242], state:0}}
# print(x.active[-1])
# -> internal
func tutorial_nested():
var x = XScene.new($World)
x.add_scene(scene1, "a")
# x1.root is "a" which is below x.root
var x1 = XScene.new(x.x("a"))
x1.add_scene(scene1, "aa")
# this frees the x1.root and therefore x1 with it
x.remove_scene("a")
# this throws an error because x1 is null
# x1.add_scene(scene1, "bb")
# if you check, you are still safe
# if x1 == null:
# or
if is_instance_valid(x1):
x1.add_scene(scene1, "dd")
else:
print("x1 was freed but its fine")
# GOOD PRACTICE
# you should make this check at the beginning of every function and after every yield
# when planning to use the XScene instance and
# if you want to avoid getting errors for null access
func tutorial_pack():
var x = XScene.new($World)
var n1 = Node.new()
var n2 = Node.new()
var n3 = Node.new()
n1.add_child(n2)
n2.add_child(n3)
# the last argument passed in, makes x.root owner of every node below n1
# this is only necessary for scenes constructed in scripts
# scenes made in the editor will be packed recursively without it
x.add_scene(n1, "b", 0, false, true)
# this packs x.root with PackedScene.pack() and saves it with ResourceSaver.save()
x.pack_root("res://example/test.scn")
$"/root/Main/Test".add_child(load("res://example/test.scn").instance())
func tutorial_time():
var time_begin
var time_add
var time_remove
var x
var p = $World
var n = 1000
var m = 10
# -------------
x = XScene.new(p, true)
time_add = 0.0
time_remove = 0.0
for j in range(m):
time_begin = OS.get_ticks_usec()
for i in range(n):
x.add_scene(scene1)
time_add += (OS.get_ticks_usec() - time_begin) / 1000000.0
time_begin = OS.get_ticks_usec()
for i in x.scenes.keys():
x.remove_scene(i)
time_remove += (OS.get_ticks_usec() - time_begin) / 1000000.0
time_add /= m
time_remove /= m
print("sync add in ", time_add)
print("sync remove in ", time_remove)
x.free()
# time_add = 0.0
# time_remove = 0.0
# var time_add_pre = 0.0
#
# for j in range(m):
# x = XScene.new(p, true)
#
# time_begin = OS.get_ticks_usec()
# for i in range(n):
# p.add_child(scene1.instance())
# time_add += (OS.get_ticks_usec() - time_begin) / 1000000.0
#
# x.free()
#
# time_begin = OS.get_ticks_usec()
# x = XScene.new(p, true)
# time_add_pre += (OS.get_ticks_usec() - time_begin) / 1000000.0
#
# time_begin = OS.get_ticks_usec()
# for i in p.get_children():
# i.free()
# time_remove += (OS.get_ticks_usec() - time_begin) / 1000000.0
#
# time_add /= m
# time_remove /= m
# time_add_pre /= m
#
# print("sync add ex ", time_add)
# print("sync add ex pre ", time_add_pre)
# print("sync remove ex ", time_remove)
# -------------
time_add = 0.0
time_remove = 0.0
for j in range(m):
time_begin = OS.get_ticks_usec()
for i in range(n):
p.add_child(scene1.instance())
time_add += (OS.get_ticks_usec() - time_begin) / 1000000.0
time_begin = OS.get_ticks_usec()
for i in p.get_children():
i.free()
time_remove += (OS.get_ticks_usec() - time_begin) / 1000000.0
time_add /= m
time_remove /= m
print("builtin add ", time_add)
print("builtin remove ", time_remove)
# -------------
x = XScene.new(p)
time_add = 0.0
time_remove = 0.0
for j in range(m):
time_begin = OS.get_ticks_usec()
for i in range(n):
x.add_scene(scene1)
time_add += (OS.get_ticks_usec() - time_begin) / 1000000.0
time_begin = OS.get_ticks_usec()
for i in x.scenes.keys():
x.remove_scene(i)
time_remove += (OS.get_ticks_usec() - time_begin) / 1000000.0
x.free()
time_add /= m
time_remove /= m
print("nosync add ", time_add)
print("nosync remove ", time_remove)
# -------------
x = XScene.new(p)
time_add = 0.0
time_remove = 0.0
for j in range(m):
var a = []
for i in range(n):
a.push_back(scene1)
time_begin = OS.get_ticks_usec()
x.add_scenes(a)
time_add += (OS.get_ticks_usec() - time_begin) / 1000000.0
time_begin = OS.get_ticks_usec()
x.remove_scenes(x.active)
time_remove += (OS.get_ticks_usec() - time_begin) / 1000000.0
x.free()
time_add /= m
time_remove /= m
print("nosync add bulk ", time_add)
print("nosync remove bulk ", time_remove)
func tutorial_readme():
var scene1 = preload("scene1.tscn")
# x adds and removes scenes below $World
# adds itself to the tree below $World
var x = XScene.new($World)
# this is a reference to $World
var r = x.root
# add_scene takes a PackedScene or a Node
# without a key specified it indexes automatically with integers starting at 1
# (this can be changed to 0)
# default method is ACTIVE, using add_child()
x.add_scene(scene1)
# uses add_child() and .hide()
x.add_scene(scene1, "a", x.HIDDEN)
# just instances and indexes the scene
x.add_scene(scene1, "stopped_s1", x.STOPPED)
get_node("/root").print_tree_pretty()
# ┖╴root
# ┖╴Main
# ┠╴Gui
# ┃ ┖╴ColorRect
# ┠╴World
# ┃ ┠╴Node2D <- was added by the editor and isn't indexed by default
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@@2 <- x instance
# ┃ ┠╴@Node2D@3 <- 1
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@4 <- "a" in the tree but hidden
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# "stopped_s1" isnt in the tree
print(x.scenes)
# {1:{scene:[Node2D:1227], state:0}, -> ACTIVE
# a:{scene:[Node2D:1231], state:1}, -> HIDDEN
# stopped_s1:{scene:[Node2D:1235], state:2}} -> STOPPED
# uses remove_child()
x.remove_scene(1, x.STOPPED)
get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@@2
# ┃ ┖╴@Node2D@4 <- "a" still in tree
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# 1 also is no longer in the tree
# make all STOPPED scenes ACTIVE
# mind the plural
x.show_scenes(x.stopped)
get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@@2
# ┃ ┠╴@Node2D@4 <- "a"
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@Node2D@3 <- 1
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@5 <- "stopped_s1"
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# exchange scene, makes "a" ACTIVE, and uses .free() on "stopped_s1"
# it defaults to FREE, the argument isn't necessary here
x.x_scene("a", "stopped_s1", x.FREE)
get_node("/root").print_tree_pretty()
# ┠╴World
# ┃ ┠╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┠╴@@2
# ┃ ┠╴@Node2D@4 <- "a" no longer hidden
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴Node2D
# ┃ ┃ ┖╴icon
# ┃ ┖╴@Node2D@3 <- 1
# ┃ ┖╴Node2D
# ┃ ┖╴Node2D
# ┃ ┖╴icon
# ┖╴Test
# "stopped_s1" was freed and is no longer indexed
# to access ("x"ess) the scene/node of "a" directly
x.x("a").hide()
# to access all hidden scenes directly, returns an array of nodes
print(x.xs(x.HIDDEN))
# [[Node2D:1231]] <- this is the node/scene of "a" in an array
# note that a was hidden externally and is still indexed correctly,
# this is done lazily, only when accessing that node
# put x.root and everything indexed into a file using PackedScene.pack() and ResourceSaver.save()
x.pack_root("res://example/test.scn")
# this can be loaded later, it includes x.root but not x
# .free() everything indexed by x, remove_scene/s defaults to FREE
# mind the plural
x.remove_scenes(x.scenes.keys())