|
@@ -1078,10 +1078,10 @@ void RigidBody::_reload_physics_characteristics() {
|
|
|
//////////////////////////////////////////////////////
|
|
|
//////////////////////////
|
|
|
|
|
|
-Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_infinite_inertia) {
|
|
|
+Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_infinite_inertia, bool p_test_only) {
|
|
|
|
|
|
Collision col;
|
|
|
- if (move_and_collide(p_motion, p_infinite_inertia, col)) {
|
|
|
+ if (move_and_collide(p_motion, p_infinite_inertia, col, p_test_only)) {
|
|
|
if (motion_cache.is_null()) {
|
|
|
motion_cache.instance();
|
|
|
motion_cache->owner = this;
|
|
@@ -1095,7 +1095,7 @@ Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_inf
|
|
|
return Ref<KinematicCollision>();
|
|
|
}
|
|
|
|
|
|
-bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision) {
|
|
|
+bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_test_only) {
|
|
|
|
|
|
Transform gt = get_global_transform();
|
|
|
PhysicsServer::MotionResult result;
|
|
@@ -1108,6 +1108,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in
|
|
|
r_collision.collision = result.collision_point;
|
|
|
r_collision.normal = result.collision_normal;
|
|
|
r_collision.collider = result.collider_id;
|
|
|
+ r_collision.collider_rid = result.collider;
|
|
|
r_collision.travel = result.motion;
|
|
|
r_collision.remainder = result.remainder;
|
|
|
r_collision.local_shape = result.collision_local_shape;
|
|
@@ -1119,8 +1120,10 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- gt.origin += result.motion;
|
|
|
- set_global_transform(gt);
|
|
|
+ if (!p_test_only) {
|
|
|
+ gt.origin += result.motion;
|
|
|
+ set_global_transform(gt);
|
|
|
+ }
|
|
|
|
|
|
return colliding;
|
|
|
}
|
|
@@ -1128,7 +1131,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in
|
|
|
//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45.
|
|
|
#define FLOOR_ANGLE_THRESHOLD 0.01
|
|
|
|
|
|
-Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
|
|
|
+Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
|
|
|
|
|
|
Vector3 lv = p_linear_velocity;
|
|
|
|
|
@@ -1146,69 +1149,127 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
|
|
|
colliders.clear();
|
|
|
floor_velocity = Vector3();
|
|
|
|
|
|
+ Vector3 lv_n = p_linear_velocity.normalized();
|
|
|
+
|
|
|
while (p_max_slides) {
|
|
|
|
|
|
Collision collision;
|
|
|
|
|
|
- bool collided = move_and_collide(motion, p_infinite_inertia, collision);
|
|
|
-
|
|
|
- if (collided) {
|
|
|
+ bool found_collision = false;
|
|
|
|
|
|
- motion = collision.remainder;
|
|
|
+ int test_type = 0;
|
|
|
|
|
|
- if (p_floor_direction == Vector3()) {
|
|
|
- //all is a wall
|
|
|
- on_wall = true;
|
|
|
+ do {
|
|
|
+ bool collided;
|
|
|
+ if (test_type == 0) { //collide
|
|
|
+ collided = move_and_collide(motion, p_infinite_inertia, collision);
|
|
|
+ if (!collided) {
|
|
|
+ motion = Vector3(); //clear because no collision happened and motion completed
|
|
|
+ }
|
|
|
} else {
|
|
|
- if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor
|
|
|
+ collided = separate_raycast_shapes(p_infinite_inertia, collision);
|
|
|
+ if (collided) {
|
|
|
+ collision.remainder = motion; //keep
|
|
|
+ collision.travel = Vector3();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- on_floor = true;
|
|
|
- floor_velocity = collision.collider_vel;
|
|
|
+ if (collided) {
|
|
|
+ found_collision = true;
|
|
|
+ }
|
|
|
|
|
|
- Vector3 rel_v = lv - floor_velocity;
|
|
|
- Vector3 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v);
|
|
|
+ if (collided) {
|
|
|
|
|
|
- if (collision.travel.length() < 0.05 && hv.length() < p_slope_stop_min_velocity) {
|
|
|
- Transform gt = get_global_transform();
|
|
|
- gt.origin -= collision.travel;
|
|
|
- set_global_transform(gt);
|
|
|
- return floor_velocity - p_floor_direction * p_floor_direction.dot(floor_velocity);
|
|
|
- }
|
|
|
- } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling
|
|
|
- on_ceiling = true;
|
|
|
- } else {
|
|
|
+ colliders.push_back(collision);
|
|
|
+ motion = collision.remainder;
|
|
|
+
|
|
|
+ bool is_on_slope = false;
|
|
|
+ if (p_floor_direction == Vector3()) {
|
|
|
+ //all is a wall
|
|
|
on_wall = true;
|
|
|
+ } else {
|
|
|
+ if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor
|
|
|
+
|
|
|
+ on_floor = true;
|
|
|
+ on_floor_body = collision.collider_rid;
|
|
|
+ floor_velocity = collision.collider_vel;
|
|
|
+
|
|
|
+ if (p_stop_on_slope) {
|
|
|
+ if (Vector3() == lv_n + p_floor_direction) {
|
|
|
+ Transform gt = get_global_transform();
|
|
|
+ gt.origin -= collision.travel;
|
|
|
+ set_global_transform(gt);
|
|
|
+ return Vector3();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ is_on_slope = true;
|
|
|
+
|
|
|
+ } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling
|
|
|
+ on_ceiling = true;
|
|
|
+ } else {
|
|
|
+ on_wall = true;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- Vector3 n = collision.normal;
|
|
|
- motion = motion.slide(n);
|
|
|
- lv = lv.slide(n);
|
|
|
+ if (p_stop_on_slope && is_on_slope) {
|
|
|
+ motion = motion.slide(p_floor_direction);
|
|
|
+ lv = lv.slide(p_floor_direction);
|
|
|
+ } else {
|
|
|
+ Vector3 n = collision.normal;
|
|
|
+ motion = motion.slide(n);
|
|
|
+ lv = lv.slide(n);
|
|
|
+ }
|
|
|
|
|
|
- for (int i = 0; i < 3; i++) {
|
|
|
- if (locked_axis & (1 << i)) {
|
|
|
- lv[i] = 0;
|
|
|
+ for (int i = 0; i < 3; i++) {
|
|
|
+ if (locked_axis & (1 << i)) {
|
|
|
+ lv[i] = 0;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- colliders.push_back(collision);
|
|
|
+ ++test_type;
|
|
|
+ } while (!p_stop_on_slope && test_type < 2);
|
|
|
|
|
|
- } else {
|
|
|
+ if (!found_collision || motion == Vector3())
|
|
|
break;
|
|
|
- }
|
|
|
|
|
|
- p_max_slides--;
|
|
|
- if (motion == Vector3())
|
|
|
- break;
|
|
|
+ --p_max_slides;
|
|
|
}
|
|
|
|
|
|
return lv;
|
|
|
}
|
|
|
|
|
|
+Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) {
|
|
|
+
|
|
|
+ bool was_on_floor = on_floor;
|
|
|
+
|
|
|
+ Vector3 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia);
|
|
|
+ if (!was_on_floor || p_snap == Vector3()) {
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ Collision col;
|
|
|
+ Transform gt = get_global_transform();
|
|
|
+
|
|
|
+ if (move_and_collide(p_snap, p_infinite_inertia, col, true)) {
|
|
|
+ gt.origin += col.travel;
|
|
|
+ if (p_floor_direction != Vector3() && Math::acos(p_floor_direction.normalized().dot(col.normal)) < p_floor_max_angle) {
|
|
|
+ on_floor = true;
|
|
|
+ on_floor_body = col.collider_rid;
|
|
|
+ floor_velocity = col.collider_vel;
|
|
|
+ }
|
|
|
+ set_global_transform(gt);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
bool KinematicBody::is_on_floor() const {
|
|
|
|
|
|
return on_floor;
|
|
|
}
|
|
|
+
|
|
|
bool KinematicBody::is_on_wall() const {
|
|
|
|
|
|
return on_wall;
|
|
@@ -1230,6 +1291,43 @@ bool KinematicBody::test_move(const Transform &p_from, const Vector3 &p_motion,
|
|
|
return PhysicsServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia);
|
|
|
}
|
|
|
|
|
|
+bool KinematicBody::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) {
|
|
|
+
|
|
|
+ PhysicsServer::SeparationResult sep_res[8]; //max 8 rays
|
|
|
+
|
|
|
+ Transform gt = get_global_transform();
|
|
|
+
|
|
|
+ Vector3 recover;
|
|
|
+ int hits = PhysicsServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin);
|
|
|
+ int deepest = -1;
|
|
|
+ float deepest_depth;
|
|
|
+ for (int i = 0; i < hits; i++) {
|
|
|
+ if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) {
|
|
|
+ deepest = i;
|
|
|
+ deepest_depth = sep_res[i].collision_depth;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gt.origin += recover;
|
|
|
+ set_global_transform(gt);
|
|
|
+
|
|
|
+ if (deepest != -1) {
|
|
|
+ r_collision.collider = sep_res[deepest].collider_id;
|
|
|
+ r_collision.collider_metadata = sep_res[deepest].collider_metadata;
|
|
|
+ r_collision.collider_shape = sep_res[deepest].collider_shape;
|
|
|
+ r_collision.collider_vel = sep_res[deepest].collider_velocity;
|
|
|
+ r_collision.collision = sep_res[deepest].collision_point;
|
|
|
+ r_collision.normal = sep_res[deepest].collision_normal;
|
|
|
+ r_collision.local_shape = sep_res[deepest].collision_local_shape;
|
|
|
+ r_collision.travel = recover;
|
|
|
+ r_collision.remainder = Vector3();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void KinematicBody::set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock) {
|
|
|
PhysicsServer::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock);
|
|
|
}
|
|
@@ -1276,8 +1374,9 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) {
|
|
|
|
|
|
void KinematicBody::_bind_methods() {
|
|
|
|
|
|
- ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody::_move, DEFVAL(true));
|
|
|
- ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
|
|
|
+ ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(false));
|
|
|
+ ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
|
|
|
+ ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move);
|
|
|
|