Add properties to CharacterBody for more move_and_slide options

This commit is contained in:
fabriceci 2021-07-26 15:05:30 +02:00
parent 50d5569ad4
commit c6666cc051
3 changed files with 344 additions and 108 deletions

View file

@ -21,10 +21,10 @@
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">
<method name="get_platform_velocity" qualifiers="const">
<return type="Vector2" />
<description>
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].
Returns the linear velocity of the platform at the last collision point. Only valid after calling [method move_and_slide].
</description>
</method>
<method name="get_slide_collision">
@ -61,18 +61,36 @@
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_ceiling_only" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the body collided only 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" />
<description>
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_floor_only" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the body collided only 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" />
<description>
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="is_on_wall_only" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the body collided only with a wall on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="move_and_slide">
<return type="void" />
<description>
@ -90,9 +108,20 @@
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.
</member>
<member name="constant_speed_on_floor" type="bool" setter="set_constant_speed_on_floor_enabled" getter="is_constant_speed_on_floor_enabled" default="false">
If [code]false[/code] (by default), the body will move faster on downward slopes and slower on upward slopes.
If [code]true[/code], the body will always move at the same speed on the ground no matter the slope. Note that you need to use [member floor_snap_length] to stick along a downward slope at constant speed.
</member>
<member name="exclude_body_layers" type="int" setter="set_exclude_body_layers" getter="get_exclude_body_layers" default="0">
Collision layers that will be excluded for detecting bodies that will act as moving platforms to be followed by the [CharacterBody2D]. By default, all touching bodies are detected and propagate their velocity. You can add excluded layers to ignore bodies that are contained in these layers.
</member>
<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="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="0.0">
Sets a snapping distance. When set to a value different from [code]0.0[/code], the body is kept attached to slopes when calling [method move_and_slide]. The snapping vector is determined by the given distance along the opposite direction of the [member up_direction].
As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping.
</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>
@ -102,9 +131,11 @@
<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="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 name="move_on_floor_only" type="bool" setter="set_move_on_floor_only_enabled" getter="is_move_on_floor_only_enabled" default="true">
If [code]true[/code], the body will be able to move on the floor only, this option avoids to be able to walk on walls, it will however allow to slide down along them.
</member>
<member name="slide_on_ceiling" type="bool" setter="set_slide_on_ceiling_enabled" getter="is_slide_on_ceiling_enabled" default="true">
If [code]true[/code], during a jump against the ceiling, the body will slide, if [code]false[/code] it will be stopped and will fall vertically.
</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.

View file

