Properties for move_and_slide and remove move_and_slide_with_snap

- snap property to replace move_and_slide_with_snap()
- floor_max_angle, stop_on_slope, infinite_inertia, max_slides,
up_direction properties to replace arguments from move_and_slide()
- up direction now defaults to Vector3.UP and Vector2.UP
This commit is contained in:
PouleyKetchoupp 2021-05-19 18:14:57 -07:00
parent ba13d23140
commit 287c3900fd
8 changed files with 286 additions and 152 deletions

View file

@ -19,14 +19,14 @@
<return type="Vector2">
</return>
<description>
Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] or [method move_and_slide_with_snap] and when [method is_on_floor] returns [code]true[/code].
Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
</description>
</method>
<method name="get_floor_velocity" qualifiers="const">
<return type="Vector2">
</return>
<description>
Returns the linear velocity of the floor at the last collision point. Only valid after calling [method move_and_slide] or [method move_and_slide_with_snap] and when [method is_on_floor] returns [code]true[/code].
Returns the linear velocity of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
</description>
</method>
<method name="get_slide_collision">
@ -35,7 +35,7 @@
<argument index="0" name="slide_idx" type="int">
</argument>
<description>
Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last call to [method move_and_slide] or [method move_and_slide_with_snap]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1).
Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last call to [method move_and_slide]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1).
[b]Example usage:[/b]
[codeblocks]
[gdscript]
@ -57,28 +57,28 @@
<return type="int">
</return>
<description>
Returns the number of times the body collided and changed direction during the last call to [method move_and_slide] or [method move_and_slide_with_snap].
Returns the number of times the body collided and changed direction during the last call to [method move_and_slide].
</description>
</method>
<method name="is_on_ceiling" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the body collided with the ceiling on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
Returns [code]true[/code] if the body collided with the ceiling on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_floor" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the body collided with the floor on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
Returns [code]true[/code] if the body collided with the floor on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_wall" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the body collided with a wall on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
Returns [code]true[/code] if the body collided with a wall on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="move_and_slide">
@ -86,55 +86,37 @@
</return>
<argument index="0" name="linear_velocity" type="Vector2">
</argument>
<argument index="1" name="up_direction" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<argument index="2" name="stop_on_slope" type="bool" default="false">
</argument>
<argument index="3" name="max_slides" type="int" default="4">
</argument>
<argument index="4" name="floor_max_angle" type="float" default="0.785398">
</argument>
<argument index="5" name="infinite_inertia" type="bool" default="true">
</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.
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.
[code]up_direction[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games.
If [code]stop_on_slope[/code] is [code]true[/code], body will not slide on slopes when you include gravity in [code]linear_velocity[/code] and the body is standing still.
If the body collides, it will change direction a maximum of [code]max_slides[/code] times before it stops.
[code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees.
If [code]infinite_inertia[/code] is [code]true[/code], body will be able to push [RigidBody2D] nodes, but it won't also detect any collisions with them. If [code]false[/code], it will interact with [RigidBody2D] nodes like with [StaticBody2D].
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].
</description>
</method>
<method name="move_and_slide_with_snap">
<return type="Vector2">
</return>
<argument index="0" name="linear_velocity" type="Vector2">
</argument>
<argument index="1" name="snap" type="Vector2">
</argument>
<argument index="2" name="up_direction" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<argument index="3" name="stop_on_slope" type="bool" default="false">
</argument>
<argument index="4" name="max_slides" type="int" default="4">
</argument>
<argument index="5" name="floor_max_angle" type="float" default="0.785398">
</argument>
<argument index="6" name="infinite_inertia" type="bool" default="true">
</argument>
<description>
Moves the body while keeping it attached to slopes. Similar to [method move_and_slide].
As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code](0, 0)[/code] or by using [method move_and_slide] instead.
</description>
</method>
</methods>
<members>
<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.785398">
Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
</member>
<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="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>
<member name="motion/sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false">
If [code]true[/code], the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method move_and_slide] or [method PhysicsBody2D.move_and_collide] functions.
</member>
<member name="snap" type="Vector2" setter="set_snap" getter="get_snap" default="Vector2( 0, 0 )">
When set to a value different from [code]Vector2(0, 0)[/code], the body is kept attached to slopes when calling [method move_and_slide].
As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code]Vector2(0, 0)[/code].
</member>
<member name="stop_on_slope" type="bool" setter="set_stop_on_slope_enabled" getter="is_stop_on_slope_enabled" default="false">
If [code]true[/code], the body will not slide on slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
</member>
<member name="up_direction" type="Vector2" setter="set_up_direction" getter="get_up_direction" default="Vector2( 0, -1 )">
Direction vector used to determine what is a wall and what is a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. Defaults to [code]Vector2.UP[/code]. If set to [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games.
</member>
</members>
<constants>
</constants>

View file

@ -20,14 +20,14 @@
<return type="Vector3">
</return>
<description>
Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] or [method move_and_slide_with_snap] and when [method is_on_floor] returns [code]true[/code].
Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
</description>
</method>
<method name="get_floor_velocity" qualifiers="const">
<return type="Vector3">
</return>
<description>
Returns the linear velocity of the floor at the last collision point. Only valid after calling [method move_and_slide] or [method move_and_slide_with_snap] and when [method is_on_floor] returns [code]true[/code].
Returns the linear velocity of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
</description>
</method>
<method name="get_slide_collision">
@ -36,35 +36,35 @@
<argument index="0" name="slide_idx" type="int">
</argument>
<description>
Returns a [KinematicCollision3D], which contains information about a collision that occurred during the last call to [method move_and_slide] or [method move_and_slide_with_snap]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1).
Returns a [KinematicCollision3D], which contains information about a collision that occurred during the last call to [method move_and_slide]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1).
</description>
</method>
<method name="get_slide_count" qualifiers="const">
<return type="int">
</return>
<description>
Returns the number of times the body collided and changed direction during the last call to [method move_and_slide] or [method move_and_slide_with_snap].
Returns the number of times the body collided and changed direction during the last call to [method move_and_slide].
</description>
</method>
<method name="is_on_ceiling" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the body collided with the ceiling on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
Returns [code]true[/code] if the body collided with the ceiling on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_floor" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the body collided with the floor on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
Returns [code]true[/code] if the body collided with the floor on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_wall" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the body collided with a wall on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
Returns [code]true[/code] if the body collided with a wall on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="move_and_slide">
@ -72,52 +72,34 @@
</return>
<argument index="0" name="linear_velocity" type="Vector3">
</argument>
<argument index="1" name="up_direction" type="Vector3" default="Vector3( 0, 0, 0 )">
</argument>
<argument index="2" name="stop_on_slope" type="bool" default="false">
</argument>
<argument index="3" name="max_slides" type="int" default="4">
</argument>
<argument index="4" name="floor_max_angle" type="float" default="0.785398">
</argument>
<argument index="5" name="infinite_inertia" type="bool" default="true">
</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.
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.
[code]up_direction[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector3(0, 0, 0)[/code], everything is considered a wall.
If [code]stop_on_slope[/code] is [code]true[/code], body will not slide on slopes when you include gravity in [code]linear_velocity[/code] and the body is standing still.
If the body collides, it will change direction a maximum of [code]max_slides[/code] times before it stops.
[code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees.
If [code]infinite_inertia[/code] is [code]true[/code], body will be able to push [RigidBody3D] nodes, but it won't also detect any collisions with them. If [code]false[/code], it will interact with [RigidBody3D] nodes like with [StaticBody3D].
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].
</description>
</method>
<method name="move_and_slide_with_snap">
<return type="Vector3">
</return>
<argument index="0" name="linear_velocity" type="Vector3">
</argument>
<argument index="1" name="snap" type="Vector3">
</argument>
<argument index="2" name="up_direction" type="Vector3" default="Vector3( 0, 0, 0 )">
</argument>
<argument index="3" name="stop_on_slope" type="bool" default="false">
</argument>
<argument index="4" name="max_slides" type="int" default="4">
</argument>
<argument index="5" name="floor_max_angle" type="float" default="0.785398">
</argument>
<argument index="6" name="infinite_inertia" type="bool" default="true">
</argument>
<description>
Moves the body while keeping it attached to slopes. Similar to [method move_and_slide].
As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code](0, 0, 0)[/code] or by using [method move_and_slide] instead.
</description>
</method>
</methods>
<members>
<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.785398">
Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
</member>
<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="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>
<member name="snap" type="Vector3" setter="set_snap" getter="get_snap" default="Vector3( 0, 0, 0 )">
When set to a value different from [code]Vector3(0, 0, 0)[/code], the body is kept attached to slopes when calling [method move_and_slide].
As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code]Vector3(0, 0, 0)[/code].
</member>
<member name="stop_on_slope" type="bool" setter="set_stop_on_slope_enabled" getter="is_stop_on_slope_enabled" default="false">
If [code]true[/code], the body will not slide on slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
</member>
<member name="up_direction" type="Vector3" setter="set_up_direction" getter="get_up_direction" default="Vector3( 0, 1, 0 )">
Direction vector used to determine what is a wall and what is a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. Defaults to [code]Vector3.UP[/code]. If set to [code]Vector3(0, 0, 0)[/code], everything is considered a wall. This is useful for topdown games.
</member>
</members>
<constants>
</constants>

