Skip to content

Commit

Permalink
Linear velocity cleanup
Browse files Browse the repository at this point in the history
CharacterBody has a linear_velocity property to replace the argument in
move_and_slide.

StaticBody handles reporting linear/angular velocity correctly when
kinematic motion is used (in 3D, used in vehicle and navigation).
  • Loading branch information
pouleyKetchoupp committed Jun 4, 2021
1 parent b2bd9f4 commit 23abac9
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 62 deletions.
12 changes: 6 additions & 6 deletions doc/classes/CharacterBody2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,12 @@
</description>
</method>
<method name="move_and_slide">
<return type="Vector2">
<return type="void">
</return>
<argument index="0" name="linear_velocity" type="Vector2">
</argument>
<description>
Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
[code]linear_velocity[/code] is the velocity vector in pixels per second. Unlike in [method PhysicsBody2D.move_and_collide], you should [i]not[/i] multiply it by [code]delta[/code] — the physics engine handles applying the velocity.
Returns the [code]linear_velocity[/code] vector, rotated and/or scaled if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision].
Modifies [member linear_velocity] if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision].
</description>
</method>
</methods>
Expand All @@ -107,6 +104,9 @@
<member name="infinite_inertia" type="bool" setter="set_infinite_inertia_enabled" getter="is_infinite_inertia_enabled" default="true">
If [code]true[/code], the body will be able to push [RigidBody2D] nodes when calling [method move_and_slide], but it also won't detect any collisions with them. If [code]false[/code], it will interact with [RigidBody2D] nodes like with [StaticBody2D].
</member>
<member name="linear_velocity" type="Vector2" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector2( 0, 0 )">
Current velocity vector in pixels per second, used and modified during calls to [method move_and_slide].
</member>
<member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4">
Maximum number of times the body can change direction before it stops when calling [method move_and_slide].
</member>
Expand Down
12 changes: 6 additions & 6 deletions doc/classes/CharacterBody3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,12 @@
</description>
</method>
<method name="move_and_slide">
<return type="Vector3">
<return type="void">
</return>
<argument index="0" name="linear_velocity" type="Vector3">
</argument>
<description>
Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody3D] or [RigidBody3D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody3D] or [RigidBody3D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
[code]linear_velocity[/code] is the velocity vector (typically meters per second). Unlike in [method PhysicsBody3D.move_and_collide], you should [i]not[/i] multiply it by [code]delta[/code] — the physics engine handles applying the velocity.
Returns the [code]linear_velocity[/code] vector, rotated and/or scaled if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision].
Modifies [member linear_velocity] if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision].
</description>
</method>
</methods>
Expand All @@ -93,6 +90,9 @@
<member name="infinite_inertia" type="bool" setter="set_infinite_inertia_enabled" getter="is_infinite_inertia_enabled" default="true">
If [code]true[/code], the body will be able to push [RigidBody3D] nodes when calling [method move_and_slide], but it also won't detect any collisions with them. If [code]false[/code], it will interact with [RigidBody3D] nodes like with [StaticBody3D].
</member>
<member name="linear_velocity" type="Vector3" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector3( 0, 0, 0 )">
Current velocity vector (typically meters per second), used and modified during calls to [method move_and_slide].
</member>
<member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4">
Maximum number of times the body can change direction before it stops when calling [method move_and_slide].
</member>
Expand Down
30 changes: 20 additions & 10 deletions scene/2d/physics_body_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -917,9 +917,8 @@ void RigidBody2D::_reload_physics_characteristics() {
//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01

Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity) {
Vector2 body_velocity = p_linear_velocity;
Vector2 body_velocity_normal = body_velocity.normalized();
void CharacterBody2D::move_and_slide() {
Vector2 body_velocity_normal = linear_velocity.normalized();

bool was_on_floor = on_floor;

Expand All @@ -933,7 +932,7 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity) {
}

// 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());
Vector2 motion = (current_floor_velocity + linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());

on_floor = false;
on_floor_body = RID();
Expand Down Expand Up @@ -985,7 +984,8 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity) {
Transform2D gt = get_global_transform();
gt.elements[2] -= result.motion.slide(up_direction);
set_global_transform(gt);
return Vector2();
linear_velocity = Vector2();
return;
}
}
} else if (Math::acos(result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
Expand All @@ -996,7 +996,7 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity) {
}

motion = motion.slide(result.collision_normal);
body_velocity = body_velocity.slide(result.collision_normal);
linear_velocity = linear_velocity.slide(result.collision_normal);
}
}

