1 | /**************************************************************************/ |
2 | /* physical_bone_2d.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "physical_bone_2d.h" |
32 | |
33 | #include "scene/2d/joint_2d.h" |
34 | |
35 | void PhysicalBone2D::_notification(int p_what) { |
36 | switch (p_what) { |
37 | case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { |
38 | // Position the RigidBody in the correct position. |
39 | if (follow_bone_when_simulating) { |
40 | _position_at_bone2d(); |
41 | } |
42 | |
43 | // Keep the child joint in the correct position. |
44 | if (child_joint && auto_configure_joint) { |
45 | child_joint->set_global_position(get_global_position()); |
46 | } |
47 | } break; |
48 | |
49 | case NOTIFICATION_READY: { |
50 | _find_skeleton_parent(); |
51 | _find_joint_child(); |
52 | |
53 | // Configure joint. |
54 | if (child_joint && auto_configure_joint) { |
55 | _auto_configure_joint(); |
56 | } |
57 | |
58 | // Simulate physics if set. |
59 | if (simulate_physics) { |
60 | _start_physics_simulation(); |
61 | } else { |
62 | _stop_physics_simulation(); |
63 | } |
64 | |
65 | set_physics_process_internal(true); |
66 | } break; |
67 | } |
68 | } |
69 | |
70 | void PhysicalBone2D::_position_at_bone2d() { |
71 | // Reset to Bone2D position |
72 | if (parent_skeleton) { |
73 | Bone2D *bone_to_use = parent_skeleton->get_bone(bone2d_index); |
74 | ERR_FAIL_NULL_MSG(bone_to_use, "It's not possible to position the bone with ID: " + itos(bone2d_index) + "." ); |
75 | set_global_transform(bone_to_use->get_global_transform()); |
76 | } |
77 | } |
78 | |
79 | void PhysicalBone2D::_find_skeleton_parent() { |
80 | Node *current_parent = get_parent(); |
81 | |
82 | while (current_parent != nullptr) { |
83 | Skeleton2D *potential_skeleton = Object::cast_to<Skeleton2D>(current_parent); |
84 | if (potential_skeleton) { |
85 | parent_skeleton = potential_skeleton; |
86 | break; |
87 | } else { |
88 | PhysicalBone2D *potential_parent_bone = Object::cast_to<PhysicalBone2D>(current_parent); |
89 | if (potential_parent_bone) { |
90 | current_parent = potential_parent_bone->get_parent(); |
91 | } else { |
92 | current_parent = nullptr; |
93 | } |
94 | } |
95 | } |
96 | } |
97 | |
98 | void PhysicalBone2D::_find_joint_child() { |
99 | for (int i = 0; i < get_child_count(); i++) { |
100 | Node *child_node = get_child(i); |
101 | Joint2D *potential_joint = Object::cast_to<Joint2D>(child_node); |
102 | if (potential_joint) { |
103 | child_joint = potential_joint; |
104 | break; |
105 | } |
106 | } |
107 | } |
108 | |
109 | PackedStringArray PhysicalBone2D::get_configuration_warnings() const { |
110 | PackedStringArray warnings = Node::get_configuration_warnings(); |
111 | |
112 | if (!parent_skeleton) { |
113 | warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!" )); |
114 | } |
115 | if (parent_skeleton && bone2d_index <= -1) { |
116 | warnings.push_back(RTR("A PhysicalBone2D needs to be assigned to a Bone2D node in order to function! Please set a Bone2D node in the inspector." )); |
117 | } |
118 | if (!child_joint) { |
119 | PhysicalBone2D *parent_bone = Object::cast_to<PhysicalBone2D>(get_parent()); |
120 | if (parent_bone) { |
121 | warnings.push_back(RTR("A PhysicalBone2D node should have a Joint2D-based child node to keep bones connected! Please add a Joint2D-based node as a child to this node!" )); |
122 | } |
123 | } |
124 | |
125 | return warnings; |
126 | } |
127 | |
128 | void PhysicalBone2D::_auto_configure_joint() { |
129 | if (!auto_configure_joint) { |
130 | return; |
131 | } |
132 | |
133 | if (child_joint) { |
134 | // Node A = parent | Node B = this node |
135 | Node *parent_node = get_parent(); |
136 | PhysicalBone2D *potential_parent_bone = Object::cast_to<PhysicalBone2D>(parent_node); |
137 | |
138 | if (potential_parent_bone) { |
139 | child_joint->set_node_a(child_joint->get_path_to(potential_parent_bone)); |
140 | child_joint->set_node_b(child_joint->get_path_to(this)); |
141 | } else { |
142 | WARN_PRINT("Cannot setup joint without a parent PhysicalBone2D node." ); |
143 | } |
144 | |
145 | // Place the child joint at this node's position. |
146 | child_joint->set_global_position(get_global_position()); |
147 | } |
148 | } |
149 | |
150 | void PhysicalBone2D::_start_physics_simulation() { |
151 | if (_internal_simulate_physics) { |
152 | return; |
153 | } |
154 | |
155 | // Reset to Bone2D position. |
156 | _position_at_bone2d(); |
157 | |
158 | // Apply the layers and masks. |
159 | PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); |
160 | PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); |
161 | PhysicsServer2D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority()); |
162 | |
163 | // Apply the correct mode. |
164 | _apply_body_mode(); |
165 | |
166 | _internal_simulate_physics = true; |
167 | set_physics_process_internal(true); |
168 | } |
169 | |
170 | void PhysicalBone2D::_stop_physics_simulation() { |
171 | if (_internal_simulate_physics) { |
172 | _internal_simulate_physics = false; |
173 | |
174 | // Reset to Bone2D position |
175 | _position_at_bone2d(); |
176 | |
177 | set_physics_process_internal(false); |
178 | PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); |
179 | PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); |
180 | PhysicsServer2D::get_singleton()->body_set_collision_priority(get_rid(), 1.0); |
181 | PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); |
182 | } |
183 | } |
184 | |
185 | Joint2D *PhysicalBone2D::get_joint() const { |
186 | return child_joint; |
187 | } |
188 | |
189 | bool PhysicalBone2D::get_auto_configure_joint() const { |
190 | return auto_configure_joint; |
191 | } |
192 | |
193 | void PhysicalBone2D::set_auto_configure_joint(bool p_auto_configure) { |
194 | auto_configure_joint = p_auto_configure; |
195 | _auto_configure_joint(); |
196 | } |
197 | |
198 | void PhysicalBone2D::set_simulate_physics(bool p_simulate) { |
199 | if (p_simulate == simulate_physics) { |
200 | return; |
201 | } |
202 | simulate_physics = p_simulate; |
203 | |
204 | if (simulate_physics) { |
205 | _start_physics_simulation(); |
206 | } else { |
207 | _stop_physics_simulation(); |
208 | } |
209 | } |
210 | |
211 | bool PhysicalBone2D::get_simulate_physics() const { |
212 | return simulate_physics; |
213 | } |
214 | |
215 | bool PhysicalBone2D::is_simulating_physics() const { |
216 | return _internal_simulate_physics; |
217 | } |
218 | |
219 | void PhysicalBone2D::set_bone2d_nodepath(const NodePath &p_nodepath) { |
220 | bone2d_nodepath = p_nodepath; |
221 | notify_property_list_changed(); |
222 | } |
223 | |
224 | NodePath PhysicalBone2D::get_bone2d_nodepath() const { |
225 | return bone2d_nodepath; |
226 | } |
227 | |
228 | void PhysicalBone2D::set_bone2d_index(int p_bone_idx) { |
229 | ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!" ); |
230 | |
231 | if (!is_inside_tree()) { |
232 | bone2d_index = p_bone_idx; |
233 | return; |
234 | } |
235 | |
236 | if (parent_skeleton) { |
237 | ERR_FAIL_INDEX_MSG(p_bone_idx, parent_skeleton->get_bone_count(), "Passed-in Bone index is out of range!" ); |
238 | bone2d_index = p_bone_idx; |
239 | |
240 | bone2d_nodepath = get_path_to(parent_skeleton->get_bone(bone2d_index)); |
241 | } else { |
242 | WARN_PRINT("Cannot verify bone index..." ); |
243 | bone2d_index = p_bone_idx; |
244 | } |
245 | |
246 | notify_property_list_changed(); |
247 | } |
248 | |
249 | int PhysicalBone2D::get_bone2d_index() const { |
250 | return bone2d_index; |
251 | } |
252 | |
253 | void PhysicalBone2D::set_follow_bone_when_simulating(bool p_follow_bone) { |
254 | follow_bone_when_simulating = p_follow_bone; |
255 | |
256 | if (_internal_simulate_physics) { |
257 | _stop_physics_simulation(); |
258 | _start_physics_simulation(); |
259 | } |
260 | } |
261 | |
262 | bool PhysicalBone2D::get_follow_bone_when_simulating() const { |
263 | return follow_bone_when_simulating; |
264 | } |
265 | |
266 | void PhysicalBone2D::_bind_methods() { |
267 | ClassDB::bind_method(D_METHOD("get_joint" ), &PhysicalBone2D::get_joint); |
268 | ClassDB::bind_method(D_METHOD("get_auto_configure_joint" ), &PhysicalBone2D::get_auto_configure_joint); |
269 | ClassDB::bind_method(D_METHOD("set_auto_configure_joint" , "auto_configure_joint" ), &PhysicalBone2D::set_auto_configure_joint); |
270 | |
271 | ClassDB::bind_method(D_METHOD("set_simulate_physics" , "simulate_physics" ), &PhysicalBone2D::set_simulate_physics); |
272 | ClassDB::bind_method(D_METHOD("get_simulate_physics" ), &PhysicalBone2D::get_simulate_physics); |
273 | ClassDB::bind_method(D_METHOD("is_simulating_physics" ), &PhysicalBone2D::is_simulating_physics); |
274 | |
275 | ClassDB::bind_method(D_METHOD("set_bone2d_nodepath" , "nodepath" ), &PhysicalBone2D::set_bone2d_nodepath); |
276 | ClassDB::bind_method(D_METHOD("get_bone2d_nodepath" ), &PhysicalBone2D::get_bone2d_nodepath); |
277 | ClassDB::bind_method(D_METHOD("set_bone2d_index" , "bone_index" ), &PhysicalBone2D::set_bone2d_index); |
278 | ClassDB::bind_method(D_METHOD("get_bone2d_index" ), &PhysicalBone2D::get_bone2d_index); |
279 | ClassDB::bind_method(D_METHOD("set_follow_bone_when_simulating" , "follow_bone" ), &PhysicalBone2D::set_follow_bone_when_simulating); |
280 | ClassDB::bind_method(D_METHOD("get_follow_bone_when_simulating" ), &PhysicalBone2D::get_follow_bone_when_simulating); |
281 | |
282 | ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_nodepath" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D" ), "set_bone2d_nodepath" , "get_bone2d_nodepath" ); |
283 | ADD_PROPERTY(PropertyInfo(Variant::INT, "bone2d_index" , PROPERTY_HINT_RANGE, "-1, 1000, 1" ), "set_bone2d_index" , "get_bone2d_index" ); |
284 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_configure_joint" ), "set_auto_configure_joint" , "get_auto_configure_joint" ); |
285 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics" ), "set_simulate_physics" , "get_simulate_physics" ); |
286 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_bone_when_simulating" ), "set_follow_bone_when_simulating" , "get_follow_bone_when_simulating" ); |
287 | } |
288 | |
289 | PhysicalBone2D::PhysicalBone2D() { |
290 | // Stop the RigidBody from executing its force integration. |
291 | PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); |
292 | PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); |
293 | PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); |
294 | |
295 | child_joint = nullptr; |
296 | } |
297 | |
298 | PhysicalBone2D::~PhysicalBone2D() { |
299 | } |
300 | |