EDIT: The downside of my suggested method:
You never have motion.y = 0 although the player is not moving. This could be a problem in the script of the AnimationPlayer node if you rely on that for animation. In our case it works just fine because we only check motion.y < 0:
@Andrey_Zhulanov hit the nail on the head!
@Yann_Burrett I would suggest to use this code for the fall method:
func fall(delta):
if is_on_floor():
motion.y = 0 # to prevent the player from accelerating while on the ground
motion.y += GRAVITY * delta # always accelerate so we always get a fresh collision for is_on_floor()
# old version:
func fall():
if is_on_floor():
motion.y = 0
else:
motion.y += GRAVITY * delta
Why?
- is_on_floor() should reliably return true, whether the player stays on the ground or just fell down on it
- no “silent acceleration” while moving on the ground as demonstrated by Yann in the lecture starting around 7:55 min
A little background info on is_on_floor:
is_on_floor() just needs a fresh collision, otherwise it will not be set to true.
This is a shortened snippet from the c++ source of KinematicBody2D as of today on Github:
Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
Vector2 floor_motion = floor_velocity;
if (on_floor && on_floor_body.is_valid()) {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body);
if (bs) {
floor_motion = bs->get_linear_velocity();
}
}
Vector2 motion = (floor_motion + p_linear_velocity) * get_physics_process_delta_time();
Vector2 lv = p_linear_velocity;
on_floor = false;
on_floor_body = RID();
on_ceiling = false;
on_wall = false;
colliders.clear();
floor_velocity = Vector2();
while (p_max_slides) {
Collision collision;
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, collision);
if (!collided) {
motion = Vector2(); //clear because no collision happened and motion completed
}
} else { //separate raycasts (if any)
collided = separate_raycast_shapes(p_infinite_inertia, collision);
if (collided) {
collision.remainder = motion; //keep
collision.travel = Vector2();
}
}
if (collided) {
found_collision = true;
}
if (collided) {
motion = collision.remainder;
if (p_floor_direction == Vector2()) {
//all is a wall
on_wall = true;
} else {
if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor
on_floor = true;
is_on_floor() is just a Getter for the on_floor variable.
btw, I love open source software and the fact, that I can just check the code and see for myself