Expand All @@ -1008,7 +1008,7 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity) {
}

if (!was_on_floor || snap == Vector2()) {
return body_velocity;
return;
}

// Apply snap.
Expand Down Expand Up @@ -1038,8 +1038,6 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity) {
set_global_transform(gt);
}
}

return body_velocity;
}

bool CharacterBody2D::separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result) {
Expand Down Expand Up @@ -1078,6 +1076,14 @@ bool CharacterBody2D::separate_raycast_shapes(PhysicsServer2D::MotionResult &r_r
}
}

const Vector2 &CharacterBody2D::get_linear_velocity() const {
return linear_velocity;
}

void CharacterBody2D::set_linear_velocity(const Vector2 &p_velocity) {
linear_velocity = p_velocity;
}

bool CharacterBody2D::is_on_floor() const {
return on_floor;
}
Expand Down Expand Up @@ -1242,7 +1248,10 @@ void CharacterBody2D::_notification(int p_what) {
}

void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity"), &CharacterBody2D::move_and_slide);
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide);

ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody2D::set_linear_velocity);
ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody2D::get_linear_velocity);

ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin);
Expand Down Expand Up @@ -1270,6 +1279,7 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &CharacterBody2D::set_sync_to_physics);
ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &CharacterBody2D::is_sync_to_physics_enabled);

ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides"), "set_max_slides", "get_max_slides");
Expand Down
7 changes: 6 additions & 1 deletion scene/2d/physics_body_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ class CharacterBody2D : public PhysicsBody2D {
Vector2 snap;
Vector2 up_direction = Vector2(0.0, -1.0);

Vector2 linear_velocity;

Vector2 floor_normal;
Vector2 floor_velocity;
RID on_floor_body;
Expand Down Expand Up @@ -312,7 +314,10 @@ class CharacterBody2D : public PhysicsBody2D {
static void _bind_methods();

public:
Vector2 move_and_slide(const Vector2 &p_linear_velocity);
void move_and_slide();

const Vector2 &get_linear_velocity() const;
void set_linear_velocity(const Vector2 &p_velocity);

bool is_on_floor() const;
bool is_on_wall() const;
Expand Down
79 changes: 46 additions & 33 deletions scene/3d/physics_body_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ Vector3 StaticBody3D::get_constant_angular_velocity() const {
return constant_angular_velocity;
}

Vector3 StaticBody3D::get_linear_velocity() const {
return linear_velocity;
}

Vector3 StaticBody3D::get_angular_velocity() const {
return angular_velocity;
}

void StaticBody3D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
#ifdef TOOLS_ENABLED
Expand Down Expand Up @@ -291,6 +299,18 @@ void StaticBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled");
}

void StaticBody3D::_direct_state_changed(Object *p_state) {
#ifdef DEBUG_ENABLED
PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state);
ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument");
#else
PhysicsDirectBodyState3D *state = (PhysicsDirectBodyState3D *)p_state; //trust it
#endif

linear_velocity = state->get_linear_velocity();
angular_velocity = state->get_angular_velocity();
}

StaticBody3D::StaticBody3D() :
PhysicsBody3D(PhysicsServer3D::BODY_MODE_STATIC) {
}
Expand All @@ -313,10 +333,14 @@ void StaticBody3D::_update_kinematic_motion() {
#endif

if (kinematic_motion) {
PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody3D::_direct_state_changed));

