1 | /**************************************************************************/ |
2 | /* physics_body_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 "physics_body_2d.h" |
32 | |
33 | #include "scene/scene_string_names.h" |
34 | |
35 | void PhysicsBody2D::_bind_methods() { |
36 | ClassDB::bind_method(D_METHOD("move_and_collide" , "motion" , "test_only" , "safe_margin" , "recovery_as_collision" ), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false)); |
37 | ClassDB::bind_method(D_METHOD("test_move" , "from" , "motion" , "collision" , "safe_margin" , "recovery_as_collision" ), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false)); |
38 | |
39 | ClassDB::bind_method(D_METHOD("get_collision_exceptions" ), &PhysicsBody2D::get_collision_exceptions); |
40 | ClassDB::bind_method(D_METHOD("add_collision_exception_with" , "body" ), &PhysicsBody2D::add_collision_exception_with); |
41 | ClassDB::bind_method(D_METHOD("remove_collision_exception_with" , "body" ), &PhysicsBody2D::remove_collision_exception_with); |
42 | } |
43 | |
44 | PhysicsBody2D::PhysicsBody2D(PhysicsServer2D::BodyMode p_mode) : |
45 | CollisionObject2D(PhysicsServer2D::get_singleton()->body_create(), false) { |
46 | set_body_mode(p_mode); |
47 | set_pickable(false); |
48 | } |
49 | |
50 | PhysicsBody2D::~PhysicsBody2D() { |
51 | if (motion_cache.is_valid()) { |
52 | motion_cache->owner = nullptr; |
53 | } |
54 | } |
55 | |
56 | Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin, bool p_recovery_as_collision) { |
57 | PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_motion, p_margin); |
58 | parameters.recovery_as_collision = p_recovery_as_collision; |
59 | |
60 | PhysicsServer2D::MotionResult result; |
61 | |
62 | if (move_and_collide(parameters, result, p_test_only)) { |
63 | // Create a new instance when the cached reference is invalid or still in use in script. |
64 | if (motion_cache.is_null() || motion_cache->get_reference_count() > 1) { |
65 | motion_cache.instantiate(); |
66 | motion_cache->owner = this; |
67 | } |
68 | |
69 | motion_cache->result = result; |
70 | return motion_cache; |
71 | } |
72 | |
73 | return Ref<KinematicCollision2D>(); |
74 | } |
75 | |
76 | bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) { |
77 | if (is_only_update_transform_changes_enabled()) { |
78 | ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation." ); |
79 | } |
80 | |
81 | bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result); |
82 | |
83 | // Restore direction of motion to be along original motion, |
84 | // in order to avoid sliding due to recovery, |
85 | // but only if collision depth is low enough to avoid tunneling. |
86 | if (p_cancel_sliding) { |
87 | real_t motion_length = p_parameters.motion.length(); |
88 | real_t precision = 0.001; |
89 | |
90 | if (colliding) { |
91 | // Can't just use margin as a threshold because collision depth is calculated on unsafe motion, |
92 | // so even in normal resting cases the depth can be a bit more than the margin. |
93 | precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); |
94 | |
95 | if (r_result.collision_depth > p_parameters.margin + precision) { |
96 | p_cancel_sliding = false; |
97 | } |
98 | } |
99 | |
100 | if (p_cancel_sliding) { |
101 | // When motion is null, recovery is the resulting motion. |
102 | Vector2 motion_normal; |
103 | if (motion_length > CMP_EPSILON) { |
104 | motion_normal = p_parameters.motion / motion_length; |
105 | } |
106 | |
107 | // Check depth of recovery. |
108 | real_t projected_length = r_result.travel.dot(motion_normal); |
109 | Vector2 recovery = r_result.travel - motion_normal * projected_length; |
110 | real_t recovery_length = recovery.length(); |
111 | // Fixes cases where canceling slide causes the motion to go too deep into the ground, |
112 | // because we're only taking rest information into account and not general recovery. |
113 | if (recovery_length < p_parameters.margin + precision) { |
114 | // Apply adjustment to motion. |
115 | r_result.travel = motion_normal * projected_length; |
116 | r_result.remainder = p_parameters.motion - r_result.travel; |
117 | } |
118 | } |
119 | } |
120 | |
121 | if (!p_test_only) { |
122 | Transform2D gt = p_parameters.from; |
123 | gt.columns[2] += r_result.travel; |
124 | set_global_transform(gt); |
125 | } |
126 | |
127 | return colliding; |
128 | } |
129 | |
130 | bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision, real_t p_margin, bool p_recovery_as_collision) { |
131 | ERR_FAIL_COND_V(!is_inside_tree(), false); |
132 | |
133 | PhysicsServer2D::MotionResult *r = nullptr; |
134 | PhysicsServer2D::MotionResult temp_result; |
135 | if (r_collision.is_valid()) { |
136 | // Needs const_cast because method bindings don't support non-const Ref. |
137 | r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result); |
138 | } else { |
139 | r = &temp_result; |
140 | } |
141 | |
142 | PhysicsServer2D::MotionParameters parameters(p_from, p_motion, p_margin); |
143 | parameters.recovery_as_collision = p_recovery_as_collision; |
144 | |
145 | return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); |
146 | } |
147 | |
148 | TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() { |
149 | List<RID> exceptions; |
150 | PhysicsServer2D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions); |
151 | Array ret; |
152 | for (const RID &body : exceptions) { |
153 | ObjectID instance_id = PhysicsServer2D::get_singleton()->body_get_object_instance_id(body); |
154 | Object *obj = ObjectDB::get_instance(instance_id); |
155 | PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(obj); |
156 | ret.append(physics_body); |
157 | } |
158 | return ret; |
159 | } |
160 | |
161 | void PhysicsBody2D::add_collision_exception_with(Node *p_node) { |
162 | ERR_FAIL_NULL(p_node); |
163 | PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(p_node); |
164 | ERR_FAIL_NULL_MSG(physics_body, "Collision exception only works between two nodes that inherit from PhysicsBody2D." ); |
165 | PhysicsServer2D::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid()); |
166 | } |
167 | |
168 | void PhysicsBody2D::remove_collision_exception_with(Node *p_node) { |
169 | ERR_FAIL_NULL(p_node); |
170 | PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(p_node); |
171 | ERR_FAIL_NULL_MSG(physics_body, "Collision exception only works between two nodes that inherit from PhysicsBody2D." ); |
172 | PhysicsServer2D::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid()); |
173 | } |
174 | |
175 | void StaticBody2D::set_constant_linear_velocity(const Vector2 &p_vel) { |
176 | constant_linear_velocity = p_vel; |
177 | |
178 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity); |
179 | } |
180 | |
181 | void StaticBody2D::set_constant_angular_velocity(real_t p_vel) { |
182 | constant_angular_velocity = p_vel; |
183 | |
184 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity); |
185 | } |
186 | |
187 | Vector2 StaticBody2D::get_constant_linear_velocity() const { |
188 | return constant_linear_velocity; |
189 | } |
190 | |
191 | real_t StaticBody2D::get_constant_angular_velocity() const { |
192 | return constant_angular_velocity; |
193 | } |
194 | |
195 | void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { |
196 | if (physics_material_override.is_valid()) { |
197 | physics_material_override->disconnect_changed(callable_mp(this, &StaticBody2D::_reload_physics_characteristics)); |
198 | } |
199 | |
200 | physics_material_override = p_physics_material_override; |
201 | |
202 | if (physics_material_override.is_valid()) { |
203 | physics_material_override->connect_changed(callable_mp(this, &StaticBody2D::_reload_physics_characteristics)); |
204 | } |
205 | _reload_physics_characteristics(); |
206 | } |
207 | |
208 | Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const { |
209 | return physics_material_override; |
210 | } |
211 | |
212 | void StaticBody2D::_bind_methods() { |
213 | ClassDB::bind_method(D_METHOD("set_constant_linear_velocity" , "vel" ), &StaticBody2D::set_constant_linear_velocity); |
214 | ClassDB::bind_method(D_METHOD("set_constant_angular_velocity" , "vel" ), &StaticBody2D::set_constant_angular_velocity); |
215 | ClassDB::bind_method(D_METHOD("get_constant_linear_velocity" ), &StaticBody2D::get_constant_linear_velocity); |
216 | ClassDB::bind_method(D_METHOD("get_constant_angular_velocity" ), &StaticBody2D::get_constant_angular_velocity); |
217 | |
218 | ClassDB::bind_method(D_METHOD("set_physics_material_override" , "physics_material_override" ), &StaticBody2D::set_physics_material_override); |
219 | ClassDB::bind_method(D_METHOD("get_physics_material_override" ), &StaticBody2D::get_physics_material_override); |
220 | |
221 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override" , PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial" ), "set_physics_material_override" , "get_physics_material_override" ); |
222 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity" , PROPERTY_HINT_NONE, "suffix:px/s" ), "set_constant_linear_velocity" , "get_constant_linear_velocity" ); |
223 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity" , PROPERTY_HINT_NONE, U"radians,suffix:\u00B0/s" ), "set_constant_angular_velocity" , "get_constant_angular_velocity" ); |
224 | } |
225 | |
226 | StaticBody2D::StaticBody2D(PhysicsServer2D::BodyMode p_mode) : |
227 | PhysicsBody2D(p_mode) { |
228 | } |
229 | |
230 | void StaticBody2D::_reload_physics_characteristics() { |
231 | if (physics_material_override.is_null()) { |
232 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0); |
233 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1); |
234 | } else { |
235 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); |
236 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); |
237 | } |
238 | } |
239 | |
240 | void AnimatableBody2D::set_sync_to_physics(bool p_enable) { |
241 | if (sync_to_physics == p_enable) { |
242 | return; |
243 | } |
244 | |
245 | sync_to_physics = p_enable; |
246 | |
247 | _update_kinematic_motion(); |
248 | } |
249 | |
250 | bool AnimatableBody2D::is_sync_to_physics_enabled() const { |
251 | return sync_to_physics; |
252 | } |
253 | |
254 | void AnimatableBody2D::_update_kinematic_motion() { |
255 | #ifdef TOOLS_ENABLED |
256 | if (Engine::get_singleton()->is_editor_hint()) { |
257 | return; |
258 | } |
259 | #endif |
260 | |
261 | if (sync_to_physics) { |
262 | PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), callable_mp(this, &AnimatableBody2D::_body_state_changed)); |
263 | set_only_update_transform_changes(true); |
264 | set_notify_local_transform(true); |
265 | } else { |
266 | PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), Callable()); |
267 | set_only_update_transform_changes(false); |
268 | set_notify_local_transform(false); |
269 | } |
270 | } |
271 | |
272 | void AnimatableBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { |
273 | if (!sync_to_physics) { |
274 | return; |
275 | } |
276 | |
277 | last_valid_transform = p_state->get_transform(); |
278 | set_notify_local_transform(false); |
279 | set_global_transform(last_valid_transform); |
280 | set_notify_local_transform(true); |
281 | } |
282 | |
283 | void AnimatableBody2D::_notification(int p_what) { |
284 | switch (p_what) { |
285 | case NOTIFICATION_ENTER_TREE: { |
286 | last_valid_transform = get_global_transform(); |
287 | _update_kinematic_motion(); |
288 | } break; |
289 | |
290 | case NOTIFICATION_EXIT_TREE: { |
291 | set_only_update_transform_changes(false); |
292 | set_notify_local_transform(false); |
293 | } break; |
294 | |
295 | case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { |
296 | // Used by sync to physics, send the new transform to the physics... |
297 | Transform2D new_transform = get_global_transform(); |
298 | |
299 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); |
300 | |
301 | // ... but then revert changes. |
302 | set_notify_local_transform(false); |
303 | set_global_transform(last_valid_transform); |
304 | set_notify_local_transform(true); |
305 | } break; |
306 | } |
307 | } |
308 | |
309 | void AnimatableBody2D::_bind_methods() { |
310 | ClassDB::bind_method(D_METHOD("set_sync_to_physics" , "enable" ), &AnimatableBody2D::set_sync_to_physics); |
311 | ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled" ), &AnimatableBody2D::is_sync_to_physics_enabled); |
312 | |
313 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics" ), "set_sync_to_physics" , "is_sync_to_physics_enabled" ); |
314 | } |
315 | |
316 | AnimatableBody2D::AnimatableBody2D() : |
317 | StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) { |
318 | } |
319 | |
320 | void RigidBody2D::_body_enter_tree(ObjectID p_id) { |
321 | Object *obj = ObjectDB::get_instance(p_id); |
322 | Node *node = Object::cast_to<Node>(obj); |
323 | ERR_FAIL_NULL(node); |
324 | ERR_FAIL_NULL(contact_monitor); |
325 | HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); |
326 | ERR_FAIL_COND(!E); |
327 | ERR_FAIL_COND(E->value.in_scene); |
328 | |
329 | contact_monitor->locked = true; |
330 | |
331 | E->value.in_scene = true; |
332 | emit_signal(SceneStringNames::get_singleton()->body_entered, node); |
333 | |
334 | for (int i = 0; i < E->value.shapes.size(); i++) { |
335 | emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); |
336 | } |
337 | |
338 | contact_monitor->locked = false; |
339 | } |
340 | |
341 | void RigidBody2D::_body_exit_tree(ObjectID p_id) { |
342 | Object *obj = ObjectDB::get_instance(p_id); |
343 | Node *node = Object::cast_to<Node>(obj); |
344 | ERR_FAIL_NULL(node); |
345 | ERR_FAIL_NULL(contact_monitor); |
346 | HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); |
347 | ERR_FAIL_COND(!E); |
348 | ERR_FAIL_COND(!E->value.in_scene); |
349 | E->value.in_scene = false; |
350 | |
351 | contact_monitor->locked = true; |
352 | |
353 | emit_signal(SceneStringNames::get_singleton()->body_exited, node); |
354 | |
355 | for (int i = 0; i < E->value.shapes.size(); i++) { |
356 | emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); |
357 | } |
358 | |
359 | contact_monitor->locked = false; |
360 | } |
361 | |
362 | void RigidBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { |
363 | bool body_in = p_status == 1; |
364 | ObjectID objid = p_instance; |
365 | |
366 | Object *obj = ObjectDB::get_instance(objid); |
367 | Node *node = Object::cast_to<Node>(obj); |
368 | |
369 | ERR_FAIL_NULL(contact_monitor); |
370 | HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(objid); |
371 | |
372 | ERR_FAIL_COND(!body_in && !E); |
373 | |
374 | if (body_in) { |
375 | if (!E) { |
376 | E = contact_monitor->body_map.insert(objid, BodyState()); |
377 | E->value.rid = p_body; |
378 | //E->value.rc=0; |
379 | E->value.in_scene = node && node->is_inside_tree(); |
380 | if (node) { |
381 | node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree).bind(objid)); |
382 | node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree).bind(objid)); |
383 | if (E->value.in_scene) { |
384 | emit_signal(SceneStringNames::get_singleton()->body_entered, node); |
385 | } |
386 | } |
387 | |
388 | //E->value.rc++; |
389 | } |
390 | |
391 | if (node) { |
392 | E->value.shapes.insert(ShapePair(p_body_shape, p_local_shape)); |
393 | } |
394 | |
395 | if (E->value.in_scene) { |
396 | emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_local_shape); |
397 | } |
398 | |
399 | } else { |
400 | //E->value.rc--; |
401 | |
402 | if (node) { |
403 | E->value.shapes.erase(ShapePair(p_body_shape, p_local_shape)); |
404 | } |
405 | |
406 | bool in_scene = E->value.in_scene; |
407 | |
408 | if (E->value.shapes.is_empty()) { |
409 | if (node) { |
410 | node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree)); |
411 | node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree)); |
412 | if (in_scene) { |
413 | emit_signal(SceneStringNames::get_singleton()->body_exited, node); |
414 | } |
415 | } |
416 | |
417 | contact_monitor->body_map.remove(E); |
418 | } |
419 | if (node && in_scene) { |
420 | emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, node, p_body_shape, p_local_shape); |
421 | } |
422 | } |
423 | } |
424 | |
425 | struct _RigidBody2DInOut { |
426 | RID rid; |
427 | ObjectID id; |
428 | int shape = 0; |
429 | int local_shape = 0; |
430 | }; |
431 | |
432 | void RigidBody2D::_sync_body_state(PhysicsDirectBodyState2D *p_state) { |
433 | if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) { |
434 | set_global_transform(p_state->get_transform()); |
435 | } |
436 | |
437 | linear_velocity = p_state->get_linear_velocity(); |
438 | angular_velocity = p_state->get_angular_velocity(); |
439 | |
440 | if (sleeping != p_state->is_sleeping()) { |
441 | sleeping = p_state->is_sleeping(); |
442 | emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); |
443 | } |
444 | } |
445 | |
446 | void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { |
447 | lock_callback(); |
448 | |
449 | set_block_transform_notify(true); // don't want notify (would feedback loop) |
450 | _sync_body_state(p_state); |
451 | |
452 | GDVIRTUAL_CALL(_integrate_forces, p_state); |
453 | |
454 | _sync_body_state(p_state); |
455 | set_block_transform_notify(false); // want it back |
456 | |
457 | if (contact_monitor) { |
458 | contact_monitor->locked = true; |
459 | |
460 | //untag all |
461 | int rc = 0; |
462 | for (KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { |
463 | for (int i = 0; i < E.value.shapes.size(); i++) { |
464 | E.value.shapes[i].tagged = false; |
465 | rc++; |
466 | } |
467 | } |
468 | |
469 | _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBody2DInOut)); |
470 | int toadd_count = 0; //state->get_contact_count(); |
471 | RigidBody2D_RemoveAction *toremove = (RigidBody2D_RemoveAction *)alloca(rc * sizeof(RigidBody2D_RemoveAction)); |
472 | int toremove_count = 0; |
473 | |
474 | //put the ones to add |
475 | |
476 | for (int i = 0; i < p_state->get_contact_count(); i++) { |
477 | RID col_rid = p_state->get_contact_collider(i); |
478 | ObjectID col_obj = p_state->get_contact_collider_id(i); |
479 | int local_shape = p_state->get_contact_local_shape(i); |
480 | int col_shape = p_state->get_contact_collider_shape(i); |
481 | |
482 | HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(col_obj); |
483 | if (!E) { |
484 | toadd[toadd_count].rid = col_rid; |
485 | toadd[toadd_count].local_shape = local_shape; |
486 | toadd[toadd_count].id = col_obj; |
487 | toadd[toadd_count].shape = col_shape; |
488 | toadd_count++; |
489 | continue; |
490 | } |
491 | |
492 | ShapePair sp(col_shape, local_shape); |
493 | int idx = E->value.shapes.find(sp); |
494 | if (idx == -1) { |
495 | toadd[toadd_count].rid = col_rid; |
496 | toadd[toadd_count].local_shape = local_shape; |
497 | toadd[toadd_count].id = col_obj; |
498 | toadd[toadd_count].shape = col_shape; |
499 | toadd_count++; |
500 | continue; |
501 | } |
502 | |
503 | E->value.shapes[idx].tagged = true; |
504 | } |
505 | |
506 | //put the ones to remove |
507 | |
508 | for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { |
509 | for (int i = 0; i < E.value.shapes.size(); i++) { |
510 | if (!E.value.shapes[i].tagged) { |
511 | toremove[toremove_count].rid = E.value.rid; |
512 | toremove[toremove_count].body_id = E.key; |
513 | toremove[toremove_count].pair = E.value.shapes[i]; |
514 | toremove_count++; |
515 | } |
516 | } |
517 | } |
518 | |
519 | //process removals |
520 | |
521 | for (int i = 0; i < toremove_count; i++) { |
522 | _body_inout(0, toremove[i].rid, toremove[i].body_id, toremove[i].pair.body_shape, toremove[i].pair.local_shape); |
523 | } |
524 | |
525 | //process additions |
526 | |
527 | for (int i = 0; i < toadd_count; i++) { |
528 | _body_inout(1, toadd[i].rid, toadd[i].id, toadd[i].shape, toadd[i].local_shape); |
529 | } |
530 | |
531 | contact_monitor->locked = false; |
532 | } |
533 | |
534 | unlock_callback(); |
535 | } |
536 | |
537 | void RigidBody2D::_apply_body_mode() { |
538 | if (freeze) { |
539 | switch (freeze_mode) { |
540 | case FREEZE_MODE_STATIC: { |
541 | set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); |
542 | } break; |
543 | case FREEZE_MODE_KINEMATIC: { |
544 | set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); |
545 | } break; |
546 | } |
547 | } else if (lock_rotation) { |
548 | set_body_mode(PhysicsServer2D::BODY_MODE_RIGID_LINEAR); |
549 | } else { |
550 | set_body_mode(PhysicsServer2D::BODY_MODE_RIGID); |
551 | } |
552 | } |
553 | |
554 | void RigidBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { |
555 | if (p_lock_rotation == lock_rotation) { |
556 | return; |
557 | } |
558 | |
559 | lock_rotation = p_lock_rotation; |
560 | _apply_body_mode(); |
561 | } |
562 | |
563 | bool RigidBody2D::is_lock_rotation_enabled() const { |
564 | return lock_rotation; |
565 | } |
566 | |
567 | void RigidBody2D::set_freeze_enabled(bool p_freeze) { |
568 | if (p_freeze == freeze) { |
569 | return; |
570 | } |
571 | |
572 | freeze = p_freeze; |
573 | _apply_body_mode(); |
574 | } |
575 | |
576 | bool RigidBody2D::is_freeze_enabled() const { |
577 | return freeze; |
578 | } |
579 | |
580 | void RigidBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { |
581 | if (p_freeze_mode == freeze_mode) { |
582 | return; |
583 | } |
584 | |
585 | freeze_mode = p_freeze_mode; |
586 | _apply_body_mode(); |
587 | } |
588 | |
589 | RigidBody2D::FreezeMode RigidBody2D::get_freeze_mode() const { |
590 | return freeze_mode; |
591 | } |
592 | |
593 | void RigidBody2D::set_mass(real_t p_mass) { |
594 | ERR_FAIL_COND(p_mass <= 0); |
595 | mass = p_mass; |
596 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_MASS, mass); |
597 | } |
598 | |
599 | real_t RigidBody2D::get_mass() const { |
600 | return mass; |
601 | } |
602 | |
603 | void RigidBody2D::set_inertia(real_t p_inertia) { |
604 | ERR_FAIL_COND(p_inertia < 0); |
605 | inertia = p_inertia; |
606 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia); |
607 | } |
608 | |
609 | real_t RigidBody2D::get_inertia() const { |
610 | return inertia; |
611 | } |
612 | |
613 | void RigidBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { |
614 | if (center_of_mass_mode == p_mode) { |
615 | return; |
616 | } |
617 | |
618 | center_of_mass_mode = p_mode; |
619 | |
620 | switch (center_of_mass_mode) { |
621 | case CENTER_OF_MASS_MODE_AUTO: { |
622 | center_of_mass = Vector2(); |
623 | PhysicsServer2D::get_singleton()->body_reset_mass_properties(get_rid()); |
624 | if (inertia != 0.0) { |
625 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia); |
626 | } |
627 | } break; |
628 | |
629 | case CENTER_OF_MASS_MODE_CUSTOM: { |
630 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); |
631 | } break; |
632 | } |
633 | } |
634 | |
635 | RigidBody2D::CenterOfMassMode RigidBody2D::get_center_of_mass_mode() const { |
636 | return center_of_mass_mode; |
637 | } |
638 | |
639 | void RigidBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { |
640 | if (center_of_mass == p_center_of_mass) { |
641 | return; |
642 | } |
643 | |
644 | ERR_FAIL_COND(center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM); |
645 | center_of_mass = p_center_of_mass; |
646 | |
647 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); |
648 | } |
649 | |
650 | const Vector2 &RigidBody2D::get_center_of_mass() const { |
651 | return center_of_mass; |
652 | } |
653 | |
654 | void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { |
655 | if (physics_material_override.is_valid()) { |
656 | physics_material_override->disconnect_changed(callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); |
657 | } |
658 | |
659 | physics_material_override = p_physics_material_override; |
660 | |
661 | if (physics_material_override.is_valid()) { |
662 | physics_material_override->connect_changed(callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); |
663 | } |
664 | _reload_physics_characteristics(); |
665 | } |
666 | |
667 | Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const { |
668 | return physics_material_override; |
669 | } |
670 | |
671 | void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) { |
672 | gravity_scale = p_gravity_scale; |
673 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE, gravity_scale); |
674 | } |
675 | |
676 | real_t RigidBody2D::get_gravity_scale() const { |
677 | return gravity_scale; |
678 | } |
679 | |
680 | void RigidBody2D::set_linear_damp_mode(DampMode p_mode) { |
681 | linear_damp_mode = p_mode; |
682 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode); |
683 | } |
684 | |
685 | RigidBody2D::DampMode RigidBody2D::get_linear_damp_mode() const { |
686 | return linear_damp_mode; |
687 | } |
688 | |
689 | void RigidBody2D::set_angular_damp_mode(DampMode p_mode) { |
690 | angular_damp_mode = p_mode; |
691 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode); |
692 | } |
693 | |
694 | RigidBody2D::DampMode RigidBody2D::get_angular_damp_mode() const { |
695 | return angular_damp_mode; |
696 | } |
697 | |
698 | void RigidBody2D::set_linear_damp(real_t p_linear_damp) { |
699 | ERR_FAIL_COND(p_linear_damp < -1); |
700 | linear_damp = p_linear_damp; |
701 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_LINEAR_DAMP, linear_damp); |
702 | } |
703 | |
704 | real_t RigidBody2D::get_linear_damp() const { |
705 | return linear_damp; |
706 | } |
707 | |
708 | void RigidBody2D::set_angular_damp(real_t p_angular_damp) { |
709 | ERR_FAIL_COND(p_angular_damp < -1); |
710 | angular_damp = p_angular_damp; |
711 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP, angular_damp); |
712 | } |
713 | |
714 | real_t RigidBody2D::get_angular_damp() const { |
715 | return angular_damp; |
716 | } |
717 | |
718 | void RigidBody2D::set_axis_velocity(const Vector2 &p_axis) { |
719 | Vector2 axis = p_axis.normalized(); |
720 | linear_velocity -= axis * axis.dot(linear_velocity); |
721 | linear_velocity += p_axis; |
722 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); |
723 | } |
724 | |
725 | void RigidBody2D::set_linear_velocity(const Vector2 &p_velocity) { |
726 | linear_velocity = p_velocity; |
727 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); |
728 | } |
729 | |
730 | Vector2 RigidBody2D::get_linear_velocity() const { |
731 | return linear_velocity; |
732 | } |
733 | |
734 | void RigidBody2D::set_angular_velocity(real_t p_velocity) { |
735 | angular_velocity = p_velocity; |
736 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); |
737 | } |
738 | |
739 | real_t RigidBody2D::get_angular_velocity() const { |
740 | return angular_velocity; |
741 | } |
742 | |
743 | void RigidBody2D::set_use_custom_integrator(bool p_enable) { |
744 | if (custom_integrator == p_enable) { |
745 | return; |
746 | } |
747 | |
748 | custom_integrator = p_enable; |
749 | PhysicsServer2D::get_singleton()->body_set_omit_force_integration(get_rid(), p_enable); |
750 | } |
751 | |
752 | bool RigidBody2D::is_using_custom_integrator() { |
753 | return custom_integrator; |
754 | } |
755 | |
756 | void RigidBody2D::set_sleeping(bool p_sleeping) { |
757 | sleeping = p_sleeping; |
758 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_SLEEPING, sleeping); |
759 | } |
760 | |
761 | void RigidBody2D::set_can_sleep(bool p_active) { |
762 | can_sleep = p_active; |
763 | PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_CAN_SLEEP, p_active); |
764 | } |
765 | |
766 | bool RigidBody2D::is_able_to_sleep() const { |
767 | return can_sleep; |
768 | } |
769 | |
770 | bool RigidBody2D::is_sleeping() const { |
771 | return sleeping; |
772 | } |
773 | |
774 | void RigidBody2D::set_max_contacts_reported(int p_amount) { |
775 | max_contacts_reported = p_amount; |
776 | PhysicsServer2D::get_singleton()->body_set_max_contacts_reported(get_rid(), p_amount); |
777 | } |
778 | |
779 | int RigidBody2D::get_max_contacts_reported() const { |
780 | return max_contacts_reported; |
781 | } |
782 | |
783 | int RigidBody2D::get_contact_count() const { |
784 | PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(get_rid()); |
785 | ERR_FAIL_NULL_V(bs, 0); |
786 | return bs->get_contact_count(); |
787 | } |
788 | |
789 | void RigidBody2D::apply_central_impulse(const Vector2 &p_impulse) { |
790 | PhysicsServer2D::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); |
791 | } |
792 | |
793 | void RigidBody2D::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { |
794 | PhysicsServer2D::get_singleton()->body_apply_impulse(get_rid(), p_impulse, p_position); |
795 | } |
796 | |
797 | void RigidBody2D::apply_torque_impulse(real_t p_torque) { |
798 | PhysicsServer2D::get_singleton()->body_apply_torque_impulse(get_rid(), p_torque); |
799 | } |
800 | |
801 | void RigidBody2D::apply_central_force(const Vector2 &p_force) { |
802 | PhysicsServer2D::get_singleton()->body_apply_central_force(get_rid(), p_force); |
803 | } |
804 | |
805 | void RigidBody2D::apply_force(const Vector2 &p_force, const Vector2 &p_position) { |
806 | PhysicsServer2D::get_singleton()->body_apply_force(get_rid(), p_force, p_position); |
807 | } |
808 | |
809 | void RigidBody2D::apply_torque(real_t p_torque) { |
810 | PhysicsServer2D::get_singleton()->body_apply_torque(get_rid(), p_torque); |
811 | } |
812 | |
813 | void RigidBody2D::add_constant_central_force(const Vector2 &p_force) { |
814 | PhysicsServer2D::get_singleton()->body_add_constant_central_force(get_rid(), p_force); |
815 | } |
816 | |
817 | void RigidBody2D::add_constant_force(const Vector2 &p_force, const Vector2 &p_position) { |
818 | PhysicsServer2D::get_singleton()->body_add_constant_force(get_rid(), p_force, p_position); |
819 | } |
820 | |
821 | void RigidBody2D::add_constant_torque(const real_t p_torque) { |
822 | PhysicsServer2D::get_singleton()->body_add_constant_torque(get_rid(), p_torque); |
823 | } |
824 | |
825 | void RigidBody2D::set_constant_force(const Vector2 &p_force) { |
826 | PhysicsServer2D::get_singleton()->body_set_constant_force(get_rid(), p_force); |
827 | } |
828 | |
829 | Vector2 RigidBody2D::get_constant_force() const { |
830 | return PhysicsServer2D::get_singleton()->body_get_constant_force(get_rid()); |
831 | } |
832 | |
833 | void RigidBody2D::set_constant_torque(real_t p_torque) { |
834 | PhysicsServer2D::get_singleton()->body_set_constant_torque(get_rid(), p_torque); |
835 | } |
836 | |
837 | real_t RigidBody2D::get_constant_torque() const { |
838 | return PhysicsServer2D::get_singleton()->body_get_constant_torque(get_rid()); |
839 | } |
840 | |
841 | void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { |
842 | ccd_mode = p_mode; |
843 | PhysicsServer2D::get_singleton()->body_set_continuous_collision_detection_mode(get_rid(), PhysicsServer2D::CCDMode(p_mode)); |
844 | } |
845 | |
846 | RigidBody2D::CCDMode RigidBody2D::get_continuous_collision_detection_mode() const { |
847 | return ccd_mode; |
848 | } |
849 | |
850 | TypedArray<Node2D> RigidBody2D::get_colliding_bodies() const { |
851 | ERR_FAIL_NULL_V(contact_monitor, TypedArray<Node2D>()); |
852 | |
853 | TypedArray<Node2D> ret; |
854 | ret.resize(contact_monitor->body_map.size()); |
855 | int idx = 0; |
856 | for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { |
857 | Object *obj = ObjectDB::get_instance(E.key); |
858 | if (!obj) { |
859 | ret.resize(ret.size() - 1); //ops |
860 | } else { |
861 | ret[idx++] = obj; |
862 | } |
863 | } |
864 | |
865 | return ret; |
866 | } |
867 | |
868 | void RigidBody2D::set_contact_monitor(bool p_enabled) { |
869 | if (p_enabled == is_contact_monitor_enabled()) { |
870 | return; |
871 | } |
872 | |
873 | if (!p_enabled) { |
874 | ERR_FAIL_COND_MSG(contact_monitor->locked, "Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\", false) instead." ); |
875 | |
876 | for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { |
877 | //clean up mess |
878 | Object *obj = ObjectDB::get_instance(E.key); |
879 | Node *node = Object::cast_to<Node>(obj); |
880 | |
881 | if (node) { |
882 | node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree)); |
883 | node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree)); |
884 | } |
885 | } |
886 | |
887 | memdelete(contact_monitor); |
888 | contact_monitor = nullptr; |
889 | } else { |
890 | contact_monitor = memnew(ContactMonitor); |
891 | contact_monitor->locked = false; |
892 | } |
893 | } |
894 | |
895 | bool RigidBody2D::is_contact_monitor_enabled() const { |
896 | return contact_monitor != nullptr; |
897 | } |
898 | |
899 | void RigidBody2D::_notification(int p_what) { |
900 | #ifdef TOOLS_ENABLED |
901 | switch (p_what) { |
902 | case NOTIFICATION_ENTER_TREE: { |
903 | if (Engine::get_singleton()->is_editor_hint()) { |
904 | set_notify_local_transform(true); // Used for warnings and only in editor. |
905 | } |
906 | } break; |
907 | |
908 | case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { |
909 | update_configuration_warnings(); |
910 | } break; |
911 | } |
912 | #endif |
913 | } |
914 | |
915 | PackedStringArray RigidBody2D::get_configuration_warnings() const { |
916 | Transform2D t = get_transform(); |
917 | |
918 | PackedStringArray warnings = CollisionObject2D::get_configuration_warnings(); |
919 | |
920 | if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) { |
921 | warnings.push_back(RTR("Size changes to RigidBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead." )); |
922 | } |
923 | |
924 | return warnings; |
925 | } |
926 | |
927 | void RigidBody2D::_bind_methods() { |
928 | ClassDB::bind_method(D_METHOD("set_mass" , "mass" ), &RigidBody2D::set_mass); |
929 | ClassDB::bind_method(D_METHOD("get_mass" ), &RigidBody2D::get_mass); |
930 | |
931 | ClassDB::bind_method(D_METHOD("get_inertia" ), &RigidBody2D::get_inertia); |
932 | ClassDB::bind_method(D_METHOD("set_inertia" , "inertia" ), &RigidBody2D::set_inertia); |
933 | |
934 | ClassDB::bind_method(D_METHOD("set_center_of_mass_mode" , "mode" ), &RigidBody2D::set_center_of_mass_mode); |
935 | ClassDB::bind_method(D_METHOD("get_center_of_mass_mode" ), &RigidBody2D::get_center_of_mass_mode); |
936 | |
937 | ClassDB::bind_method(D_METHOD("set_center_of_mass" , "center_of_mass" ), &RigidBody2D::set_center_of_mass); |
938 | ClassDB::bind_method(D_METHOD("get_center_of_mass" ), &RigidBody2D::get_center_of_mass); |
939 | |
940 | ClassDB::bind_method(D_METHOD("set_physics_material_override" , "physics_material_override" ), &RigidBody2D::set_physics_material_override); |
941 | ClassDB::bind_method(D_METHOD("get_physics_material_override" ), &RigidBody2D::get_physics_material_override); |
942 | |
943 | ClassDB::bind_method(D_METHOD("set_gravity_scale" , "gravity_scale" ), &RigidBody2D::set_gravity_scale); |
944 | ClassDB::bind_method(D_METHOD("get_gravity_scale" ), &RigidBody2D::get_gravity_scale); |
945 | |
946 | ClassDB::bind_method(D_METHOD("set_linear_damp_mode" , "linear_damp_mode" ), &RigidBody2D::set_linear_damp_mode); |
947 | ClassDB::bind_method(D_METHOD("get_linear_damp_mode" ), &RigidBody2D::get_linear_damp_mode); |
948 | |
949 | ClassDB::bind_method(D_METHOD("set_angular_damp_mode" , "angular_damp_mode" ), &RigidBody2D::set_angular_damp_mode); |
950 | ClassDB::bind_method(D_METHOD("get_angular_damp_mode" ), &RigidBody2D::get_angular_damp_mode); |
951 | |
952 | ClassDB::bind_method(D_METHOD("set_linear_damp" , "linear_damp" ), &RigidBody2D::set_linear_damp); |
953 | ClassDB::bind_method(D_METHOD("get_linear_damp" ), &RigidBody2D::get_linear_damp); |
954 | |
955 | ClassDB::bind_method(D_METHOD("set_angular_damp" , "angular_damp" ), &RigidBody2D::set_angular_damp); |
956 | ClassDB::bind_method(D_METHOD("get_angular_damp" ), &RigidBody2D::get_angular_damp); |
957 | |
958 | ClassDB::bind_method(D_METHOD("set_linear_velocity" , "linear_velocity" ), &RigidBody2D::set_linear_velocity); |
959 | ClassDB::bind_method(D_METHOD("get_linear_velocity" ), &RigidBody2D::get_linear_velocity); |
960 | |
961 | ClassDB::bind_method(D_METHOD("set_angular_velocity" , "angular_velocity" ), &RigidBody2D::set_angular_velocity); |
962 | ClassDB::bind_method(D_METHOD("get_angular_velocity" ), &RigidBody2D::get_angular_velocity); |
963 | |
964 | ClassDB::bind_method(D_METHOD("set_max_contacts_reported" , "amount" ), &RigidBody2D::set_max_contacts_reported); |
965 | ClassDB::bind_method(D_METHOD("get_max_contacts_reported" ), &RigidBody2D::get_max_contacts_reported); |
966 | ClassDB::bind_method(D_METHOD("get_contact_count" ), &RigidBody2D::get_contact_count); |
967 | |
968 | ClassDB::bind_method(D_METHOD("set_use_custom_integrator" , "enable" ), &RigidBody2D::set_use_custom_integrator); |
969 | ClassDB::bind_method(D_METHOD("is_using_custom_integrator" ), &RigidBody2D::is_using_custom_integrator); |
970 | |
971 | ClassDB::bind_method(D_METHOD("set_contact_monitor" , "enabled" ), &RigidBody2D::set_contact_monitor); |
972 | ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled" ), &RigidBody2D::is_contact_monitor_enabled); |
973 | |
974 | ClassDB::bind_method(D_METHOD("set_continuous_collision_detection_mode" , "mode" ), &RigidBody2D::set_continuous_collision_detection_mode); |
975 | ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode" ), &RigidBody2D::get_continuous_collision_detection_mode); |
976 | |
977 | ClassDB::bind_method(D_METHOD("set_axis_velocity" , "axis_velocity" ), &RigidBody2D::set_axis_velocity); |
978 | ClassDB::bind_method(D_METHOD("apply_central_impulse" , "impulse" ), &RigidBody2D::apply_central_impulse, Vector2()); |
979 | ClassDB::bind_method(D_METHOD("apply_impulse" , "impulse" , "position" ), &RigidBody2D::apply_impulse, Vector2()); |
980 | ClassDB::bind_method(D_METHOD("apply_torque_impulse" , "torque" ), &RigidBody2D::apply_torque_impulse); |
981 | |
982 | ClassDB::bind_method(D_METHOD("apply_central_force" , "force" ), &RigidBody2D::apply_central_force); |
983 | ClassDB::bind_method(D_METHOD("apply_force" , "force" , "position" ), &RigidBody2D::apply_force, Vector2()); |
984 | ClassDB::bind_method(D_METHOD("apply_torque" , "torque" ), &RigidBody2D::apply_torque); |
985 | |
986 | ClassDB::bind_method(D_METHOD("add_constant_central_force" , "force" ), &RigidBody2D::add_constant_central_force); |
987 | ClassDB::bind_method(D_METHOD("add_constant_force" , "force" , "position" ), &RigidBody2D::add_constant_force, Vector2()); |
988 | ClassDB::bind_method(D_METHOD("add_constant_torque" , "torque" ), &RigidBody2D::add_constant_torque); |
989 | |
990 | ClassDB::bind_method(D_METHOD("set_constant_force" , "force" ), &RigidBody2D::set_constant_force); |
991 | ClassDB::bind_method(D_METHOD("get_constant_force" ), &RigidBody2D::get_constant_force); |
992 | |
993 | ClassDB::bind_method(D_METHOD("set_constant_torque" , "torque" ), &RigidBody2D::set_constant_torque); |
994 | ClassDB::bind_method(D_METHOD("get_constant_torque" ), &RigidBody2D::get_constant_torque); |
995 | |
996 | ClassDB::bind_method(D_METHOD("set_sleeping" , "sleeping" ), &RigidBody2D::set_sleeping); |
997 | ClassDB::bind_method(D_METHOD("is_sleeping" ), &RigidBody2D::is_sleeping); |
998 | |
999 | ClassDB::bind_method(D_METHOD("set_can_sleep" , "able_to_sleep" ), &RigidBody2D::set_can_sleep); |
1000 | ClassDB::bind_method(D_METHOD("is_able_to_sleep" ), &RigidBody2D::is_able_to_sleep); |
1001 | |
1002 | ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled" , "lock_rotation" ), &RigidBody2D::set_lock_rotation_enabled); |
1003 | ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled" ), &RigidBody2D::is_lock_rotation_enabled); |
1004 | |
1005 | ClassDB::bind_method(D_METHOD("set_freeze_enabled" , "freeze_mode" ), &RigidBody2D::set_freeze_enabled); |
1006 | ClassDB::bind_method(D_METHOD("is_freeze_enabled" ), &RigidBody2D::is_freeze_enabled); |
1007 | |
1008 | ClassDB::bind_method(D_METHOD("set_freeze_mode" , "freeze_mode" ), &RigidBody2D::set_freeze_mode); |
1009 | ClassDB::bind_method(D_METHOD("get_freeze_mode" ), &RigidBody2D::get_freeze_mode); |
1010 | |
1011 | ClassDB::bind_method(D_METHOD("get_colliding_bodies" ), &RigidBody2D::get_colliding_bodies); |
1012 | |
1013 | GDVIRTUAL_BIND(_integrate_forces, "state" ); |
1014 | |
1015 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass" , PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp,suffix:kg" ), "set_mass" , "get_mass" ); |
1016 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override" , PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial" ), "set_physics_material_override" , "get_physics_material_override" ); |
1017 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale" , PROPERTY_HINT_RANGE, "-128,128,0.01" ), "set_gravity_scale" , "get_gravity_scale" ); |
1018 | ADD_GROUP("Mass Distribution" , "" ); |
1019 | ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode" , PROPERTY_HINT_ENUM, "Auto,Custom" , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode" , "get_center_of_mass_mode" ); |
1020 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass" , PROPERTY_HINT_RANGE, "-10,10,0.01,or_less,or_greater,suffix:px" ), "set_center_of_mass" , "get_center_of_mass" ); |
1021 | ADD_LINKED_PROPERTY("center_of_mass_mode" , "center_of_mass" ); |
1022 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia" , PROPERTY_HINT_RANGE, U"0,1000,0.01,or_greater,exp,suffix:kg\u22C5px\u00B2" ), "set_inertia" , "get_inertia" ); |
1023 | ADD_GROUP("Deactivation" , "" ); |
1024 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping" ), "set_sleeping" , "is_sleeping" ); |
1025 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep" ), "set_can_sleep" , "is_able_to_sleep" ); |
1026 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation" ), "set_lock_rotation_enabled" , "is_lock_rotation_enabled" ); |
1027 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze" ), "set_freeze_enabled" , "is_freeze_enabled" ); |
1028 | ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode" , PROPERTY_HINT_ENUM, "Static,Kinematic" ), "set_freeze_mode" , "get_freeze_mode" ); |
1029 | ADD_GROUP("Solver" , "" ); |
1030 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator" ), "set_use_custom_integrator" , "is_using_custom_integrator" ); |
1031 | ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd" , PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape" ), "set_continuous_collision_detection_mode" , "get_continuous_collision_detection_mode" ); |
1032 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_contacts_reported" , PROPERTY_HINT_RANGE, "0,64,1,or_greater" ), "set_max_contacts_reported" , "get_max_contacts_reported" ); |
1033 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor" ), "set_contact_monitor" , "is_contact_monitor_enabled" ); |
1034 | ADD_GROUP("Linear" , "linear_" ); |
1035 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity" , PROPERTY_HINT_NONE, "suffix:px/s" ), "set_linear_velocity" , "get_linear_velocity" ); |
1036 | ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode" , PROPERTY_HINT_ENUM, "Combine,Replace" ), "set_linear_damp_mode" , "get_linear_damp_mode" ); |
1037 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp" , PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater" ), "set_linear_damp" , "get_linear_damp" ); |
1038 | ADD_GROUP("Angular" , "angular_" ); |
1039 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_velocity" , PROPERTY_HINT_NONE, U"radians,suffix:\u00B0/s" ), "set_angular_velocity" , "get_angular_velocity" ); |
1040 | ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode" , PROPERTY_HINT_ENUM, "Combine,Replace" ), "set_angular_damp_mode" , "get_angular_damp_mode" ); |
1041 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp" , PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater" ), "set_angular_damp" , "get_angular_damp" ); |
1042 | ADD_GROUP("Constant Forces" , "constant_" ); |
1043 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_force" , PROPERTY_HINT_NONE, U"suffix:kg\u22C5px/s\u00B2" ), "set_constant_force" , "get_constant_force" ); |
1044 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_torque" , PROPERTY_HINT_NONE, U"suffix:kg\u22C5px\u00B2/s\u00B2/rad" ), "set_constant_torque" , "get_constant_torque" ); |
1045 | |
1046 | ADD_SIGNAL(MethodInfo("body_shape_entered" , PropertyInfo(Variant::RID, "body_rid" ), PropertyInfo(Variant::OBJECT, "body" , PROPERTY_HINT_RESOURCE_TYPE, "Node" ), PropertyInfo(Variant::INT, "body_shape_index" ), PropertyInfo(Variant::INT, "local_shape_index" ))); |
1047 | ADD_SIGNAL(MethodInfo("body_shape_exited" , PropertyInfo(Variant::RID, "body_rid" ), PropertyInfo(Variant::OBJECT, "body" , PROPERTY_HINT_RESOURCE_TYPE, "Node" ), PropertyInfo(Variant::INT, "body_shape_index" ), PropertyInfo(Variant::INT, "local_shape_index" ))); |
1048 | ADD_SIGNAL(MethodInfo("body_entered" , PropertyInfo(Variant::OBJECT, "body" , PROPERTY_HINT_RESOURCE_TYPE, "Node" ))); |
1049 | ADD_SIGNAL(MethodInfo("body_exited" , PropertyInfo(Variant::OBJECT, "body" , PROPERTY_HINT_RESOURCE_TYPE, "Node" ))); |
1050 | ADD_SIGNAL(MethodInfo("sleeping_state_changed" )); |
1051 | |
1052 | BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); |
1053 | BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); |
1054 | |
1055 | BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); |
1056 | BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); |
1057 | |
1058 | BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE); |
1059 | BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE); |
1060 | |
1061 | BIND_ENUM_CONSTANT(CCD_MODE_DISABLED); |
1062 | BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY); |
1063 | BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE); |
1064 | } |
1065 | |
1066 | void RigidBody2D::_validate_property(PropertyInfo &p_property) const { |
1067 | if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { |
1068 | if (p_property.name == "center_of_mass" ) { |
1069 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
1070 | } |
1071 | } |
1072 | } |
1073 | |
1074 | RigidBody2D::RigidBody2D() : |
1075 | PhysicsBody2D(PhysicsServer2D::BODY_MODE_RIGID) { |
1076 | PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), callable_mp(this, &RigidBody2D::_body_state_changed)); |
1077 | } |
1078 | |
1079 | RigidBody2D::~RigidBody2D() { |
1080 | if (contact_monitor) { |
1081 | memdelete(contact_monitor); |
1082 | } |
1083 | } |
1084 | |
1085 | void RigidBody2D::_reload_physics_characteristics() { |
1086 | if (physics_material_override.is_null()) { |
1087 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0); |
1088 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1); |
1089 | } else { |
1090 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); |
1091 | PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); |
1092 | } |
1093 | } |
1094 | |
1095 | ////////////////////////// |
1096 | |
1097 | // So, if you pass 45 as limit, avoid numerical precision errors when angle is 45. |
1098 | #define FLOOR_ANGLE_THRESHOLD 0.01 |
1099 | |
1100 | bool CharacterBody2D::move_and_slide() { |
1101 | // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. |
1102 | double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); |
1103 | |
1104 | Vector2 current_platform_velocity = platform_velocity; |
1105 | Transform2D gt = get_global_transform(); |
1106 | previous_position = gt.columns[2]; |
1107 | |
1108 | if ((on_floor || on_wall) && platform_rid.is_valid()) { |
1109 | bool excluded = false; |
1110 | if (on_floor) { |
1111 | excluded = (platform_floor_layers & platform_layer) == 0; |
1112 | } else if (on_wall) { |
1113 | excluded = (platform_wall_layers & platform_layer) == 0; |
1114 | } |
1115 | if (!excluded) { |
1116 | //this approach makes sure there is less delay between the actual body velocity and the one we saved |
1117 | PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid); |
1118 | if (bs) { |
1119 | Vector2 local_position = gt.columns[2] - bs->get_transform().columns[2]; |
1120 | current_platform_velocity = bs->get_velocity_at_local_position(local_position); |
1121 | } else { |
1122 | // Body is removed or destroyed, invalidate floor. |
1123 | current_platform_velocity = Vector2(); |
1124 | platform_rid = RID(); |
1125 | } |
1126 | } else { |
1127 | current_platform_velocity = Vector2(); |
1128 | } |
1129 | } |
1130 | |
1131 | motion_results.clear(); |
1132 | last_motion = Vector2(); |
1133 | |
1134 | bool was_on_floor = on_floor; |
1135 | on_floor = false; |
1136 | on_ceiling = false; |
1137 | on_wall = false; |
1138 | |
1139 | if (!current_platform_velocity.is_zero_approx()) { |
1140 | PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); |
1141 | parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. |
1142 | parameters.exclude_bodies.insert(platform_rid); |
1143 | if (platform_object_id.is_valid()) { |
1144 | parameters.exclude_objects.insert(platform_object_id); |
1145 | } |
1146 | |
1147 | PhysicsServer2D::MotionResult floor_result; |
1148 | if (move_and_collide(parameters, floor_result, false, false)) { |
1149 | motion_results.push_back(floor_result); |
1150 | _set_collision_direction(floor_result); |
1151 | } |
1152 | } |
1153 | |
1154 | if (motion_mode == MOTION_MODE_GROUNDED) { |
1155 | _move_and_slide_grounded(delta, was_on_floor); |
1156 | } else { |
1157 | _move_and_slide_floating(delta); |
1158 | } |
1159 | |
1160 | // Compute real velocity. |
1161 | real_velocity = get_position_delta() / delta; |
1162 | |
1163 | if (platform_on_leave != PLATFORM_ON_LEAVE_DO_NOTHING) { |
1164 | // Add last platform velocity when just left a moving platform. |
1165 | if (!on_floor && !on_wall) { |
1166 | if (platform_on_leave == PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY && current_platform_velocity.dot(up_direction) < 0) { |
1167 | current_platform_velocity = current_platform_velocity.slide(up_direction); |
1168 | } |
1169 | velocity += current_platform_velocity; |
1170 | } |
1171 | } |
1172 | |
1173 | return motion_results.size() > 0; |
1174 | } |
1175 | |
1176 | void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) { |
1177 | Vector2 motion = velocity * p_delta; |
1178 | Vector2 motion_slide_up = motion.slide(up_direction); |
1179 | |
1180 | Vector2 prev_floor_normal = floor_normal; |
1181 | |
1182 | platform_rid = RID(); |
1183 | platform_object_id = ObjectID(); |
1184 | floor_normal = Vector2(); |
1185 | platform_velocity = Vector2(); |
1186 | |
1187 | // No sliding on first attempt to keep floor motion stable when possible, |
1188 | // When stop on slope is enabled or when there is no up direction. |
1189 | bool sliding_enabled = !floor_stop_on_slope; |
1190 | // Constant speed can be applied only the first time sliding is enabled. |
1191 | bool can_apply_constant_speed = sliding_enabled; |
1192 | // If the platform's ceiling push down the body. |
1193 | bool apply_ceiling_velocity = false; |
1194 | bool first_slide = true; |
1195 | bool vel_dir_facing_up = velocity.dot(up_direction) > 0; |
1196 | Vector2 last_travel; |
1197 | |
1198 | for (int iteration = 0; iteration < max_slides; ++iteration) { |
1199 | PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); |
1200 | parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. |
1201 | |
1202 | Vector2 prev_position = parameters.from.columns[2]; |
1203 | |
1204 | PhysicsServer2D::MotionResult result; |
1205 | bool collided = move_and_collide(parameters, result, false, !sliding_enabled); |
1206 | |
1207 | last_motion = result.travel; |
1208 | |
1209 | if (collided) { |
1210 | motion_results.push_back(result); |
1211 | _set_collision_direction(result); |
1212 | |
1213 | // If we hit a ceiling platform, we set the vertical velocity to at least the platform one. |
1214 | if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) { |
1215 | // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward. |
1216 | if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) { |
1217 | apply_ceiling_velocity = true; |
1218 | Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity); |
1219 | Vector2 motion_vertical_velocity = up_direction * up_direction.dot(velocity); |
1220 | if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) { |
1221 | velocity = ceiling_vertical_velocity + velocity.slide(up_direction); |
1222 | } |
1223 | } |
1224 | } |
1225 | |
1226 | if (on_floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) { |
1227 | Transform2D gt = get_global_transform(); |
1228 | if (result.travel.length() <= margin + CMP_EPSILON) { |
1229 | gt.columns[2] -= result.travel; |
1230 | } |
1231 | set_global_transform(gt); |
1232 | velocity = Vector2(); |
1233 | last_motion = Vector2(); |
1234 | motion = Vector2(); |
1235 | break; |
1236 | } |
1237 | |
1238 | if (result.remainder.is_zero_approx()) { |
1239 | motion = Vector2(); |
1240 | break; |
1241 | } |
1242 | |
1243 | // Move on floor only checks. |
1244 | if (floor_block_on_wall && on_wall && motion_slide_up.dot(result.collision_normal) <= 0) { |
1245 | // Avoid to move forward on a wall if floor_block_on_wall is true. |
1246 | if (p_was_on_floor && !on_floor && !vel_dir_facing_up) { |
1247 | // If the movement is large the body can be prevented from reaching the walls. |
1248 | if (result.travel.length() <= margin + CMP_EPSILON) { |
1249 | // Cancels the motion. |
1250 | Transform2D gt = get_global_transform(); |
1251 | gt.columns[2] -= result.travel; |
1252 | set_global_transform(gt); |
1253 | } |
1254 | // Determines if you are on the ground. |
1255 | _snap_on_floor(true, false, true); |
1256 | velocity = Vector2(); |
1257 | last_motion = Vector2(); |
1258 | motion = Vector2(); |
1259 | break; |
1260 | } |
1261 | // Prevents the body from being able to climb a slope when it moves forward against the wall. |
1262 | else if (!on_floor) { |
1263 | motion = up_direction * up_direction.dot(result.remainder); |
1264 | motion = motion.slide(result.collision_normal); |
1265 | } else { |
1266 | motion = result.remainder; |
1267 | } |
1268 | } |
1269 | // Constant Speed when the slope is upward. |
1270 | else if (floor_constant_speed && is_on_floor_only() && can_apply_constant_speed && p_was_on_floor && motion.dot(result.collision_normal) < 0) { |
1271 | can_apply_constant_speed = false; |
1272 | Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized(); |
1273 | motion = motion_slide_norm * (motion_slide_up.length() - result.travel.slide(up_direction).length() - last_travel.slide(up_direction).length()); |
1274 | } |
1275 | // Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling. |
1276 | else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) { |
1277 | Vector2 slide_motion = result.remainder.slide(result.collision_normal); |
1278 | if (slide_motion.dot(velocity) > 0.0) { |
1279 | motion = slide_motion; |
1280 | } else { |
1281 | motion = Vector2(); |
1282 | } |
1283 | if (slide_on_ceiling && on_ceiling) { |
1284 | // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. |
1285 | if (vel_dir_facing_up) { |
1286 | velocity = velocity.slide(result.collision_normal); |
1287 | } else { |
1288 | // Avoid acceleration in slope when falling. |
1289 | velocity = up_direction * up_direction.dot(velocity); |
1290 | } |
1291 | } |
1292 | } |
1293 | // No sliding on first attempt to keep floor motion stable when possible. |
1294 | else { |
1295 | motion = result.remainder; |
1296 | if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) { |
1297 | velocity = velocity.slide(up_direction); |
1298 | motion = motion.slide(up_direction); |
1299 | } |
1300 | } |
1301 | |
1302 | last_travel = result.travel; |
1303 | } |
1304 | // When you move forward in a downward slope you don’t collide because you will be in the air. |
1305 | // This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied. |
1306 | else if (floor_constant_speed && first_slide && _on_floor_if_snapped(p_was_on_floor, vel_dir_facing_up)) { |
1307 | can_apply_constant_speed = false; |
1308 | sliding_enabled = true; |
1309 | Transform2D gt = get_global_transform(); |
1310 | gt.columns[2] = prev_position; |
1311 | set_global_transform(gt); |
1312 | |
1313 | Vector2 motion_slide_norm = motion.slide(prev_floor_normal).normalized(); |
1314 | motion = motion_slide_norm * (motion_slide_up.length()); |
1315 | collided = true; |
1316 | } |
1317 | |
1318 | can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled; |
1319 | sliding_enabled = true; |
1320 | first_slide = false; |
1321 | |
1322 | if (!collided || motion.is_zero_approx()) { |
1323 | break; |
1324 | } |
1325 | } |
1326 | |
1327 | _snap_on_floor(p_was_on_floor, vel_dir_facing_up); |
1328 | |
1329 | // Scales the horizontal velocity according to the wall slope. |
1330 | if (is_on_wall_only() && motion_slide_up.dot(motion_results.get(0).collision_normal) < 0) { |
1331 | Vector2 slide_motion = velocity.slide(motion_results.get(0).collision_normal); |
1332 | if (motion_slide_up.dot(slide_motion) < 0) { |
1333 | velocity = up_direction * up_direction.dot(velocity); |
1334 | } else { |
1335 | // Keeps the vertical motion from velocity and add the horizontal motion of the projection. |
1336 | velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction); |
1337 | } |
1338 | } |
1339 | |
1340 | // Reset the gravity accumulation when touching the ground. |
1341 | if (on_floor && !vel_dir_facing_up) { |
1342 | velocity = velocity.slide(up_direction); |
1343 | } |
1344 | } |
1345 | |
1346 | void CharacterBody2D::_move_and_slide_floating(double p_delta) { |
1347 | Vector2 motion = velocity * p_delta; |
1348 | |
1349 | platform_rid = RID(); |
1350 | platform_object_id = ObjectID(); |
1351 | floor_normal = Vector2(); |
1352 | platform_velocity = Vector2(); |
1353 | |
1354 | bool first_slide = true; |
1355 | for (int iteration = 0; iteration < max_slides; ++iteration) { |
1356 | PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); |
1357 | parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. |
1358 | |
1359 | PhysicsServer2D::MotionResult result; |
1360 | bool collided = move_and_collide(parameters, result, false, false); |
1361 | |
1362 | last_motion = result.travel; |
1363 | |
1364 | if (collided) { |
1365 | motion_results.push_back(result); |
1366 | _set_collision_direction(result); |
1367 | |
1368 | if (result.remainder.is_zero_approx()) { |
1369 | motion = Vector2(); |
1370 | break; |
1371 | } |
1372 | |
1373 | if (wall_min_slide_angle != 0 && result.get_angle(-velocity.normalized()) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { |
1374 | motion = Vector2(); |
1375 | } else if (first_slide) { |
1376 | Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized(); |
1377 | motion = motion_slide_norm * (motion.length() - result.travel.length()); |
1378 | } else { |
1379 | motion = result.remainder.slide(result.collision_normal); |
1380 | } |
1381 | |
1382 | if (motion.dot(velocity) <= 0.0) { |
1383 | motion = Vector2(); |
1384 | } |
1385 | } |
1386 | |
1387 | if (!collided || motion.is_zero_approx()) { |
1388 | break; |
1389 | } |
1390 | |
1391 | first_slide = false; |
1392 | } |
1393 | } |
1394 | void CharacterBody2D::apply_floor_snap() { |
1395 | _apply_floor_snap(); |
1396 | } |
1397 | |
1398 | // Method that avoids the p_wall_as_floor parameter for the public method. |
1399 | void CharacterBody2D::_apply_floor_snap(bool p_wall_as_floor) { |
1400 | if (on_floor) { |
1401 | return; |
1402 | } |
1403 | |
1404 | // Snap by at least collision margin to keep floor state consistent. |
1405 | real_t length = MAX(floor_snap_length, margin); |
1406 | |
1407 | PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); |
1408 | parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. |
1409 | parameters.collide_separation_ray = true; |
1410 | |
1411 | PhysicsServer2D::MotionResult result; |
1412 | if (move_and_collide(parameters, result, true, false)) { |
1413 | if ((result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) || |
1414 | (p_wall_as_floor && result.get_angle(-up_direction) > floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { |
1415 | on_floor = true; |
1416 | floor_normal = result.collision_normal; |
1417 | _set_platform_data(result); |
1418 | |
1419 | if (floor_stop_on_slope) { |
1420 | // move and collide may stray the object a bit because of pre un-stucking, |
1421 | // so only ensure that motion happens on floor direction in this case. |
1422 | if (result.travel.length() > margin) { |
1423 | result.travel = up_direction * up_direction.dot(result.travel); |
1424 | } else { |
1425 | result.travel = Vector2(); |
1426 | } |
1427 | } |
1428 | |
1429 | parameters.from.columns[2] += result.travel; |
1430 | set_global_transform(parameters.from); |
1431 | } |
1432 | } |
1433 | } |
1434 | |
1435 | void CharacterBody2D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up, bool p_wall_as_floor) { |
1436 | if (on_floor || !p_was_on_floor || p_vel_dir_facing_up) { |
1437 | return; |
1438 | } |
1439 | |
1440 | _apply_floor_snap(p_wall_as_floor); |
1441 | } |
1442 | |
1443 | bool CharacterBody2D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up) { |
1444 | if (up_direction == Vector2() || on_floor || !p_was_on_floor || p_vel_dir_facing_up) { |
1445 | return false; |
1446 | } |
1447 | |
1448 | // Snap by at least collision margin to keep floor state consistent. |
1449 | real_t length = MAX(floor_snap_length, margin); |
1450 | |
1451 | PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); |
1452 | parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. |
1453 | parameters.collide_separation_ray = true; |
1454 | |
1455 | PhysicsServer2D::MotionResult result; |
1456 | if (move_and_collide(parameters, result, true, false)) { |
1457 | if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { |
1458 | return true; |
1459 | } |
1460 | } |
1461 | |
1462 | return false; |
1463 | } |
1464 | |
1465 | void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResult &p_result) { |
1466 | if (motion_mode == MOTION_MODE_GROUNDED && p_result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor |
1467 | on_floor = true; |
1468 | floor_normal = p_result.collision_normal; |
1469 | _set_platform_data(p_result); |
1470 | } else if (motion_mode == MOTION_MODE_GROUNDED && p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling |
1471 | on_ceiling = true; |
1472 | } else { |
1473 | on_wall = true; |
1474 | wall_normal = p_result.collision_normal; |
1475 | // Don't apply wall velocity when the collider is a CharacterBody2D. |
1476 | if (Object::cast_to<CharacterBody2D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) { |
1477 | _set_platform_data(p_result); |
1478 | } |
1479 | } |
1480 | } |
1481 | |
1482 | void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) { |
1483 | platform_rid = p_result.collider; |
1484 | platform_object_id = p_result.collider_id; |
1485 | platform_velocity = p_result.collider_velocity; |
1486 | platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid); |
1487 | } |
1488 | |
1489 | const Vector2 &CharacterBody2D::get_velocity() const { |
1490 | return velocity; |
1491 | } |
1492 | |
1493 | void CharacterBody2D::set_velocity(const Vector2 &p_velocity) { |
1494 | velocity = p_velocity; |
1495 | } |
1496 | |
1497 | bool CharacterBody2D::is_on_floor() const { |
1498 | return on_floor; |
1499 | } |
1500 | |
1501 | bool CharacterBody2D::is_on_floor_only() const { |
1502 | return on_floor && !on_wall && !on_ceiling; |
1503 | } |
1504 | |
1505 | bool CharacterBody2D::is_on_wall() const { |
1506 | return on_wall; |
1507 | } |
1508 | |
1509 | bool CharacterBody2D::is_on_wall_only() const { |
1510 | return on_wall && !on_floor && !on_ceiling; |
1511 | } |
1512 | |
1513 | bool CharacterBody2D::is_on_ceiling() const { |
1514 | return on_ceiling; |
1515 | } |
1516 | |
1517 | bool CharacterBody2D::is_on_ceiling_only() const { |
1518 | return on_ceiling && !on_floor && !on_wall; |
1519 | } |
1520 | |
1521 | const Vector2 &CharacterBody2D::get_floor_normal() const { |
1522 | return floor_normal; |
1523 | } |
1524 | |
1525 | const Vector2 &CharacterBody2D::get_wall_normal() const { |
1526 | return wall_normal; |
1527 | } |
1528 | |
1529 | const Vector2 &CharacterBody2D::get_last_motion() const { |
1530 | return last_motion; |
1531 | } |
1532 | |
1533 | Vector2 CharacterBody2D::get_position_delta() const { |
1534 | return get_global_transform().columns[2] - previous_position; |
1535 | } |
1536 | |
1537 | const Vector2 &CharacterBody2D::get_real_velocity() const { |
1538 | return real_velocity; |
1539 | } |
1540 | |
1541 | real_t CharacterBody2D::get_floor_angle(const Vector2 &p_up_direction) const { |
1542 | ERR_FAIL_COND_V(p_up_direction == Vector2(), 0); |
1543 | return Math::acos(floor_normal.dot(p_up_direction)); |
1544 | } |
1545 | |
1546 | const Vector2 &CharacterBody2D::get_platform_velocity() const { |
1547 | return platform_velocity; |
1548 | } |
1549 | |
1550 | int CharacterBody2D::get_slide_collision_count() const { |
1551 | return motion_results.size(); |
1552 | } |
1553 | |
1554 | PhysicsServer2D::MotionResult CharacterBody2D::get_slide_collision(int p_bounce) const { |
1555 | ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), PhysicsServer2D::MotionResult()); |
1556 | return motion_results[p_bounce]; |
1557 | } |
1558 | |
1559 | Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) { |
1560 | ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), Ref<KinematicCollision2D>()); |
1561 | if (p_bounce >= slide_colliders.size()) { |
1562 | slide_colliders.resize(p_bounce + 1); |
1563 | } |
1564 | |
1565 | // Create a new instance when the cached reference is invalid or still in use in script. |
1566 | if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) { |
1567 | slide_colliders.write[p_bounce].instantiate(); |
1568 | slide_colliders.write[p_bounce]->owner = this; |
1569 | } |
1570 | |
1571 | slide_colliders.write[p_bounce]->result = motion_results[p_bounce]; |
1572 | return slide_colliders[p_bounce]; |
1573 | } |
1574 | |
1575 | Ref<KinematicCollision2D> CharacterBody2D::_get_last_slide_collision() { |
1576 | if (motion_results.size() == 0) { |
1577 | return Ref<KinematicCollision2D>(); |
1578 | } |
1579 | return _get_slide_collision(motion_results.size() - 1); |
1580 | } |
1581 | |
1582 | void CharacterBody2D::set_safe_margin(real_t p_margin) { |
1583 | margin = p_margin; |
1584 | } |
1585 | |
1586 | real_t CharacterBody2D::get_safe_margin() const { |
1587 | return margin; |
1588 | } |
1589 | |
1590 | bool CharacterBody2D::is_floor_stop_on_slope_enabled() const { |
1591 | return floor_stop_on_slope; |
1592 | } |
1593 | |
1594 | void CharacterBody2D::set_floor_stop_on_slope_enabled(bool p_enabled) { |
1595 | floor_stop_on_slope = p_enabled; |
1596 | } |
1597 | |
1598 | bool CharacterBody2D::is_floor_constant_speed_enabled() const { |
1599 | return floor_constant_speed; |
1600 | } |
1601 | |
1602 | void CharacterBody2D::set_floor_constant_speed_enabled(bool p_enabled) { |
1603 | floor_constant_speed = p_enabled; |
1604 | } |
1605 | |
1606 | bool CharacterBody2D::is_floor_block_on_wall_enabled() const { |
1607 | return floor_block_on_wall; |
1608 | } |
1609 | |
1610 | void CharacterBody2D::set_floor_block_on_wall_enabled(bool p_enabled) { |
1611 | floor_block_on_wall = p_enabled; |
1612 | } |
1613 | |
1614 | bool CharacterBody2D::is_slide_on_ceiling_enabled() const { |
1615 | return slide_on_ceiling; |
1616 | } |
1617 | |
1618 | void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) { |
1619 | slide_on_ceiling = p_enabled; |
1620 | } |
1621 | |
1622 | uint32_t CharacterBody2D::get_platform_floor_layers() const { |
1623 | return platform_floor_layers; |
1624 | } |
1625 | |
1626 | void CharacterBody2D::set_platform_floor_layers(uint32_t p_exclude_layers) { |
1627 | platform_floor_layers = p_exclude_layers; |
1628 | } |
1629 | |
1630 | uint32_t CharacterBody2D::get_platform_wall_layers() const { |
1631 | return platform_wall_layers; |
1632 | } |
1633 | |
1634 | void CharacterBody2D::set_platform_wall_layers(uint32_t p_exclude_layers) { |
1635 | platform_wall_layers = p_exclude_layers; |
1636 | } |
1637 | |
1638 | void CharacterBody2D::set_motion_mode(MotionMode p_mode) { |
1639 | motion_mode = p_mode; |
1640 | } |
1641 | |
1642 | CharacterBody2D::MotionMode CharacterBody2D::get_motion_mode() const { |
1643 | return motion_mode; |
1644 | } |
1645 | |
1646 | void CharacterBody2D::set_platform_on_leave(PlatformOnLeave p_on_leave_apply_velocity) { |
1647 | platform_on_leave = p_on_leave_apply_velocity; |
1648 | } |
1649 | |
1650 | CharacterBody2D::PlatformOnLeave CharacterBody2D::get_platform_on_leave() const { |
1651 | return platform_on_leave; |
1652 | } |
1653 | |
1654 | int CharacterBody2D::get_max_slides() const { |
1655 | return max_slides; |
1656 | } |
1657 | |
1658 | void CharacterBody2D::set_max_slides(int p_max_slides) { |
1659 | ERR_FAIL_COND(p_max_slides < 1); |
1660 | max_slides = p_max_slides; |
1661 | } |
1662 | |
1663 | real_t CharacterBody2D::get_floor_max_angle() const { |
1664 | return floor_max_angle; |
1665 | } |
1666 | |
1667 | void CharacterBody2D::set_floor_max_angle(real_t p_radians) { |
1668 | floor_max_angle = p_radians; |
1669 | } |
1670 | |
1671 | real_t CharacterBody2D::get_floor_snap_length() { |
1672 | return floor_snap_length; |
1673 | } |
1674 | |
1675 | void CharacterBody2D::set_floor_snap_length(real_t p_floor_snap_length) { |
1676 | ERR_FAIL_COND(p_floor_snap_length < 0); |
1677 | floor_snap_length = p_floor_snap_length; |
1678 | } |
1679 | |
1680 | real_t CharacterBody2D::get_wall_min_slide_angle() const { |
1681 | return wall_min_slide_angle; |
1682 | } |
1683 | |
1684 | void CharacterBody2D::set_wall_min_slide_angle(real_t p_radians) { |
1685 | wall_min_slide_angle = p_radians; |
1686 | } |
1687 | |
1688 | const Vector2 &CharacterBody2D::get_up_direction() const { |
1689 | return up_direction; |
1690 | } |
1691 | |
1692 | void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) { |
1693 | ERR_FAIL_COND_MSG(p_up_direction == Vector2(), "up_direction can't be equal to Vector2.ZERO, consider using Floating motion mode instead." ); |
1694 | up_direction = p_up_direction.normalized(); |
1695 | } |
1696 | |
1697 | void CharacterBody2D::_notification(int p_what) { |
1698 | switch (p_what) { |
1699 | case NOTIFICATION_ENTER_TREE: { |
1700 | // Reset move_and_slide() data. |
1701 | on_floor = false; |
1702 | platform_rid = RID(); |
1703 | platform_object_id = ObjectID(); |
1704 | on_ceiling = false; |
1705 | on_wall = false; |
1706 | motion_results.clear(); |
1707 | platform_velocity = Vector2(); |
1708 | } break; |
1709 | } |
1710 | } |
1711 | |
1712 | void CharacterBody2D::_bind_methods() { |
1713 | ClassDB::bind_method(D_METHOD("move_and_slide" ), &CharacterBody2D::move_and_slide); |
1714 | ClassDB::bind_method(D_METHOD("apply_floor_snap" ), &CharacterBody2D::apply_floor_snap); |
1715 | |
1716 | ClassDB::bind_method(D_METHOD("set_velocity" , "velocity" ), &CharacterBody2D::set_velocity); |
1717 | ClassDB::bind_method(D_METHOD("get_velocity" ), &CharacterBody2D::get_velocity); |
1718 | |
1719 | ClassDB::bind_method(D_METHOD("set_safe_margin" , "margin" ), &CharacterBody2D::set_safe_margin); |
1720 | ClassDB::bind_method(D_METHOD("get_safe_margin" ), &CharacterBody2D::get_safe_margin); |
1721 | ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled" ), &CharacterBody2D::is_floor_stop_on_slope_enabled); |
1722 | ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled" , "enabled" ), &CharacterBody2D::set_floor_stop_on_slope_enabled); |
1723 | ClassDB::bind_method(D_METHOD("set_floor_constant_speed_enabled" , "enabled" ), &CharacterBody2D::set_floor_constant_speed_enabled); |
1724 | ClassDB::bind_method(D_METHOD("is_floor_constant_speed_enabled" ), &CharacterBody2D::is_floor_constant_speed_enabled); |
1725 | ClassDB::bind_method(D_METHOD("set_floor_block_on_wall_enabled" , "enabled" ), &CharacterBody2D::set_floor_block_on_wall_enabled); |
1726 | ClassDB::bind_method(D_METHOD("is_floor_block_on_wall_enabled" ), &CharacterBody2D::is_floor_block_on_wall_enabled); |
1727 | ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled" , "enabled" ), &CharacterBody2D::set_slide_on_ceiling_enabled); |
1728 | ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled" ), &CharacterBody2D::is_slide_on_ceiling_enabled); |
1729 | |
1730 | ClassDB::bind_method(D_METHOD("set_platform_floor_layers" , "exclude_layer" ), &CharacterBody2D::set_platform_floor_layers); |
1731 | ClassDB::bind_method(D_METHOD("get_platform_floor_layers" ), &CharacterBody2D::get_platform_floor_layers); |
1732 | ClassDB::bind_method(D_METHOD("set_platform_wall_layers" , "exclude_layer" ), &CharacterBody2D::set_platform_wall_layers); |
1733 | ClassDB::bind_method(D_METHOD("get_platform_wall_layers" ), &CharacterBody2D::get_platform_wall_layers); |
1734 | |
1735 | ClassDB::bind_method(D_METHOD("get_max_slides" ), &CharacterBody2D::get_max_slides); |
1736 | ClassDB::bind_method(D_METHOD("set_max_slides" , "max_slides" ), &CharacterBody2D::set_max_slides); |
1737 | ClassDB::bind_method(D_METHOD("get_floor_max_angle" ), &CharacterBody2D::get_floor_max_angle); |
1738 | ClassDB::bind_method(D_METHOD("set_floor_max_angle" , "radians" ), &CharacterBody2D::set_floor_max_angle); |
1739 | ClassDB::bind_method(D_METHOD("get_floor_snap_length" ), &CharacterBody2D::get_floor_snap_length); |
1740 | ClassDB::bind_method(D_METHOD("set_floor_snap_length" , "floor_snap_length" ), &CharacterBody2D::set_floor_snap_length); |
1741 | ClassDB::bind_method(D_METHOD("get_wall_min_slide_angle" ), &CharacterBody2D::get_wall_min_slide_angle); |
1742 | ClassDB::bind_method(D_METHOD("set_wall_min_slide_angle" , "radians" ), &CharacterBody2D::set_wall_min_slide_angle); |
1743 | ClassDB::bind_method(D_METHOD("get_up_direction" ), &CharacterBody2D::get_up_direction); |
1744 | ClassDB::bind_method(D_METHOD("set_up_direction" , "up_direction" ), &CharacterBody2D::set_up_direction); |
1745 | ClassDB::bind_method(D_METHOD("set_motion_mode" , "mode" ), &CharacterBody2D::set_motion_mode); |
1746 | ClassDB::bind_method(D_METHOD("get_motion_mode" ), &CharacterBody2D::get_motion_mode); |
1747 | ClassDB::bind_method(D_METHOD("set_platform_on_leave" , "on_leave_apply_velocity" ), &CharacterBody2D::set_platform_on_leave); |
1748 | ClassDB::bind_method(D_METHOD("get_platform_on_leave" ), &CharacterBody2D::get_platform_on_leave); |
1749 | |
1750 | ClassDB::bind_method(D_METHOD("is_on_floor" ), &CharacterBody2D::is_on_floor); |
1751 | ClassDB::bind_method(D_METHOD("is_on_floor_only" ), &CharacterBody2D::is_on_floor_only); |
1752 | ClassDB::bind_method(D_METHOD("is_on_ceiling" ), &CharacterBody2D::is_on_ceiling); |
1753 | ClassDB::bind_method(D_METHOD("is_on_ceiling_only" ), &CharacterBody2D::is_on_ceiling_only); |
1754 | ClassDB::bind_method(D_METHOD("is_on_wall" ), &CharacterBody2D::is_on_wall); |
1755 | ClassDB::bind_method(D_METHOD("is_on_wall_only" ), &CharacterBody2D::is_on_wall_only); |
1756 | ClassDB::bind_method(D_METHOD("get_floor_normal" ), &CharacterBody2D::get_floor_normal); |
1757 | ClassDB::bind_method(D_METHOD("get_wall_normal" ), &CharacterBody2D::get_wall_normal); |
1758 | ClassDB::bind_method(D_METHOD("get_last_motion" ), &CharacterBody2D::get_last_motion); |
1759 | ClassDB::bind_method(D_METHOD("get_position_delta" ), &CharacterBody2D::get_position_delta); |
1760 | ClassDB::bind_method(D_METHOD("get_real_velocity" ), &CharacterBody2D::get_real_velocity); |
1761 | ClassDB::bind_method(D_METHOD("get_floor_angle" , "up_direction" ), &CharacterBody2D::get_floor_angle, DEFVAL(Vector2(0.0, -1.0))); |
1762 | ClassDB::bind_method(D_METHOD("get_platform_velocity" ), &CharacterBody2D::get_platform_velocity); |
1763 | ClassDB::bind_method(D_METHOD("get_slide_collision_count" ), &CharacterBody2D::get_slide_collision_count); |
1764 | ClassDB::bind_method(D_METHOD("get_slide_collision" , "slide_idx" ), &CharacterBody2D::_get_slide_collision); |
1765 | ClassDB::bind_method(D_METHOD("get_last_slide_collision" ), &CharacterBody2D::_get_last_slide_collision); |
1766 | |
1767 | ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode" , PROPERTY_HINT_ENUM, "Grounded,Floating" , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode" , "get_motion_mode" ); |
1768 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction" ), "set_up_direction" , "get_up_direction" ); |
1769 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity" , PROPERTY_HINT_NONE, "suffix:px/s" , PROPERTY_USAGE_NO_EDITOR), "set_velocity" , "get_velocity" ); |
1770 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling" ), "set_slide_on_ceiling_enabled" , "is_slide_on_ceiling_enabled" ); |
1771 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR), "set_max_slides" , "get_max_slides" ); |
1772 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle" , PROPERTY_HINT_RANGE, "0,180,0.1,radians" , PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle" , "get_wall_min_slide_angle" ); |
1773 | |
1774 | ADD_GROUP("Floor" , "floor_" ); |
1775 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope" ), "set_floor_stop_on_slope_enabled" , "is_floor_stop_on_slope_enabled" ); |
1776 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed" ), "set_floor_constant_speed_enabled" , "is_floor_constant_speed_enabled" ); |
1777 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall" ), "set_floor_block_on_wall_enabled" , "is_floor_block_on_wall_enabled" ); |
1778 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle" , PROPERTY_HINT_RANGE, "0,180,0.1,radians" ), "set_floor_max_angle" , "get_floor_max_angle" ); |
1779 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length" , PROPERTY_HINT_RANGE, "0,32,0.1,or_greater,suffix:px" ), "set_floor_snap_length" , "get_floor_snap_length" ); |
1780 | |
1781 | ADD_GROUP("Moving Platform" , "platform_" ); |
1782 | ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_on_leave" , PROPERTY_HINT_ENUM, "Add Velocity,Add Upward Velocity,Do Nothing" , PROPERTY_USAGE_DEFAULT), "set_platform_on_leave" , "get_platform_on_leave" ); |
1783 | ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_floor_layers" , PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_floor_layers" , "get_platform_floor_layers" ); |
1784 | ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_wall_layers" , PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_wall_layers" , "get_platform_wall_layers" ); |
1785 | |
1786 | ADD_GROUP("Collision" , "" ); |
1787 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "safe_margin" , PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:px" ), "set_safe_margin" , "get_safe_margin" ); |
1788 | |
1789 | BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); |
1790 | BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING); |
1791 | |
1792 | BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_VELOCITY); |
1793 | BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY); |
1794 | BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_DO_NOTHING); |
1795 | } |
1796 | |
1797 | void CharacterBody2D::_validate_property(PropertyInfo &p_property) const { |
1798 | if (motion_mode == MOTION_MODE_FLOATING) { |
1799 | if (p_property.name.begins_with("floor_" ) || p_property.name == "up_direction" || p_property.name == "slide_on_ceiling" ) { |
1800 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
1801 | } |
1802 | } else { |
1803 | if (p_property.name == "wall_min_slide_angle" ) { |
1804 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
1805 | } |
1806 | } |
1807 | } |
1808 | |
1809 | CharacterBody2D::CharacterBody2D() : |
1810 | PhysicsBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) { |
1811 | } |
1812 | |
1813 | CharacterBody2D::~CharacterBody2D() { |
1814 | for (int i = 0; i < slide_colliders.size(); i++) { |
1815 | if (slide_colliders[i].is_valid()) { |
1816 | slide_colliders.write[i]->owner = nullptr; |
1817 | } |
1818 | } |
1819 | } |
1820 | |
1821 | //////////////////////// |
1822 | |
1823 | Vector2 KinematicCollision2D::get_position() const { |
1824 | return result.collision_point; |
1825 | } |
1826 | |
1827 | Vector2 KinematicCollision2D::get_normal() const { |
1828 | return result.collision_normal; |
1829 | } |
1830 | |
1831 | Vector2 KinematicCollision2D::get_travel() const { |
1832 | return result.travel; |
1833 | } |
1834 | |
1835 | Vector2 KinematicCollision2D::get_remainder() const { |
1836 | return result.remainder; |
1837 | } |
1838 | |
1839 | real_t KinematicCollision2D::get_angle(const Vector2 &p_up_direction) const { |
1840 | ERR_FAIL_COND_V(p_up_direction == Vector2(), 0); |
1841 | return result.get_angle(p_up_direction); |
1842 | } |
1843 | |
1844 | real_t KinematicCollision2D::get_depth() const { |
1845 | return result.collision_depth; |
1846 | } |
1847 | |
1848 | Object *KinematicCollision2D::get_local_shape() const { |
1849 | if (!owner) { |
1850 | return nullptr; |
1851 | } |
1852 | uint32_t ownerid = owner->shape_find_owner(result.collision_local_shape); |
1853 | return owner->shape_owner_get_owner(ownerid); |
1854 | } |
1855 | |
1856 | Object *KinematicCollision2D::get_collider() const { |
1857 | if (result.collider_id.is_valid()) { |
1858 | return ObjectDB::get_instance(result.collider_id); |
1859 | } |
1860 | |
1861 | return nullptr; |
1862 | } |
1863 | |
1864 | ObjectID KinematicCollision2D::get_collider_id() const { |
1865 | return result.collider_id; |
1866 | } |
1867 | |
1868 | RID KinematicCollision2D::get_collider_rid() const { |
1869 | return result.collider; |
1870 | } |
1871 | |
1872 | Object *KinematicCollision2D::get_collider_shape() const { |
1873 | Object *collider = get_collider(); |
1874 | if (collider) { |
1875 | CollisionObject2D *obj2d = Object::cast_to<CollisionObject2D>(collider); |
1876 | if (obj2d) { |
1877 | uint32_t ownerid = obj2d->shape_find_owner(result.collider_shape); |
1878 | return obj2d->shape_owner_get_owner(ownerid); |
1879 | } |
1880 | } |
1881 | |
1882 | return nullptr; |
1883 | } |
1884 | |
1885 | int KinematicCollision2D::get_collider_shape_index() const { |
1886 | return result.collider_shape; |
1887 | } |
1888 | |
1889 | Vector2 KinematicCollision2D::get_collider_velocity() const { |
1890 | return result.collider_velocity; |
1891 | } |
1892 | |
1893 | void KinematicCollision2D::_bind_methods() { |
1894 | ClassDB::bind_method(D_METHOD("get_position" ), &KinematicCollision2D::get_position); |
1895 | ClassDB::bind_method(D_METHOD("get_normal" ), &KinematicCollision2D::get_normal); |
1896 | ClassDB::bind_method(D_METHOD("get_travel" ), &KinematicCollision2D::get_travel); |
1897 | ClassDB::bind_method(D_METHOD("get_remainder" ), &KinematicCollision2D::get_remainder); |
1898 | ClassDB::bind_method(D_METHOD("get_angle" , "up_direction" ), &KinematicCollision2D::get_angle, DEFVAL(Vector2(0.0, -1.0))); |
1899 | ClassDB::bind_method(D_METHOD("get_depth" ), &KinematicCollision2D::get_depth); |
1900 | ClassDB::bind_method(D_METHOD("get_local_shape" ), &KinematicCollision2D::get_local_shape); |
1901 | ClassDB::bind_method(D_METHOD("get_collider" ), &KinematicCollision2D::get_collider); |
1902 | ClassDB::bind_method(D_METHOD("get_collider_id" ), &KinematicCollision2D::get_collider_id); |
1903 | ClassDB::bind_method(D_METHOD("get_collider_rid" ), &KinematicCollision2D::get_collider_rid); |
1904 | ClassDB::bind_method(D_METHOD("get_collider_shape" ), &KinematicCollision2D::get_collider_shape); |
1905 | ClassDB::bind_method(D_METHOD("get_collider_shape_index" ), &KinematicCollision2D::get_collider_shape_index); |
1906 | ClassDB::bind_method(D_METHOD("get_collider_velocity" ), &KinematicCollision2D::get_collider_velocity); |
1907 | } |
1908 | |