Fixing 2D moving platform logic
Fixing by applying the movement in two steps, first the platform movement, and then the body movement. Plus, add the platform movement when we are on_wall.
This commit is contained in:
parent
91241d9944
commit
fba4c9d552
10 changed files with 88 additions and 43 deletions
|
@ -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,29 +1126,14 @@ 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 ((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);
|
||||
set_global_transform(gt);
|
||||
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;
|
||||
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);
|
||||
set_global_transform(gt);
|
||||
return Vector2();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 a new issue