Linear velocity cleanup

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).
This commit is contained in:
PouleyKetchoupp 2021-06-01 20:13:25 -07:00
parent b2bd9f4e51
commit 23abac9325
6 changed files with 95 additions and 62 deletions

View file

@ -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>
@ -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>

View file

@ -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>
@ -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>

View file

@ -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;
@ -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();
@ -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
@ -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);
}
}
@ -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.
@ -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) {
@ -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;
}
@ -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);
@ -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");

View file

@ -268,6 +268,8 @@ private:
Vector2 snap;
Vector2 up_direction = Vector2(0.0, -1.0);
Vector2 linear_velocity;
Vector2 floor_normal;
Vector2 floor_velocity;
RID on_floor_body;
@ -312,7 +314,10 @@ protected:
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;

View file

@ -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
@ -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) {
}
@ -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);
@ -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();
@ -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
@ -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;
}
}
}
@ -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.
@ -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) {
@ -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;
}
@ -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);
@ -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");
@ -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() {

View file

@ -76,6 +76,9 @@ class StaticBody3D : public PhysicsBody3D {
Vector3 constant_linear_velocity;
Vector3 constant_angular_velocity;
Vector3 linear_velocity;
Vector3 angular_velocity;
Ref<PhysicsMaterial> physics_material_override;
bool kinematic_motion = false;
@ -84,6 +87,8 @@ protected:
void _notification(int p_what);
static void _bind_methods();
void _direct_state_changed(Object *p_state);
public:
void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
Ref<PhysicsMaterial> get_physics_material_override() const;
@ -94,6 +99,9 @@ public:
Vector3 get_constant_linear_velocity() const;
Vector3 get_constant_angular_velocity() const;
virtual Vector3 get_linear_velocity() const override;
virtual Vector3 get_angular_velocity() const override;
StaticBody3D();
private:
@ -270,7 +278,6 @@ private:
Vector3 up_direction = Vector3(0.0, 1.0, 0.0);
Vector3 linear_velocity;
Vector3 angular_velocity;
Vector3 floor_normal;
Vector3 floor_velocity;
@ -310,13 +317,11 @@ protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _direct_state_changed(Object *p_state);
public:
virtual Vector3 get_linear_velocity() const override;
virtual Vector3 get_angular_velocity() const override;
void move_and_slide();
Vector3 move_and_slide(const Vector3 &p_linear_velocity);
virtual Vector3 get_linear_velocity() const override;
void set_linear_velocity(const Vector3 &p_velocity);
bool is_on_floor() const;
bool is_on_wall() const;