Merge pull request #50314 from fabriceci/fix-2d-moving-platform-4
Fixing 2D moving platform logic
This commit is contained in:
commit
eb23e52542
10 changed files with 92 additions and 49 deletions
|
@ -820,6 +820,10 @@
|
|||
</argument>
|
||||
<argument index="5" name="result" type="PhysicsTestMotionResult2D" default="null">
|
||||
</argument>
|
||||
<argument index="6" name="exclude_raycast_shapes" type="bool" default="true">
|
||||
</argument>
|
||||
<argument index="7" name="exclude" type="Array" default="[]">
|
||||
</argument>
|
||||
<description>
|
||||
Returns [code]true[/code] if a collision would result from moving in the given direction from a given point in space. Margin increases the size of the shapes involved in the collision detection. [PhysicsTestMotionResult2D] can be passed to return additional information in.
|
||||
</description>
|
||||
|
|
|
@ -76,12 +76,12 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_i
|
|||
return Ref<KinematicCollision2D>();
|
||||
}
|
||||
|
||||
bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding) {
|
||||
bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding, const Set<RID> &p_exclude) {
|
||||
if (is_only_update_transform_changes_enabled()) {
|
||||
ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation.");
|
||||
}
|
||||
Transform2D gt = get_global_transform();
|
||||
bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes);
|
||||
bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes, p_exclude);
|
||||
|
||||
// Restore direction of motion to be along original motion,
|
||||
// in order to avoid sliding due to recovery,
|
||||
|
@ -960,11 +960,14 @@ void RigidBody2D::_reload_physics_characteristics() {
|
|||
|
||||
void CharacterBody2D::move_and_slide() {
|
||||
Vector2 body_velocity_normal = linear_velocity.normalized();
|
||||
|
||||
bool was_on_floor = on_floor;
|
||||
|
||||
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
|
||||
float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
|
||||
|
||||
Vector2 current_floor_velocity = floor_velocity;
|
||||
if (on_floor && on_floor_body.is_valid()) {
|
||||
|
||||
if ((on_floor || on_wall) && on_floor_body.is_valid()) {
|
||||
//this approach makes sure there is less delay between the actual body velocity and the one we saved
|
||||
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(on_floor_body);
|
||||
if (bs) {
|
||||
|
@ -972,20 +975,30 @@ void CharacterBody2D::move_and_slide() {
|
|||
}
|
||||
}
|
||||
|
||||
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
|
||||
Vector2 motion = (current_floor_velocity + linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
|
||||
|
||||
motion_results.clear();
|
||||
on_floor = false;
|
||||
on_floor_body = RID();
|
||||
on_ceiling = false;
|
||||
on_wall = false;
|
||||
motion_results.clear();
|
||||
floor_normal = Vector2();
|
||||
floor_velocity = Vector2();
|
||||
|
||||
if (current_floor_velocity != Vector2()) {
|
||||
PhysicsServer2D::MotionResult floor_result;
|
||||
Set<RID> exclude;
|
||||
exclude.insert(on_floor_body);
|
||||
if (move_and_collide(current_floor_velocity * delta, infinite_inertia, floor_result, true, false, false, false, exclude)) {
|
||||
motion_results.push_back(floor_result);
|
||||
_set_collision_direction(floor_result);
|
||||
}
|
||||
}
|
||||
|
||||
on_floor_body = RID();
|
||||
Vector2 motion = linear_velocity * delta;
|
||||
|
||||
// No sliding on first attempt to keep floor motion stable when possible,
|
||||
// when stop on slope is enabled.
|
||||
bool sliding_enabled = !stop_on_slope;
|
||||
|
||||
for (int iteration = 0; iteration < max_slides; ++iteration) {
|
||||
PhysicsServer2D::MotionResult result;
|
||||
bool found_collision = false;
|
||||
|
@ -1009,35 +1022,19 @@ void CharacterBody2D::move_and_slide() {
|
|||
found_collision = true;
|
||||
|
||||
motion_results.push_back(result);
|
||||
_set_collision_direction(result);
|
||||
|
||||
if (up_direction == Vector2()) {
|
||||
//all is a wall
|
||||
on_wall = true;
|
||||
} else {
|
||||
if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
|
||||
|
||||
on_floor = true;
|
||||
floor_normal = result.collision_normal;
|
||||
on_floor_body = result.collider;
|
||||
floor_velocity = result.collider_velocity;
|
||||
|
||||
if (stop_on_slope) {
|
||||
if ((body_velocity_normal + up_direction).length() < 0.01) {
|
||||
Transform2D gt = get_global_transform();
|
||||
if (result.motion.length() > margin) {
|
||||
gt.elements[2] -= result.motion.slide(up_direction);
|
||||
} else {
|
||||
gt.elements[2] -= result.motion;
|
||||
}
|
||||
set_global_transform(gt);
|
||||
linear_velocity = Vector2();
|
||||
return;
|
||||
}
|
||||
if (on_floor && stop_on_slope) {
|
||||
if ((body_velocity_normal + up_direction).length() < 0.01) {
|
||||
Transform2D gt = get_global_transform();
|
||||
if (result.motion.length() > margin) {
|
||||
gt.elements[2] -= result.motion.slide(up_direction);
|
||||
} else {
|
||||
gt.elements[2] -= result.motion;
|
||||
}
|
||||
} else if (Math::acos(result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
|
||||
on_ceiling = true;
|
||||
} else {
|
||||
on_wall = true;
|
||||
set_global_transform(gt);
|
||||
linear_velocity = Vector2();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1057,6 +1054,11 @@ void CharacterBody2D::move_and_slide() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!on_floor && !on_wall) {
|
||||
// Add last platform velocity when just left a moving platform.
|
||||
linear_velocity += current_floor_velocity;
|
||||
}
|
||||
|
||||
if (!was_on_floor || snap == Vector2()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1093,6 +1095,29 @@ void CharacterBody2D::move_and_slide() {
|
|||
}
|
||||
}
|
||||
|
||||
void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResult &p_result) {
|
||||
on_floor = false;
|
||||
on_ceiling = false;
|
||||
on_wall = false;
|
||||
if (up_direction == Vector2()) {
|
||||
//all is a wall
|
||||
on_wall = true;
|
||||
} else {
|
||||
if (Math::acos(p_result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
|
||||
on_floor = true;
|
||||
floor_normal = p_result.collision_normal;
|
||||
on_floor_body = p_result.collider;
|
||||
floor_velocity = p_result.collider_velocity;
|
||||
} else if (Math::acos(p_result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
|
||||
on_ceiling = true;
|
||||
} else {
|
||||
on_wall = true;
|
||||
on_floor_body = p_result.collider;
|
||||
floor_velocity = p_result.collider_velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CharacterBody2D::separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result) {
|
||||
PhysicsServer2D::SeparationResult sep_res[8]; //max 8 rays
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ protected:
|
|||
Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false, real_t p_margin = 0.08);
|
||||
|
||||
public:
|
||||
bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false, bool p_cancel_sliding = true);
|
||||
bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false, bool p_cancel_sliding = true, const Set<RID> &p_exclude = Set<RID>());
|
||||
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
|
||||
|
||||
TypedArray<PhysicsBody2D> get_collision_exceptions();
|
||||
|
@ -308,6 +308,7 @@ private:
|
|||
|
||||
const Vector2 &get_up_direction() const;
|
||||
void set_up_direction(const Vector2 &p_up_direction);
|
||||
void _set_collision_direction(const PhysicsServer2D::MotionResult &p_result);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
|
|
@ -946,7 +946,7 @@ void PhysicsServer2DSW::body_set_pickable(RID p_body, bool p_pickable) {
|
|||
body->set_pickable(p_pickable);
|
||||
}
|
||||
|
||||
bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result, bool p_exclude_raycast_shapes) {
|
||||
bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude) {
|
||||
Body2DSW *body = body_owner.getornull(p_body);
|
||||
ERR_FAIL_COND_V(!body, false);
|
||||
ERR_FAIL_COND_V(!body->get_space(), false);
|
||||
|
@ -954,7 +954,7 @@ bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from,
|
|||
|
||||
_update_shapes();
|
||||
|
||||
return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes);
|
||||
return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes, p_exclude);
|
||||
}
|
||||
|
||||
int PhysicsServer2DSW::body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin) {
|
||||
|
|
|
@ -247,7 +247,7 @@ public:
|
|||
|
||||
virtual void body_set_pickable(RID p_body, bool p_pickable) override;
|
||||
|
||||
virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override;
|
||||
virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set<RID> &p_exclude = Set<RID>()) override;
|
||||
virtual int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin = 0.08) override;
|
||||
|
||||
// this function only works on physics process, errors and returns null otherwise
|
||||
|
|
|
@ -253,9 +253,9 @@ public:
|
|||
|
||||
FUNC2(body_set_pickable, RID, bool);
|
||||
|
||||
bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override {
|
||||
bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set<RID> &p_exclude = Set<RID>()) override {
|
||||
ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
|
||||
return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes);
|
||||
return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes, p_exclude);
|
||||
}
|
||||
|
||||
int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin = 0.08) override {
|
||||
|
|
|
@ -709,7 +709,7 @@ int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_t
|
|||
return rays_found;
|
||||
}
|
||||
|
||||
bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_exclude_raycast_shapes) {
|
||||
bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude) {
|
||||
//give me back regular physics engine logic
|
||||
//this is madness
|
||||
//and most people using this function will think
|
||||
|
@ -801,6 +801,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
|
|||
Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
const CollisionObject2DSW *col_obj = intersection_query_results[i];
|
||||
if (p_exclude.has(col_obj->get_self())) {
|
||||
continue;
|
||||
}
|
||||
int shape_idx = intersection_query_subindex_results[i];
|
||||
|
||||
if (CollisionObject2DSW::TYPE_BODY == col_obj->get_type()) {
|
||||
|
@ -930,6 +933,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
|
|||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
const CollisionObject2DSW *col_obj = intersection_query_results[i];
|
||||
if (p_exclude.has(col_obj->get_self())) {
|
||||
continue;
|
||||
}
|
||||
int col_shape_idx = intersection_query_subindex_results[i];
|
||||
Shape2DSW *against_shape = col_obj->get_shape(col_shape_idx);
|
||||
|
||||
|
@ -1086,6 +1092,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
|
|||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
const CollisionObject2DSW *col_obj = intersection_query_results[i];
|
||||
if (p_exclude.has(col_obj->get_self())) {
|
||||
continue;
|
||||
}
|
||||
int shape_idx = intersection_query_subindex_results[i];
|
||||
|
||||
if (CollisionObject2DSW::TYPE_BODY == col_obj->get_type()) {
|
||||
|
|
|
@ -183,7 +183,7 @@ public:
|
|||
|
||||
int get_collision_pairs() const { return collision_pairs; }
|
||||
|
||||
bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_exclude_raycast_shapes = true);
|
||||
bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_exclude_raycast_shapes = true, const Set<RID> &p_exclude = Set<RID>());
|
||||
int test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, PhysicsServer2D::SeparationResult *r_results, int p_result_max, real_t p_margin);
|
||||
|
||||
void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); }
|
||||
|
|
|
@ -498,12 +498,16 @@ void PhysicsTestMotionResult2D::_bind_methods() {
|
|||
|
||||
///////////////////////////////////////
|
||||
|
||||
bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result) {
|
||||
bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result, bool p_exclude_raycast_shapes, const Vector<RID> &p_exclude) {
|
||||
MotionResult *r = nullptr;
|
||||
if (p_result.is_valid()) {
|
||||
r = p_result->get_result_ptr();
|
||||
}
|
||||
return body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r);
|
||||
Set<RID> exclude;
|
||||
for (int i = 0; i < p_exclude.size(); i++) {
|
||||
exclude.insert(p_exclude[i]);
|
||||
}
|
||||
return body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r, p_exclude_raycast_shapes, exclude);
|
||||
}
|
||||
|
||||
void PhysicsServer2D::_bind_methods() {
|
||||
|
@ -630,7 +634,7 @@ void PhysicsServer2D::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("body_set_force_integration_callback", "body", "callable", "userdata"), &PhysicsServer2D::body_set_force_integration_callback, DEFVAL(Variant()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "infinite_inertia", "margin", "result"), &PhysicsServer2D::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "infinite_inertia", "margin", "result", "exclude_raycast_shapes", "exclude"), &PhysicsServer2D::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()), DEFVAL(true), DEFVAL(Array()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer2D::body_get_direct_state);
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ class PhysicsServer2D : public Object {
|
|||
|
||||
static PhysicsServer2D *singleton;
|
||||
|
||||
virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>());
|
||||
virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>(), bool p_exclude_raycast_shapes = true, const Vector<RID> &p_exclude = Vector<RID>());
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -481,7 +481,7 @@ public:
|
|||
Variant collider_metadata;
|
||||
};
|
||||
|
||||
virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) = 0;
|
||||
virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set<RID> &p_exclude = Set<RID>()) = 0;
|
||||
|
||||
struct SeparationResult {
|
||||
real_t collision_depth;
|
||||
|
|
Loading…
Reference in a new issue