@ -69,7 +69,6 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_i
}
motion_cache->result = result;
return motion_cache;
}
@ -1046,139 +1045,240 @@ void RigidBody2D::_reload_physics_characteristics() {
//////////////////////////
//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
// So, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01
void CharacterBody2D::move_and_slide() {
Vector2 body_velocity_normal = linear_velocity.normalized();
bool was_on_floor = on_floor;
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
Vector2 current_floor_velocity = floor_velocity;
Vector2 current_platform_velocity = platform_velocity;
if ((on_floor || on_wall) && on_floor_body.is_valid()) {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(on_floor_body);
if (bs) {
Transform2D gt = get_global_transform();
Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2];
current_floor_velocity = bs->get_velocity_at_local_position(local_position);
if ((on_floor || on_wall) && platform_rid.is_valid()) {
bool excluded = (exclude_body_layers & platform_layer) != 0;
if (!excluded) {
// This approach makes sure there is less delay between the actual body velocity and the one we saved.
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);
if (bs) {
Transform2D gt = get_global_transform();
Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2];
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
}
} else {
current_platform_velocity = Vector2();
}
}
motion_results.clear();
bool was_on_floor = on_floor;
on_floor = false;
on_ceiling = false;
on_wall = false;
floor_normal = Vector2();
floor_velocity = Vector2();
if (current_floor_velocity != Vector2() && on_floor_body.is_valid()) {
if (!current_platform_velocity.is_equal_approx(Vector2())) {
PhysicsServer2D::MotionResult floor_result;
Set<RID> exclude;
exclude.insert(on_floor_body);
if (move_and_collide(current_floor_velocity * delta, infinite_inertia, floor_result, true, false, false, false, exclude)) {
exclude.insert(platform_rid);
if (move_and_collide(current_platform_velocity * delta, infinite_inertia, floor_result, true, false, false, false, exclude)) {
motion_results.push_back(floor_result);
_set_collision_direction(floor_result);
}
}
on_floor_body = RID();
Vector2 motion = linear_velocity * delta;
Vector2 motion_slide_up = motion.slide(up_direction);
Vector2 prev_platform_velocity = current_platform_velocity;
Vector2 prev_floor_normal = floor_normal;
RID prev_platform_rid = platform_rid;
int prev_platform_layer = platform_layer;
platform_rid = RID();
floor_normal = Vector2();
platform_velocity = Vector2();
// No sliding on first attempt to keep floor motion stable when possible,
// when stop on slope is enabled.
bool sliding_enabled = !stop_on_slope;
// When stop on slope is enabled or when there is no up direction.
bool sliding_enabled = !stop_on_slope || up_direction == Vector2();
// Constant speed can be applied only the first time sliding is enabled.
bool can_apply_constant_speed = sliding_enabled;
bool first_slide = true;
bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0;
Vector2 last_travel;
for (int iteration = 0; iteration < max_slides; ++iteration) {
PhysicsServer2D::MotionResult result;
bool found_collision = false;
Vector2 prev_position = get_global_transform().elements[2];
for (int i = 0; i < 2; ++i) {
bool collided;
if (i == 0) { //collide
if (i == 0) { // Collide.
collided = move_and_collide(motion, infinite_inertia, result, margin, true, false, !sliding_enabled);
if (!collided) {
motion = Vector2(); //clear because no collision happened and motion completed
}
} else { //separate raycasts (if any)
} else { // Separate raycasts (if any).
collided = separate_raycast_shapes(result);
if (collided) {
result.remainder = motion; //keep
result.remainder = motion; // Keep.
result.motion = Vector2();
}
}
if (collided) {
found_collision = true;
motion_results.push_back(result);
_set_collision_direction(result);
if (on_floor && stop_on_slope) {
if ((body_velocity_normal + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
if (result.motion.length() > margin) {
gt.elements[2] -= result.motion.slide(up_direction);
} else {
if (on_floor && stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
if (result.motion.length() > margin) {
gt.elements[2] -= result.motion.slide(up_direction);
} else {
gt.elements[2] -= result.motion;
}
set_global_transform(gt);
linear_velocity = Vector2();
motion = Vector2();
break;
}
if (result.remainder.is_equal_approx(Vector2())) {
motion = Vector2();
break;
}
// Move on floor only checks.
if (move_on_floor_only && on_wall && motion_slide_up.dot(result.collision_normal) <= 0) {
// Avoid to move forward on a wall if move_on_floor_only is true.
if (was_on_floor && !is_on_floor_only() && !vel_dir_facing_up) {
// If the movement is large the body can be prevented from reaching the walls.
if (result.motion.length() <= margin) {
// Cancels the motion.
Transform2D gt = get_global_transform();
gt.elements[2] -= result.motion;
set_global_transform(gt);
}
set_global_transform(gt);
on_floor = true;
platform_rid = prev_platform_rid;
platform_layer = prev_platform_layer;
platform_velocity = prev_platform_velocity;
floor_normal = prev_floor_normal;
linear_velocity = Vector2();
return;
motion = Vector2();
break;
}
// Prevents the body from being able to climb a slope when it moves forward against the wall.
else if (!is_on_floor_only()) {
motion = up_direction * up_direction.dot(result.remainder);
motion = motion.slide(result.collision_normal);
} else {
motion = result.remainder;
}
}
// Constant Speed when the slope is upward.
else if (constant_speed_on_floor && is_on_floor_only() && can_apply_constant_speed && was_on_floor && motion.dot(result.collision_normal) < 0) {
can_apply_constant_speed = false;
Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();
if (!motion_slide_norm.is_equal_approx(Vector2())) {
motion = motion_slide_norm * (motion_slide_up.length() - result.motion.slide(up_direction).length() - last_travel.slide(up_direction).length());
}
}
// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
Vector2 slide_motion = result.remainder.slide(result.collision_normal);
if (slide_motion.dot(linear_velocity) > 0.0) {
motion = slide_motion;
} else {
motion = Vector2();
}
if (slide_on_ceiling && on_ceiling) {
// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.
if (vel_dir_facing_up) {
linear_velocity = linear_velocity.slide(result.collision_normal);
} else {
// Avoid acceleration in slope when falling.
linear_velocity = up_direction * up_direction.dot(linear_velocity);
}
}
}
// No sliding on first attempt to keep floor motion stable when possible.
else {
motion = result.remainder;
if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) {
linear_velocity = linear_velocity.slide(up_direction);
motion = motion.slide(up_direction);
}
}
if (sliding_enabled || !on_floor) {
motion = result.remainder.slide(result.collision_normal);
linear_velocity = linear_velocity.slide(result.collision_normal);
} else {
motion = result.remainder;
last_travel = result.motion;
}
// When you move forward in a downward slope you dont collide because you will be in the air.
// This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied.
else if (i == 0 && constant_speed_on_floor && first_slide && _on_floor_if_snapped(was_on_floor, vel_dir_facing_up)) {
can_apply_constant_speed = false;
sliding_enabled = true;
Transform2D gt = get_global_transform();
gt.elements[2] = prev_position;
set_global_transform(gt);
Vector2 motion_slide_norm = motion.slide(prev_floor_normal).normalized();
if (!motion_slide_norm.is_equal_approx(Vector2())) {
motion = motion_slide_norm * (motion_slide_up.length());
found_collision = true;
}
}
sliding_enabled = true;
}
can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled;
sliding_enabled = true;
first_slide = false;
if (!found_collision || motion == Vector2()) {
if (!found_collision || motion.is_equal_approx(Vector2())) {
break;
}
}
_snap_on_floor(was_on_floor, vel_dir_facing_up);
if (!on_floor && !on_wall) {
// Add last platform velocity when just left a moving platform.
linear_velocity += current_floor_velocity;
linear_velocity += current_platform_velocity;
}
if (!was_on_floor || snap == Vector2()) {
// Reset the gravity accumulation when touching the ground.
if (on_floor && !vel_dir_facing_up) {
linear_velocity = linear_velocity.slide(up_direction);
}
}
void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) {
if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) {
return;
}
// Apply snap.
Transform2D gt = get_global_transform();
PhysicsServer2D::MotionResult result;
if (move_and_collide(snap, infinite_inertia, result, margin, false, true, false)) {
if (move_and_collide(up_direction * -floor_snap_length, infinite_inertia, result, margin, false, true, false)) {
bool apply = true;
if (up_direction != Vector2()) {
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 (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.
if (result.motion.length() > margin) {
result.motion = up_direction * up_direction.dot(result.motion);
} else {
result.motion = Vector2();
}
float collision_angle = Math::acos(result.collision_normal.dot(up_direction));
if (collision_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
floor_normal = result.collision_normal;
platform_velocity = result.collider_velocity;
_set_platform_data(result);
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.
if (result.motion.length() > margin) {
result.motion = up_direction * up_direction.dot(result.motion);
} else {
result.motion = Vector2();
}
} else {
apply = false;
}
} else {
apply = false;
}
if (apply) {
@ -1188,23 +1288,46 @@ void CharacterBody2D::move_and_slide() {
}
}
bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) {
if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) {
return false;
}
PhysicsServer2D::MotionResult result;
if (move_and_collide(up_direction * -floor_snap_length, infinite_inertia, result, margin, false, true, false)) {
if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
return true;
}
}
return false;
}
void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResult &p_result) {
if (up_direction == Vector2()) {
//all is a wall
on_wall = true;
return;
}
if (Math::acos(p_result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = p_result.collision_normal;
platform_velocity = p_result.collider_velocity;
_set_platform_data(p_result);
} else if (Math::acos(p_result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
if (Math::acos(p_result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = p_result.collision_normal;
on_floor_body = p_result.collider;
floor_velocity = p_result.collider_velocity;
} else if (Math::acos(p_result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
on_floor_body = p_result.collider;
floor_velocity = p_result.collider_velocity;
}
on_wall = true;
platform_velocity = p_result.collider_velocity;
_set_platform_data(p_result);
}
}
void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) {
platform_rid = p_result.collider;
platform_layer = 0;
CollisionObject2D *collision_object = Object::cast_to<CollisionObject2D>(ObjectDB::get_instance(p_result.collider_id));
if (collision_object) {
platform_layer = collision_object->get_collision_layer();
}
}
@ -1256,20 +1379,32 @@ bool CharacterBody2D::is_on_floor() const {
return on_floor;
}
bool CharacterBody2D::is_on_floor_only() const {
return on_floor && !on_wall && !on_ceiling;
}
bool CharacterBody2D::is_on_wall() const {
return on_wall;
}
bool CharacterBody2D::is_on_wall_only() const {
return on_wall && !on_floor && !on_ceiling;
}
bool CharacterBody2D::is_on_ceiling() const {
return on_ceiling;
}
bool CharacterBody2D::is_on_ceiling_only() const {
return on_ceiling && !on_floor && !on_wall;
}
Vector2 CharacterBody2D::get_floor_normal() const {
return floor_normal;
}
Vector2 CharacterBody2D::get_floor_velocity() const {
return floor_velocity;
Vector2 CharacterBody2D::get_platform_velocity() const {
return platform_velocity;
}
int CharacterBody2D::get_slide_count() const {
@ -1319,6 +1454,35 @@ void CharacterBody2D::set_infinite_inertia_enabled(bool p_enabled) {
infinite_inertia = p_enabled;
}
bool CharacterBody2D::is_constant_speed_on_floor_enabled() const {
return constant_speed_on_floor;
}
void CharacterBody2D::set_constant_speed_on_floor_enabled(bool p_enabled) {
constant_speed_on_floor = p_enabled;
}
bool CharacterBody2D::is_move_on_floor_only_enabled() const {
return move_on_floor_only;
}
void CharacterBody2D::set_move_on_floor_only_enabled(bool p_enabled) {
move_on_floor_only = p_enabled;
}
bool CharacterBody2D::is_slide_on_ceiling_enabled() const {
return slide_on_ceiling;
}
void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) {
slide_on_ceiling = p_enabled;
}
uint32_t CharacterBody2D::get_exclude_body_layers() const {
return exclude_body_layers;
}
void CharacterBody2D::set_exclude_body_layers(uint32_t p_exclude_layers) {
exclude_body_layers = p_exclude_layers;
}
int CharacterBody2D::get_max_slides() const {
return max_slides;
}
@ -1336,12 +1500,13 @@ void CharacterBody2D::set_floor_max_angle(real_t p_radians) {
floor_max_angle = p_radians;
}
const Vector2 &CharacterBody2D::get_snap() const {
return snap;
real_t CharacterBody2D::get_floor_snap_length() {
return floor_snap_length;
}
void CharacterBody2D::set_snap(const Vector2 &p_snap) {
snap = p_snap;
void CharacterBody2D::set_floor_snap_length(real_t p_floor_snap_length) {
ERR_FAIL_COND(p_floor_snap_length < 0);
floor_snap_length = p_floor_snap_length;
}
const Vector2 &CharacterBody2D::get_up_direction() const {
@ -1357,11 +1522,11 @@ void CharacterBody2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
// Reset move_and_slide() data.
on_floor = false;
on_floor_body = RID();
platform_rid = RID();
on_ceiling = false;
on_wall = false;
motion_results.clear();
floor_velocity = Vector2();
platform_velocity = Vector2();
} break;
}
}
@ -1377,31 +1542,48 @@ void CharacterBody2D::_bind_methods() {
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_constant_speed_on_floor_enabled", "enabled"), &CharacterBody2D::set_constant_speed_on_floor_enabled);
ClassDB::bind_method(D_METHOD("is_constant_speed_on_floor_enabled"), &CharacterBody2D::is_constant_speed_on_floor_enabled);
ClassDB::bind_method(D_METHOD("set_move_on_floor_only_enabled", "enabled"), &CharacterBody2D::set_move_on_floor_only_enabled);
ClassDB::bind_method(D_METHOD("is_move_on_floor_only_enabled"), &CharacterBody2D::is_move_on_floor_only_enabled);
ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody2D::set_slide_on_ceiling_enabled);
ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody2D::is_slide_on_ceiling_enabled);
ClassDB::bind_method(D_METHOD("set_exclude_body_layers", "exclude_layer"), &CharacterBody2D::set_exclude_body_layers);
ClassDB::bind_method(D_METHOD("get_exclude_body_layers"), &CharacterBody2D::get_exclude_body_layers);
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", "radians"), &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_floor_snap_length"), &CharacterBody2D::get_floor_snap_length);
ClassDB::bind_method(D_METHOD("set_floor_snap_length", "floor_snap_length"), &CharacterBody2D::set_floor_snap_length);
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_floor_only"), &CharacterBody2D::is_on_floor_only);
ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody2D::is_on_ceiling);
ClassDB::bind_method(D_METHOD("is_on_ceiling_only"), &CharacterBody2D::is_on_ceiling_only);
ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall);
ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody2D::is_on_wall_only);
ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal);
ClassDB::bind_method(D_METHOD("get_floor_velocity"), &CharacterBody2D::get_floor_velocity);
ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody2D::get_platform_velocity);
ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody2D::get_slide_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision);
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::BOOL, "constant_speed_on_floor"), "set_constant_speed_on_floor_enabled", "is_constant_speed_on_floor_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "move_on_floor_only"), "set_move_on_floor_only_enabled", "is_move_on_floor_only_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_RANGE, "1,8,1,or_greater"), "set_max_slides", "get_max_slides");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::INT, "exclude_body_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_exclude_body_layers", "get_exclude_body_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}

