Player's code acting strange

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.
ezgif-5-448b5cf97b

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?

  1. The timer will fire every 0.1 seconds.

Screen Shot 2024-09-04 at 4.18.57 AM
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