View file

@ -72,7 +72,7 @@
</methods>
<members>
<member name="collision/safe_margin" type="float" setter="set_safe_margin" getter="get_safe_margin" default="0.08">
Extra margin used for collision recovery in motion functions (see [method move_and_collide], [method CharacterBody2D.move_and_slide], [method CharacterBody2D.move_and_slide_with_snap]).
Extra margin used for collision recovery in motion functions (see [method move_and_collide] and [method CharacterBody2D.move_and_slide]).
If the body is at least this close to another body, it will consider them to be colliding and will be pushed away before performing the actual motion.
A higher value means it's more flexible for detecting collision, which helps with consistently detecting walls and floors.
A lower value forces the collision algorithm to use more exact detection, so it can be used in cases that specifically require precision, e.g at very low scale to avoid visible jittering, or for stability with a stack of character bodies.

View file

@ -110,7 +110,7 @@
Lock the body's linear movement in the Z axis.
</member>
<member name="collision/safe_margin" type="float" setter="set_safe_margin" getter="get_safe_margin" default="0.001">
Extra margin used for collision recovery in motion functions (see [method move_and_collide], [method CharacterBody3D.move_and_slide], [method CharacterBody3D.move_and_slide_with_snap]).
Extra margin used for collision recovery in motion functions (see [method move_and_collide] and [method CharacterBody3D.move_and_slide]).
If the body is at least this close to another body, it will consider them to be colliding and will be pushed away before performing the actual motion.
A higher value means it's more flexible for detecting collision, which helps with consistently detecting walls and floors.
A lower value forces the collision algorithm to use more exact detection, so it can be used in cases that specifically require precision, e.g at very low scale to avoid visible jittering, or for stability with a stack of character bodies.

