-
Notifications
You must be signed in to change notification settings - Fork 79
/
Copy pathInstanceResource.gd
88 lines (68 loc) · 2.76 KB
/
InstanceResource.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
class_name InstanceResource
extends Resource
signal updated
signal scene_changed
## Using file path to a scene no not cause cyclic reference when including in scenes.
## Scene file will be loaded when instancing scene for the first time
@export var scene_path:String
## Reference to a node that will be used as a parent.
@export var parent_reference_resource:ReferenceNodeResource
## After first instance crewation a scene file is cached
var scene:PackedScene
## Collect references of all active instances
var active_list:Array[Node]
## Inactive scenes with a PoolNode are put in the list, to pull out when a new one is needed, instead of instancing every time.
var pool_list:Array[Node]
## Function uses Threads so connect single shot Callable to scene_changed before calling this function if scene is null.
func preload_scene(use_thread:bool = true)->void:
if scene != null:
return
# TODO: check if platform can use Threads. If no then use regular load.
if use_thread:
ThreadUtility.load_resource(scene_path, set_scene)
else:
set_scene(load(scene_path))
func set_scene(value:PackedScene)->void:
scene = value
#print("InstanceResource [INFO]: loaded ", resource_name)
scene_changed.emit()
func _create_instance()->Node:
# Use pooled instances first
if !pool_list.is_empty():
var _node:Node = pool_list.pop_back()
active_list.append(_node)
updated.emit()
return _node
var _node:Node = scene.instantiate()
active_list.append(_node)
if _node.has_node("PoolNode"):
var _pool_node:PoolNode = _node.get_node("PoolNode")
_pool_node.pool_requested.connect(_return_to_pool.bind(_node))
_node.tree_exiting.connect(_erase.bind(_node))
updated.emit()
return _node
## Pass a config_callback to configure instance before it is added the scene tree
func instance(config_callback:Callable = Callable())->Node:
assert(parent_reference_resource != null)
assert(parent_reference_resource.node != null)
# using threaded preload is recommended to remove stutters
if scene == null:
set_scene(load(scene_path))
var _node:Node = _create_instance()
if config_callback.is_valid():
config_callback.call(_node)
assert(_node.get_parent() == null)
parent_reference_resource.node.add_child.call_deferred(_node)
return _node
## Remove from active instance list
func _erase(node:Node)->void:
active_list.erase(node)
updated.emit()
## Apparently, physics thread or other reason, this can be called more than once
func _return_to_pool(node:Node)->void:
assert(node.get_parent() == parent_reference_resource.node)
_handle_return.call_deferred(node)
func _handle_return(node:Node)->void:
assert(node.get_parent() == parent_reference_resource.node)
node.tree_exiting.connect(pool_list.append.call_deferred.bind(node), CONNECT_ONE_SHOT)
parent_reference_resource.node.remove_child(node)