How can i make a rigid body jump in godot without givign it the ability to fly
Asked Answered
P

2

2

I would have used a kinematic body but i want to add real life physics to my 2d object but it seems that i can literally fly by pressing the up key several times

extends RigidBody2D

var velocity = Vector2.ZERO

const GRAVITY = 35
const SPEED = 1000
const JUMPFORCE = -900

func _process(delta):
    if Input.is_action_just_pressed("ui_right"):
        apply_central_impulse(Vector2(1000,0))
    if Input.is_action_just_pressed("ui_left"):
        apply_central_impulse(Vector2(-1000,0))
    if Input.is_action_just_released("ui_up"):
        apply_central_impulse(Vector2(0,-1000))
Purgative answered 5/5, 2021 at 13:5 Comment(0)
S
2

With test_motion

I do not recommend handling this with the RigidBody2D collisions because not every collision means it is on the ground (e.g. could be hitting a wall or ceiling).

However, there is another way: you can use test_motion. It returns true if a movement would result in a collision (without actuallyu moving, of course).

So use test_motion downwards, if it detects a collision, it means the RigidBody2D is on the ground:

if Input.is_action_just_released("ui_up") and self.test_motion(Vector2(0, 1), false):
    apply_central_impulse(Vector2(0,-1000))

With RayCast2D

Another simple setup is using RayCast2D instead. Position it to extend downwards from the the bottom of the CollisionShape2D or CollisionPolygon2D of the RigidBody2D. Make sure the RayCast2D is enabled. Then you can check if the RayCast2D is colliding to decide if the RigidBody2D can jump:

if Input.is_action_just_released("ui_up") and $RayCast2D.is_colliding():
    apply_central_impulse(Vector2(0,-1000))

Consider that with the above method, if the RayCast2D fail to detect something, the RigidBody2D won't be able to jump. For example, if the RayCast2D is centered, but the RigidBody2D is only partially on the ground, such that the RayCast2D hits nothing.


With Area2D

Another alternative is to use an Area2D instead of a RayCast2D. Which has the advantage of having its own CollisionShape2D or CollisionPolygon2D. You will need to keep count of bodies that enter or exist the Area2D:

var on_ground := 0

func _on_Area2D_body_entered(body: Node) -> void:
    on_ground += 1

func _on_Area2D_body_exited(body: Node) -> void:
    on_ground -= 1

And of course, use that in your check to jump:

if Input.is_action_just_released("ui_up") and on_ground > 0:
    apply_central_impulse(Vector2(0,-1000))

Finally I remind you that you have collision layers and collision masks. Using a RayCast2D or an Area2D would allow you to set a different mask than the one the RigidBody2D uses. Which is a way to specify that not all things that the RigidBody2D collides with, are things from which it can jump.

I find Area2D to be more reliable and versatile. However, it is more work to setup.

Singlefoot answered 5/5, 2021 at 15:36 Comment(0)
J
0

Just wanted to add something in the answer by Theraot. Here one signal by area2d can be avoided.

var on_ground := 0

func _on_Area2D_body_entered(body: Node) -> void:
 on_ground = 0

if Input.is_action_just_released("ui_up") and on_ground == 0:
 apply_central_impulse(Vector2(0,-1000))
 on_ground = 1

This works same as using both signals of body entered and exited.

Jerricajerrie answered 3/3, 2022 at 10:54 Comment(1)
If the player character is on a platform, since you handled body entered, it knows it is on the ground. Then if the character walks off the platform (so it is falling, not jumped), since you didn't handle body exited, the code does not know it is no longer on a platform. So it still lets you jump.Singlefoot

© 2022 - 2024 — McMap. All rights reserved.