View file

@ -856,10 +856,11 @@ 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, const Vector2 &p_up_direction, bool p_stop_on_slope, int p_max_slides, real_t p_floor_max_angle, bool p_infinite_inertia) {
Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity) {
Vector2 body_velocity = p_linear_velocity;
Vector2 body_velocity_normal = body_velocity.normalized();
Vector2 up_direction = p_up_direction.normalized();
bool was_on_floor = on_floor;
Vector2 current_floor_velocity = floor_velocity;
if (on_floor && on_floor_body.is_valid()) {
@ -881,19 +882,20 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
floor_normal = Vector2();
floor_velocity = Vector2();
while (p_max_slides) {
int slide_count = max_slides;
while (slide_count) {
PhysicsServer2D::MotionResult result;
bool found_collision = false;
for (int i = 0; i < 2; ++i) {
bool collided;
if (i == 0) { //collide
collided = move_and_collide(motion, p_infinite_inertia, result);
collided = move_and_collide(motion, infinite_inertia, result);
if (!collided) {
motion = Vector2(); //clear because no collision happened and motion completed
}
} else { //separate raycasts (if any)
collided = separate_raycast_shapes(p_infinite_inertia, result);
collided = separate_raycast_shapes(result);
if (collided) {
result.remainder = motion; //keep
result.motion = Vector2();
@ -910,14 +912,14 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
//all is a wall
on_wall = true;
} else {
if (Math::acos(result.collision_normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = result.collision_normal;
on_floor_body = result.collider;
floor_velocity = result.collider_velocity;
if (p_stop_on_slope) {
if (stop_on_slope) {
if ((body_velocity_normal + up_direction).length() < 0.01 && result.motion.length() < 1) {
Transform2D gt = get_global_transform();
gt.elements[2] -= result.motion.slide(up_direction);
@ -925,7 +927,7 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
return Vector2();
}
}
} else if (Math::acos(result.collision_normal.dot(-up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
} else if (Math::acos(result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
@ -941,33 +943,25 @@ Vector2 CharacterBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
break;
}
--p_max_slides;
--slide_count;
}
return body_velocity;
}
Vector2 CharacterBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_up_direction, bool p_stop_on_slope, int p_max_slides, real_t p_floor_max_angle, bool p_infinite_inertia) {
Vector2 up_direction = p_up_direction.normalized();
bool was_on_floor = on_floor;
Vector2 ret = move_and_slide(p_linear_velocity, up_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia);
if (!was_on_floor || p_snap == Vector2()) {
return ret;
if (!was_on_floor || snap == Vector2()) {
return body_velocity;
}
PhysicsServer2D::MotionResult result;
// Apply snap.
Transform2D gt = get_global_transform();
if (move_and_collide(p_snap, p_infinite_inertia, result, false, true)) {
PhysicsServer2D::MotionResult result;
if (move_and_collide(snap, infinite_inertia, result, false, true)) {
bool apply = true;
if (up_direction != Vector2()) {
if (Math::acos(result.collision_normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
floor_normal = result.collision_normal;
on_floor_body = result.collider;
floor_velocity = result.collider_velocity;
if (p_stop_on_slope) {
if (stop_on_slope) {
// move and collide may stray the object a bit because of pre un-stucking,
// so only ensure that motion happens on floor direction in this case.
result.motion = up_direction * up_direction.dot(result.motion);
@ -984,7 +978,7 @@ Vector2 CharacterBody2D::move_and_slide_with_snap(const Vector2 &p_linear_veloci
}
}
return ret;
return body_velocity;
}
bool CharacterBody2D::separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result) {
@ -1106,6 +1100,54 @@ void CharacterBody2D::_direct_state_changed(Object *p_state) {
set_notify_local_transform(true);
}
bool CharacterBody2D::is_stop_on_slope_enabled() const {
return stop_on_slope;
}
void CharacterBody2D::set_stop_on_slope_enabled(bool p_enabled) {
stop_on_slope = p_enabled;
}
bool CharacterBody2D::is_infinite_inertia_enabled() const {
return infinite_inertia;
}
void CharacterBody2D::set_infinite_inertia_enabled(bool p_enabled) {
infinite_inertia = p_enabled;
}
int CharacterBody2D::get_max_slides() const {
return max_slides;
}
void CharacterBody2D::set_max_slides(int p_max_slides) {
ERR_FAIL_COND(p_max_slides > 0);
max_slides = p_max_slides;
}
real_t CharacterBody2D::get_floor_max_angle() const {
return floor_max_angle;
}
void CharacterBody2D::set_floor_max_angle(real_t p_floor_max_angle) {
floor_max_angle = p_floor_max_angle;
}
const Vector2 &CharacterBody2D::get_snap() const {
return snap;
}
void CharacterBody2D::set_snap(const Vector2 &p_snap) {
snap = p_snap;
}
const Vector2 &CharacterBody2D::get_up_direction() const {
return up_direction;
}
void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) {
up_direction = p_up_direction.normalized();
}
void CharacterBody2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
last_valid_transform = get_global_transform();
@ -1131,8 +1173,20 @@ void CharacterBody2D::_notification(int p_what) {
}
void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &CharacterBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true));
ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &CharacterBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true));
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity"), &CharacterBody2D::move_and_slide);
ClassDB::bind_method(D_METHOD("is_stop_on_slope_enabled"), &CharacterBody2D::is_stop_on_slope_enabled);
ClassDB::bind_method(D_METHOD("set_stop_on_slope_enabled", "enabled"), &CharacterBody2D::set_stop_on_slope_enabled);
ClassDB::bind_method(D_METHOD("is_infinite_inertia_enabled"), &CharacterBody2D::is_infinite_inertia_enabled);
ClassDB::bind_method(D_METHOD("set_infinite_inertia_enabled", "enabled"), &CharacterBody2D::set_infinite_inertia_enabled);
ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides);
ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides);
ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody2D::get_floor_max_angle);
ClassDB::bind_method(D_METHOD("set_floor_max_angle", "floor_max_angle"), &CharacterBody2D::set_floor_max_angle);
ClassDB::bind_method(D_METHOD("get_snap"), &CharacterBody2D::get_snap);
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CharacterBody2D::set_snap);
ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody2D::get_up_direction);
ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction);
ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor);
ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody2D::is_on_ceiling);
@ -1145,6 +1199,13 @@ 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::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");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
}

View file

@ -256,6 +256,13 @@ class CharacterBody2D : public PhysicsBody2D {
GDCLASS(CharacterBody2D, PhysicsBody2D);
private:
bool stop_on_slope = false;
bool infinite_inertia = true;
int max_slides = 4;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
Vector2 snap;
Vector2 up_direction = Vector2(0.0, -1.0);
Vector2 floor_normal;
Vector2 floor_velocity;
RID on_floor_body;
@ -267,22 +274,38 @@ private:
Vector<PhysicsServer2D::MotionResult> motion_results;
Vector<Ref<KinematicCollision2D>> slide_colliders;
_FORCE_INLINE_ bool _ignores_mode(PhysicsServer2D::BodyMode) const;
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
bool separate_raycast_shapes(bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result);
bool separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result);
Transform2D last_valid_transform;
void _direct_state_changed(Object *p_state);
bool is_stop_on_slope_enabled() const;
void set_stop_on_slope_enabled(bool p_enabled);
bool is_infinite_inertia_enabled() const;
void set_infinite_inertia_enabled(bool p_enabled);
int get_max_slides() const;
void set_max_slides(int p_max_slides);
real_t get_floor_max_angle() const;
void set_floor_max_angle(real_t p_floor_max_angle);
const Vector2 &get_snap() const;
void set_snap(const Vector2 &p_snap);
const Vector2 &get_up_direction() const;
void set_up_direction(const Vector2 &p_up_direction);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_up_direction = Vector2(0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, real_t p_floor_max_angle = Math::deg2rad((real_t)45.0), bool p_infinite_inertia = true);
Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_up_direction = Vector2(0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, real_t p_floor_max_angle = Math::deg2rad((real_t)45.0), bool p_infinite_inertia = true);
Vector2 move_and_slide(const Vector2 &p_linear_velocity);
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;

View file

@ -872,10 +872,11 @@ Vector3 CharacterBody3D::get_angular_velocity() const {
//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, const Vector3 &p_up_direction, bool p_stop_on_slope, int p_max_slides, real_t p_floor_max_angle, bool p_infinite_inertia) {
Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity) {
Vector3 body_velocity = p_linear_velocity;
Vector3 body_velocity_normal = body_velocity.normalized();
Vector3 up_direction = p_up_direction.normalized();
bool was_on_floor = on_floor;
for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
@ -894,19 +895,20 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity, const
floor_normal = Vector3();
floor_velocity = Vector3();
while (p_max_slides) {
int slide_count = max_slides;
while (slide_count) {
PhysicsServer3D::MotionResult result;
bool found_collision = false;
for (int i = 0; i < 2; ++i) {
bool collided;
if (i == 0) { //collide
collided = move_and_collide(motion, p_infinite_inertia, result);
collided = move_and_collide(motion, infinite_inertia, result);
if (!collided) {
motion = Vector3(); //clear because no collision happened and motion completed
}
} else { //separate raycasts (if any)
collided = separate_raycast_shapes(p_infinite_inertia, result);
collided = separate_raycast_shapes(result);
if (collided) {
result.remainder = motion; //keep
result.motion = Vector3();
@ -923,14 +925,14 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity, const
//all is a wall
on_wall = true;
} else {
if (Math::acos(result.collision_normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = result.collision_normal;
on_floor_body = result.collider;
floor_velocity = result.collider_velocity;
if (p_stop_on_slope) {
if (stop_on_slope) {
if ((body_velocity_normal + up_direction).length() < 0.01 && result.motion.length() < 1) {
Transform3D gt = get_global_transform();
gt.origin -= result.motion.slide(up_direction);
@ -938,7 +940,7 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity, const
return Vector3();
}
}
} else if (Math::acos(result.collision_normal.dot(-up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
} else if (Math::acos(result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
@ -960,33 +962,25 @@ Vector3 CharacterBody3D::move_and_slide(const Vector3 &p_linear_velocity, const
break;
}
--p_max_slides;
--slide_count;
}
return body_velocity;
}
Vector3 CharacterBody3D::move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_up_direction, bool p_stop_on_slope, int p_max_slides, real_t p_floor_max_angle, bool p_infinite_inertia) {
Vector3 up_direction = p_up_direction.normalized();
bool was_on_floor = on_floor;
Vector3 ret = move_and_slide(p_linear_velocity, up_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia);
if (!was_on_floor || p_snap == Vector3()) {
return ret;
if (!was_on_floor || snap == Vector3()) {
return body_velocity;
}
PhysicsServer3D::MotionResult result;
// Apply snap.
Transform3D gt = get_global_transform();
if (move_and_collide(p_snap, p_infinite_inertia, result, false, true)) {
PhysicsServer3D::MotionResult result;
if (move_and_collide(snap, infinite_inertia, result, false, true)) {
bool apply = true;
if (up_direction != Vector3()) {
if (Math::acos(result.collision_normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
floor_normal = result.collision_normal;
on_floor_body = result.collider;
floor_velocity = result.collider_velocity;
if (p_stop_on_slope) {
if (stop_on_slope) {
// move and collide may stray the object a bit because of pre un-stucking,
// so only ensure that motion happens on floor direction in this case.
result.motion = result.motion.project(up_direction);
@ -1001,7 +995,7 @@ Vector3 CharacterBody3D::move_and_slide_with_snap(const Vector3 &p_linear_veloci
}
}
return ret;
return body_velocity;
}
bool CharacterBody3D::separate_raycast_shapes(PhysicsServer3D::MotionResult &r_result) {
@ -1084,6 +1078,54 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) {
return slide_colliders[p_bounce];
}
bool CharacterBody3D::is_stop_on_slope_enabled() const {
return stop_on_slope;
}
void CharacterBody3D::set_stop_on_slope_enabled(bool p_enabled) {
stop_on_slope = p_enabled;
}
bool CharacterBody3D::is_infinite_inertia_enabled() const {
return infinite_inertia;
}
void CharacterBody3D::set_infinite_inertia_enabled(bool p_enabled) {
infinite_inertia = p_enabled;
}
int CharacterBody3D::get_max_slides() const {
return max_slides;
}
void CharacterBody3D::set_max_slides(int p_max_slides) {
ERR_FAIL_COND(p_max_slides > 0);
max_slides = p_max_slides;
}
real_t CharacterBody3D::get_floor_max_angle() const {
return floor_max_angle;
}
void CharacterBody3D::set_floor_max_angle(real_t p_floor_max_angle) {
floor_max_angle = p_floor_max_angle;
}
const Vector3 &CharacterBody3D::get_snap() const {
return snap;
}
void CharacterBody3D::set_snap(const Vector3 &p_snap) {
snap = p_snap;
}
const Vector3 &CharacterBody3D::get_up_direction() const {
return up_direction;
}
void CharacterBody3D::set_up_direction(const Vector3 &p_up_direction) {
up_direction = p_up_direction.normalized();
}
void CharacterBody3D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
// Reset move_and_slide() data.
@ -1097,8 +1139,20 @@ void CharacterBody3D::_notification(int p_what) {
}
void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &CharacterBody3D::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true));
ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &CharacterBody3D::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true));
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity"), &CharacterBody3D::move_and_slide);
ClassDB::bind_method(D_METHOD("is_stop_on_slope_enabled"), &CharacterBody3D::is_stop_on_slope_enabled);
ClassDB::bind_method(D_METHOD("set_stop_on_slope_enabled", "enabled"), &CharacterBody3D::set_stop_on_slope_enabled);
ClassDB::bind_method(D_METHOD("is_infinite_inertia_enabled"), &CharacterBody3D::is_infinite_inertia_enabled);
ClassDB::bind_method(D_METHOD("set_infinite_inertia_enabled", "enabled"), &CharacterBody3D::set_infinite_inertia_enabled);
ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody3D::get_max_slides);
ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody3D::set_max_slides);
ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody3D::get_floor_max_angle);
ClassDB::bind_method(D_METHOD("set_floor_max_angle", "floor_max_angle"), &CharacterBody3D::set_floor_max_angle);
ClassDB::bind_method(D_METHOD("get_snap"), &CharacterBody3D::get_snap);
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CharacterBody3D::set_snap);
ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody3D::get_up_direction);
ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody3D::set_up_direction);
ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody3D::is_on_floor);
ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody3D::is_on_ceiling);
@ -1108,6 +1162,13 @@ 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::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");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "snap"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction");
}
void CharacterBody3D::_direct_state_changed(Object *p_state) {

View file

@ -257,6 +257,13 @@ class CharacterBody3D : public PhysicsBody3D {
GDCLASS(CharacterBody3D, PhysicsBody3D);
private:
bool stop_on_slope = false;
bool infinite_inertia = true;
int max_slides = 4;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
Vector3 snap;
Vector3 up_direction = Vector3(0.0, 1.0, 0.0);
Vector3 linear_velocity;
Vector3 angular_velocity;
@ -271,7 +278,25 @@ private:
Ref<KinematicCollision3D> _get_slide_collision(int p_bounce);
bool separate_raycast_shapes(bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result);
bool separate_raycast_shapes(PhysicsServer3D::MotionResult &r_result);
bool is_stop_on_slope_enabled() const;
void set_stop_on_slope_enabled(bool p_enabled);
bool is_infinite_inertia_enabled() const;
void set_infinite_inertia_enabled(bool p_enabled);
int get_max_slides() const;
void set_max_slides(int p_max_slides);
real_t get_floor_max_angle() const;
void set_floor_max_angle(real_t p_floor_max_angle);
const Vector3 &get_snap() const;
void set_snap(const Vector3 &p_snap);
const Vector3 &get_up_direction() const;
void set_up_direction(const Vector3 &p_up_direction);
protected:
void _notification(int p_what);
@ -283,8 +308,8 @@ public:
virtual Vector3 get_linear_velocity() const override;
virtual Vector3 get_angular_velocity() const override;
Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_up_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, real_t p_floor_max_angle = Math::deg2rad((real_t)45.0), bool p_infinite_inertia = true);
Vector3 move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_up_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, real_t p_floor_max_angle = Math::deg2rad((real_t)45.0), bool p_infinite_inertia = true);
Vector3 move_and_slide(const Vector3 &p_linear_velocity);
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;