View file

@ -273,16 +273,20 @@ private:
bool stop_on_slope = false;
bool infinite_inertia = true;
bool constant_speed_on_floor = false;
bool move_on_floor_only = true;
bool slide_on_ceiling = true;
int max_slides = 4;
int platform_layer;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
Vector2 snap;
float floor_snap_length = 0;
Vector2 up_direction = Vector2(0.0, -1.0);
uint32_t exclude_body_layers = 0;
Vector2 linear_velocity;
Vector2 floor_normal;
Vector2 floor_velocity;
RID on_floor_body;
Vector2 platform_velocity;
RID platform_rid;
bool on_floor = false;
bool on_ceiling = false;
bool on_wall = false;
@ -290,10 +294,6 @@ private:
Vector<PhysicsServer2D::MotionResult> motion_results;
Vector<Ref<KinematicCollision2D>> slide_colliders;
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
void _set_collision_direction(const PhysicsServer2D::MotionResult &p_result);
bool separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result);
void set_safe_margin(real_t p_margin);
@ -305,17 +305,37 @@ private:
bool is_infinite_inertia_enabled() const;
void set_infinite_inertia_enabled(bool p_enabled);
bool is_constant_speed_on_floor_enabled() const;
void set_constant_speed_on_floor_enabled(bool p_enabled);
bool is_move_on_floor_only_enabled() const;
void set_move_on_floor_only_enabled(bool p_enabled);
bool is_slide_on_ceiling_enabled() const;
void set_slide_on_ceiling_enabled(bool p_enabled);
int get_max_slides() const;
void set_max_slides(int p_max_slides);
real_t get_move_max_angle() const;
void set_move_max_angle(real_t p_radians);
real_t get_floor_max_angle() const;
void set_floor_max_angle(real_t p_radians);
const Vector2 &get_snap() const;
void set_snap(const Vector2 &p_snap);
real_t get_floor_snap_length();
void set_floor_snap_length(real_t p_floor_snap_length);
uint32_t get_exclude_body_layers() const;
void set_exclude_body_layers(const uint32_t p_exclude_layer);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
const Vector2 &get_up_direction() const;
bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up);
void set_up_direction(const Vector2 &p_up_direction);
void _set_collision_direction(const PhysicsServer2D::MotionResult &p_result);
void _set_platform_data(const PhysicsServer2D::MotionResult &p_result);
void _snap_on_floor(bool was_on_floor, bool vel_dir_facing_up);
protected:
void _notification(int p_what);
@ -328,10 +348,13 @@ public:
void set_linear_velocity(const Vector2 &p_velocity);
bool is_on_floor() const;
bool is_on_floor_only() const;
bool is_on_wall() const;
bool is_on_wall_only() const;
bool is_on_ceiling() const;
bool is_on_ceiling_only() const;
Vector2 get_floor_normal() const;
Vector2 get_floor_velocity() const;
Vector2 get_platform_velocity() const;
int get_slide_count() const;
PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const;