Merge pull request #50166 from fabriceci/fix-2d-moving-platform
[3.x] Fixing 2D moving platform logic
This commit is contained in:
commit
231efe0c6d
|
@ -822,6 +822,10 @@
|
|||
</argument>
|
||||
<argument index="5" name="result" type="Physics2DTestMotionResult" 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. [Physics2DTestMotionResult] can be passed to return additional information in.
|
||||
</description>
|
||||
|
|
|
@ -1030,13 +1030,14 @@ bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision
|
|||
}
|
||||
}
|
||||
|
||||
bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) {
|
||||
bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only, const Set<RID> &p_exclude) {
|
||||
if (sync_to_physics) {
|
||||
ERR_PRINT("Functions move_and_slide and move_and_collide do not work together with 'sync to physics' option. Please read the documentation.");
|
||||
}
|
||||
Transform2D gt = get_global_transform();
|
||||
Physics2DServer::MotionResult result;
|
||||
bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes);
|
||||
|
||||
bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes, p_exclude);
|
||||
|
||||
if (colliding) {
|
||||
r_collision.collider_metadata = result.collider_metadata;
|
||||
|
@ -1067,8 +1068,12 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|||
Vector2 body_velocity_normal = body_velocity.normalized();
|
||||
Vector2 up_direction = p_up_direction.normalized();
|
||||
|
||||
// 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
|
||||
Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body);
|
||||
if (bs) {
|
||||
|
@ -1076,17 +1081,26 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|||
}
|
||||
}
|
||||
|
||||
// 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 + body_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
|
||||
|
||||
colliders.clear();
|
||||
on_floor = false;
|
||||
on_floor_body = RID();
|
||||
on_ceiling = false;
|
||||
on_wall = false;
|
||||
colliders.clear();
|
||||
floor_normal = Vector2();
|
||||
floor_velocity = Vector2();
|
||||
|
||||
if (current_floor_velocity != Vector2()) {
|
||||
Collision floor_collision;
|
||||
Set<RID> exclude;
|
||||
exclude.insert(on_floor_body);
|
||||
if (move_and_collide(current_floor_velocity * delta, p_infinite_inertia, floor_collision, true, false, exclude)) {
|
||||
colliders.push_back(floor_collision);
|
||||
_set_collision_direction(floor_collision, up_direction, p_floor_max_angle);
|
||||
}
|
||||
}
|
||||
|
||||
on_floor_body = RID();
|
||||
Vector2 motion = body_velocity * delta;
|
||||
|
||||
while (p_max_slides) {
|
||||
Collision collision;
|
||||
bool found_collision = false;
|
||||
|
@ -1112,18 +1126,9 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|||
colliders.push_back(collision);
|
||||
motion = collision.remainder;
|
||||
|
||||
if (up_direction == Vector2()) {
|
||||
//all is a wall
|
||||
on_wall = true;
|
||||
} else {
|
||||
if (Math::acos(collision.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
|
||||
_set_collision_direction(collision, up_direction, p_floor_max_angle);
|
||||
|
||||
on_floor = true;
|
||||
floor_normal = collision.normal;
|
||||
on_floor_body = collision.collider_rid;
|
||||
floor_velocity = collision.collider_vel;
|
||||
|
||||
if (p_stop_on_slope) {
|
||||
if (on_floor && p_stop_on_slope) {
|
||||
if ((body_velocity_normal + up_direction).length() < 0.01 && collision.travel.length() < 1) {
|
||||
Transform2D gt = get_global_transform();
|
||||
gt.elements[2] -= collision.travel.slide(up_direction);
|
||||
|
@ -1131,12 +1136,6 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|||
return Vector2();
|
||||
}
|
||||
}
|
||||
} else if (Math::acos(collision.normal.dot(-up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
|
||||
on_ceiling = true;
|
||||
} else {
|
||||
on_wall = true;
|
||||
}
|
||||
}
|
||||
|
||||
motion = motion.slide(collision.normal);
|
||||
body_velocity = body_velocity.slide(collision.normal);
|
||||
|
@ -1150,6 +1149,11 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|||
--p_max_slides;
|
||||
}
|
||||
|
||||
if (!on_floor && !on_wall) {
|
||||
// Add last platform velocity when just left a moving platform.
|
||||
return body_velocity + current_floor_velocity;
|
||||
}
|
||||
|
||||
return body_velocity;
|
||||
}
|
||||
|
||||
|
@ -1193,6 +1197,29 @@ Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_veloci
|
|||
return ret;
|
||||
}
|
||||
|
||||
void KinematicBody2D::_set_collision_direction(const Collision &p_collision, const Vector2 &p_up_direction, float p_floor_max_angle) {
|
||||
on_floor = false;
|
||||
on_ceiling = false;
|
||||
on_wall = false;
|
||||
if (p_up_direction == Vector2()) {
|
||||
//all is a wall
|
||||
on_wall = true;
|
||||
} else {
|
||||
if (Math::acos(p_collision.normal.dot(p_up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
|
||||
on_floor = true;
|
||||
floor_normal = p_collision.normal;
|
||||
on_floor_body = p_collision.collider_rid;
|
||||
floor_velocity = p_collision.collider_vel;
|
||||
} else if (Math::acos(p_collision.normal.dot(-p_up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
|
||||
on_ceiling = true;
|
||||
} else {
|
||||
on_wall = true;
|
||||
on_floor_body = p_collision.collider_rid;
|
||||
floor_velocity = p_collision.collider_vel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KinematicBody2D::is_on_floor() const {
|
||||
return on_floor;
|
||||
}
|
||||
|
|
|
@ -304,13 +304,14 @@ private:
|
|||
|
||||
Transform2D last_valid_transform;
|
||||
void _direct_state_changed(Object *p_state);
|
||||
void _set_collision_direction(const Collision &p_collision, const Vector2 &p_up_direction, float p_floor_max_angle);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
|
||||
bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false, const Set<RID> &p_exclude = Set<RID>());
|
||||
|
||||
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia = true);
|
||||
|
||||
|
|
|
@ -933,7 +933,7 @@ void Physics2DServerSW::body_set_pickable(RID p_body, bool p_pickable) {
|
|||
body->set_pickable(p_pickable);
|
||||
}
|
||||
|
||||
bool Physics2DServerSW::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 Physics2DServerSW::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.get(p_body);
|
||||
ERR_FAIL_COND_V(!body, false);
|
||||
ERR_FAIL_COND_V(!body->get_space(), false);
|
||||
|
@ -941,7 +941,7 @@ bool Physics2DServerSW::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 Physics2DServerSW::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, float p_margin) {
|
||||
|
|
|
@ -246,7 +246,7 @@ public:
|
|||
|
||||
virtual void body_set_pickable(RID p_body, bool p_pickable);
|
||||
|
||||
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);
|
||||
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>());
|
||||
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, float p_margin = 0.08);
|
||||
|
||||
// this function only works on physics process, errors and returns null otherwise
|
||||
|
|
|
@ -251,9 +251,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) {
|
||||
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>()) {
|
||||
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, float p_margin = 0.08) {
|
||||
|
|
|
@ -692,7 +692,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, Physics2DServer::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, Physics2DServer::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
|
||||
|
@ -784,6 +784,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()) {
|
||||
|
@ -913,6 +916,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);
|
||||
|
||||
|
@ -1054,6 +1060,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()) {
|
||||
|
|
|
@ -184,7 +184,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, Physics2DServer::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, Physics2DServer::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, Physics2DServer::SeparationResult *r_results, int p_result_max, real_t p_margin);
|
||||
|
||||
void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); }
|
||||
|
|
|
@ -468,12 +468,16 @@ void Physics2DTestMotionResult::_bind_methods() {
|
|||
|
||||
///////////////////////////////////////
|
||||
|
||||
bool Physics2DServer::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin, const Ref<Physics2DTestMotionResult> &p_result) {
|
||||
bool Physics2DServer::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin, const Ref<Physics2DTestMotionResult> &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 Physics2DServer::_bind_methods() {
|
||||
|
@ -600,7 +604,7 @@ void Physics2DServer::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("body_set_force_integration_callback", "body", "receiver", "method", "userdata"), &Physics2DServer::body_set_force_integration_callback, DEFVAL(Variant()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "infinite_inertia", "margin", "result"), &Physics2DServer::_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"), &Physics2DServer::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()), DEFVAL(true), DEFVAL(Array()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &Physics2DServer::body_get_direct_state);
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ class Physics2DServer : public Object {
|
|||
|
||||
static Physics2DServer *singleton;
|
||||
|
||||
virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin = 0.08, const Ref<Physics2DTestMotionResult> &p_result = Ref<Physics2DTestMotionResult>());
|
||||
virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin = 0.08, const Ref<Physics2DTestMotionResult> &p_result = Ref<Physics2DTestMotionResult>(), bool p_exclude_raycast_shapes = true, const Vector<RID> &p_exclude = Vector<RID>());
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -476,7 +476,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, float 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, float p_margin = 0.08, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set<RID> &p_exclude = Set<RID>()) = 0;
|
||||
|
||||
struct SeparationResult {
|
||||
float collision_depth;
|
||||
|
|
Loading…
Reference in New Issue