if (!constant_angular_velocity.is_equal_approx(Vector3()) || !constant_linear_velocity.is_equal_approx(Vector3())) {
set_physics_process_internal(true);
return;
}
} else {
PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable());
}

set_physics_process_internal(false);
Expand Down Expand Up @@ -929,31 +953,22 @@ void RigidBody3D::_reload_physics_characteristics() {

///////////////////////////////////////

Vector3 CharacterBody3D::get_linear_velocity() const {
return linear_velocity;
}

Vector3 CharacterBody3D::get_angular_velocity() const {
return angular_velocity;
}

//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01

Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity) {
Vector3 body_velocity = p_linear_velocity;
Vector3 body_velocity_normal = body_velocity.normalized();
void CharacterBody3D::move_and_slide() {
Vector3 body_velocity_normal = linear_velocity.normalized();

bool was_on_floor = on_floor;

for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
body_velocity[i] = 0;
linear_velocity[i] = 0.0;
}
}

// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
Vector3 motion = (floor_velocity + body_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
Vector3 motion = (floor_velocity + linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());

on_floor = false;
on_floor_body = RID();
Expand Down Expand Up @@ -1005,7 +1020,8 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity) {
Transform3D gt = get_global_transform();
gt.origin -= result.motion.slide(up_direction);
set_global_transform(gt);
return Vector3();
linear_velocity = Vector3();
return;
}
}
} else if (Math::acos(result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
Expand All @@ -1016,11 +1032,11 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity) {
}

motion = motion.slide(result.collision_normal);
body_velocity = body_velocity.slide(result.collision_normal);
linear_velocity = linear_velocity.slide(result.collision_normal);

for (int j = 0; j < 3; j++) {
if (locked_axis & (1 << j)) {
body_velocity[j] = 0;
linear_velocity[j] = 0.0;
}
}
}
Expand All @@ -1034,7 +1050,7 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity) {
}

if (!was_on_floor || snap == Vector3()) {
return body_velocity;
return;
}

// Apply snap.
Expand Down Expand Up @@ -1062,8 +1078,6 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity) {
set_global_transform(gt);
}
}

return body_velocity;
}

bool CharacterBody3D::separate_raycast_shapes(PhysicsServer3D::MotionResult &r_result) {
Expand Down Expand Up @@ -1110,6 +1124,14 @@ real_t CharacterBody3D::get_safe_margin() const {
return margin;
}

Vector3 CharacterBody3D::get_linear_velocity() const {
return linear_velocity;
}

void CharacterBody3D::set_linear_velocity(const Vector3 &p_velocity) {
linear_velocity = p_velocity;
}

bool CharacterBody3D::is_on_floor() const {
return on_floor;
}
Expand Down Expand Up @@ -1215,7 +1237,10 @@ void CharacterBody3D::_notification(int p_what) {
}

void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity"), &CharacterBody3D::move_and_slide);
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide);

ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody3D::set_linear_velocity);
ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody3D::get_linear_velocity);

ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin);
Expand All @@ -1241,6 +1266,7 @@ void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody3D::get_slide_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision);

ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides"), "set_max_slides", "get_max_slides");
Expand All @@ -1250,21 +1276,8 @@ void CharacterBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}

void CharacterBody3D::_direct_state_changed(Object *p_state) {
#ifdef DEBUG_ENABLED
PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state);
ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument");
#else
PhysicsDirectBodyState3D *state = (PhysicsDirectBodyState3D *)p_state; //trust it
#endif

linear_velocity = state->get_linear_velocity();
angular_velocity = state->get_angular_velocity();
}

CharacterBody3D::CharacterBody3D() :
PhysicsBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) {
PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &CharacterBody3D::_direct_state_changed));
}

CharacterBody3D::~CharacterBody3D() {
Expand Down
Loading

0 comments on commit 23abac9

Please sign in to comment.