Godot Version
4.3
Question
Im not very good at coding in Godot & Ive been trying to figure out why my code is acting funny. Someone on here helped me out with this but it’s still not working. When the player jumps, it’s usually gimped or it sends the player floating somewhere. The player is also supposed to connect to the other planet when it’s close to its area.
Here is the code:
extends CharacterBody2D
# change export to @export and fix type declararions
@export_subgroup("Properties")
@export var speed : int = 320
@export var jump_speed : int = -780
@export var gravity : int = 8000
# velocity is integrated in Character2D so this isn't needed
# but move the initialisation to zero to func ready
#var velocity = Vector2.ZERO
var is_jumping = false
var planets: Array
var current_planet: Node
var time_delta = 0
func _ready():
planets = get_node("/root/Sector2D/Planets").get_children()
current_planet = planets[0]
_get_closest_planet()
$Timer.start()
velocity = Vector2.ZERO
self.floor_max_angle = 180
$Timer.one_shot = true
$Timer.wait_time = 0.1
func get_input():
velocity.x = 0
if Input.is_action_pressed("move_right"):
velocity.x += speed
elif Input.is_action_pressed("move_left"):
velocity.x -= speed
func _physics_process(delta):
get_input()
var gravity_dir = current_planet.global_transform.origin - global_transform.origin
rotation = gravity_dir.angle() - PI/2
if !is_jumping:
velocity.y = 0
velocity.y += gravity * delta
velocity = velocity.rotated(rotation)
move_and_slide()
if is_on_floor():
is_jumping = false
if Input.is_action_just_pressed("ui_accept"):
$Timer.start()
is_jumping = true
velocity.y = jump_speed
func _get_closest_planet() -> void:
var _closest_planet = current_planet
var smallest_distance : float = INF
for planet in planets:
var distance_to_planet : float = global_position.distance_to(planet.global_position)
if distance_to_planet < smallest_distance:
current_planet = planet
smallest_distance = distance_to_planet
func _on_timer_timeout() -> void:
_get_closest_planet()
2 Likes
velocity.y += gravity * delta
This piece of code constantly pulls the player down (or towards a planet) each physics frame. This shouldn’t matter in theory, since you’re directly assigning the jump_speed
variable directly into velocity.y, though in practice it could be a cause. Maybe clamp it? Something like:
velocity.y = clampf(velocity.y + gravity * delta, -INF, 14)
2 Likes
Thanks for the help but this still doesn’t fix the issue. The player now falls very slowly to the planet now, and doesn’t solve any of the other issues.
If it helps, this is code remade for Godot 4, from a Godot 3 game called “Little World”. You can read about here https://game-blog.sethcorker.com/2020-08-05-godot-experiments/ maybe you can find the differences between this & my code, & fix the problem?
extends KinematicBody2D
export (int) var speed = 1200
export (int) var jump_speed = -1800
export (int) var gravity = 4000
var velocity = Vector2.ZERO
var is_jumping = false
var planets: Array
var current_planet: Node
var time_delta = 0
func _ready():
planets = get_node("/root/MainLevel/Planets").get_children()
current_planet = planets[0]
_get_closest_planet(current_planet)
_start_closest_planet_timer()
func get_input():
velocity.x = 0
if Input.is_action_pressed("walk_right"):
velocity.x += speed
$AnimatedSprite.play()
$AnimatedSprite.flip_h = false
elif Input.is_action_pressed("walk_left"):
velocity.x -= speed
$AnimatedSprite.play()
$AnimatedSprite.flip_h = true
else:
$AnimatedSprite.playing = false
func _physics_process(delta):
get_input()
time_delta += delta
var gravity_dir = current_planet.global_transform.origin - global_transform.origin
rotation = gravity_dir.angle() - PI/2
velocity.y += gravity * delta
var snap = transform.y * 128 if !is_jumping else Vector2.ZERO
velocity = move_and_slide_with_snap(velocity.rotated(rotation), snap, -transform.y, true, 2, PI/3)
velocity = velocity.rotated(-rotation)
if is_on_floor():
is_jumping = false
if Input.is_action_just_pressed("jump"):
is_jumping = true
velocity.y = jump_speed
func _get_closest_planet(smallest):
var new_smallest = smallest
var did_change = false
if !is_jumping:
return
for planet in planets:
if !new_smallest:
new_smallest = planet
if global_position.distance_to(planet.global_position) < global_position.distance_to(new_smallest.global_position):
new_smallest = planet
if new_smallest != current_planet:
is_jumping = false
velocity.y = 1200
current_planet = new_smallest
func _start_closest_planet_timer():
var timer = Timer.new()
timer.wait_time = 0.1
timer.connect("timeout", self, "_get_closest_planet", [current_planet])
add_child(timer)
timer.start()
First of all I would suggest to call move_and_slide() after your jump-logic.
Second i would like to know how long is this timer that checks for the closest planet?
And how do the planets end up in the planets-array?
- The timer will fire every 0.1 seconds.
3. Reworked the code a bit.
extends CharacterBody2D
@export_subgroup("Properties")
@export var speed: int = 320
@export var jump_speed: int = -780
@export var gravity: int = 4000
var is_jumping: bool = false
var planets: Array = []
var current_planet: Node = null
var time_delta: float = 0.0
func _ready() -> void:
planets = get_node("/root/Sector2D/Planets").get_children()
if planets.size() > 0:
current_planet = planets[0]
_get_closest_planet()
$Timer.start()
velocity = Vector2.ZERO
self.floor_max_angle = 180
$Timer.one_shot = true
$Timer.wait_time = 0.1
func get_input() -> void:
velocity.x = 0
if Input.is_action_pressed("move_right"):
velocity.x += speed
elif Input.is_action_pressed("move_left"):
velocity.x -= speed
func _physics_process(delta: float) -> void:
get_input()
var gravity_dir: Vector2 = current_planet.global_transform.origin - global_transform.origin
rotation = gravity_dir.angle() - PI / 2
if !is_jumping:
velocity.y = 0
velocity.y += gravity * delta
velocity = velocity.rotated(rotation)
if is_on_floor():
is_jumping = false
if Input.is_action_just_pressed("ui_accept"):
$Timer.start()
is_jumping = true
velocity.y = jump_speed
move_and_slide()
if is_on_floor():
is_jumping = false
if Input.is_action_just_pressed("ui_accept"):
$Timer.start()
is_jumping = true
velocity.y = jump_speed
func _get_closest_planet() -> void:
var _closest_planet: Node = current_planet
var smallest_distance: float = INF
for planet in planets:
var distance_to_planet: float = global_position.distance_to(planet.global_position)
if distance_to_planet < smallest_distance:
current_planet = planet
smallest_distance = distance_to_planet
func _on_timer_timeout() -> void:
_get_closest_planet()
why do you have the jumplogic twice now?
also why use a timer at all, just call it while the character is not on ground
if is_on_floor():
is_jumping = false
if Input.is_action_just_pressed("ui_accept"):
is_jumping = true
velocity.y = jump_speed
else:
_get_closest_planet()
There might also be problems with the is_on_floor()-method since your up-direction changes depending on where you are on the planet.
The docs:
bool is_on_floor() const Returns true if the body collided with the floor on the last call of move_and_slide. Otherwise, returns false. The up_direction and floor_max_angle are used to determine whether a surface is "floor" or not.
My fault for the jumplogic thing, I didn’t notice that when I pasted the code. Sorry if my code is really weird, Im not very experienced Godot & Ive been very frequent on here. Someone else on here helped me with some of this code & Ive been picking up on the changes they’ve made to it. What can I do to replace the is_on_floor() thing so the jumping works correctly?
Nothing wrong with inexperienced coding, we all were beginners at some point.
You have to rotate the up_direction by the gravity_dir, there might be an offset necessary, but im not sure since i have to imagine it in my head
1 Like