buggy collisions with CharacterBody2D and CollisionPolygon2D

Godot Version

godot 4.3.stable

Question

I have a player as a characterbody2d node, but when It walks on a collision polygon the movement gets all jittery and sometimes the player even just gets stuck and can't move. How do I fix this?

It’s also not a regular character controller, it is designed to walk around a spherical body that is also moving (although without physics calculations) and the planet the player is on also updates the player’s position every time it moves by getting the amount it has travelled every frame and adding that to the player’s position as well as its own. I have a planet that is a polygon (not a sphere cause a sphere can’t have any bumps in it and planets aren’t perfectly round).

please help, It ruins the gameplay and I’ve been stuck on it for ages now. If you want to see some code, I can share it

Yes, share some code. Is all the movement happening during the physics update?

the planet is updating its position with _process and the player _physics_process because for some reason I can’t be bothered to work out it means that you don’t notice that the planet is moving

heres the player script:

extends CharacterBody2D

@export var speed : float = 100.0
@export var jump_vel : float = 55.0
@export var mode : int = 0
@export var currentplanet : Node = null

var dir = Vector2.DOWN
var gravitational_field_strength : float = 0.0
var dist_to_planet : float = 0.0
var target_eyepos : Vector2 = Vector2()
var interacting : bool = false
var interacting_node : Node = null
var rocket : String = ""
var oxygen : float = 100.0


func _ready() -> void:
	if currentplanet != null:
		gravitational_field_strength = currentplanet.gravity

func _physics_process(delta : float) -> void:
	$heat.global_rotation_degrees = rad_to_deg(velocity.angle()) - 90.0
	if currentplanet != null and currentplanet.has_atmosphere:
		$heat.modulate.a = lerp($heat.modulate.a, clamp((pcent(velocity.length(), 2525.0) / 100.0) - 0.25, 0.0, 1.0), delta * 2.0)
	else:
		$heat.modulate.a = lerp($heat.modulate.a, 0.0, delta * 2.0)
	oxygen = clamp(oxygen, 0.0, 100.0)
	$canvas/info/oxygen/bar.value = oxygen
	if mode == 2 and get_tree().current_scene.get_node(rocket) != null:
		if "maxfuel" in get_tree().current_scene.get_node(rocket):
			$canvas/info/fuel/bar.max_value = get_tree().current_scene.get_node(rocket).maxfuel
		if "fuel" in get_tree().current_scene.get_node(rocket):
			$canvas/info/fuel.visible = get_tree().current_scene.get_node(rocket).fuel > 0.0
			$canvas/info/fuel/bar.value = get_tree().current_scene.get_node(rocket).fuel
	else:
		$canvas/info/fuel.hide()
	for i in $canvas/info.get_children():
		if i is VBoxContainer:
			i.get_node("label").text = i.name.to_upper()
	if currentplanet != null:
		gravitational_field_strength = currentplanet.gravity
	else:
		gravitational_field_strength = 0.0
	$canvas/gravity.text = "GRAVITY : " + str(round(gravitational_field_strength * dist_to_planet * 100.0) / 100.0)
	$suit.visible = mode != 2
	$arrow.visible = get_tree().current_scene.markervisible
	if get_parent().current_camera != null:
		$arrow.scale = Vector2(0.018, 0.018) / get_tree().current_scene.current_camera.zoom
	$arrow.look_at(get_parent().markerpos)
	if currentplanet != null:
		$planetrotation.global_position = currentplanet.global_position
		$planetrotation.look_at(global_position)
	if get_parent().current_camera != null:
		$camera.enabled = get_tree().current_scene.current_camera == $camera
	var zoom : float
	if Input.is_action_just_pressed("zoomin"):
		zoom = 1.0
	elif Input.is_action_just_pressed("zoomout"):
		zoom = -1.0
	else:
		zoom = 0.0
	if $camera.enabled:
		$camera.zoom = clamp($camera.zoom + Vector2(zoom, zoom) / 10, Vector2(0.1, 0.1), Vector2(9.0, 9.0))
	if get_parent().current_camera != null:
		$playermarker.scale = Vector2(0.025, 0.025) / get_tree().current_scene.current_camera.zoom
		$playermarker.visible = get_tree().current_scene.current_camera.zoom.x <= 0.2
	interacting = mode != 2 and interacting
	if Input.is_action_just_pressed("interact") and interacting:
		rocket = interacting_node.name
		if interacting_node != null:
			if interacting_node == get_tree().current_scene.get_node(rocket):
				mode = 2
	if Input.is_action_just_pressed("leave") and mode == 2:
		mode = 0
		if get_tree().current_scene.get_node(rocket) != null:
			global_position = get_tree().current_scene.get_node(rocket).to_global(get_tree().current_scene.get_node(rocket).playerexitpos)
			velocity = get_tree().current_scene.get_node(rocket).velocity
	$etointeract.visible = interacting and !$playermarker.visible
	$canvas/planet_minimap.visible = currentplanet != null
	$canvas/planet_minimap.pivot_offset = $canvas/planet_minimap.size / 2.0
	$canvas/planet_minimap.rotation_degrees = -$planetrotation.rotation_degrees - 90.0
	if currentplanet == null:
		if mode != 2:
			mode = 1
		dist_to_planet = 0.0
	else:
		dist_to_planet = invert((global_position.distance_to(currentplanet.global_position) - (currentplanet.radius + $detect/collider.shape.radius)) / (currentplanet.get_node("gravitationalfield/collider").shape.radius - (currentplanet.radius + $detect/collider.shape.radius)), 1.0)
	dist_to_planet = clamp(dist_to_planet, 0.1, 1.0)

	if currentplanet != null and currentplanet.has_oxygen and mode != 2:
		oxygen += delta * dist_to_planet
	elif mode != 2:
		oxygen -= delta / 4.0
	elif get_tree().current_scene.get_node(rocket) != null and get_tree().current_scene.get_node(rocket).fuel > 0.0:
		oxygen += delta

	if mode == 0:
		$collider.disabled = false
		if currentplanet != null:
			dir = (currentplanet.global_position - global_position).normalized()
		velocity += delta * dir * gravitational_field_strength * dist_to_planet * 10.0
	var right_vector: Vector2 = dir.orthogonal()
	var direction: float = Input.get_axis("left", "right")
	if direction:
		if mode == 0:
			velocity += delta * dir * gravitational_field_strength * 5.0 * direction
		target_eyepos.x = direction * 70.0
		velocity = velocity.slide(right_vector) + direction * speed * right_vector
	else:
		target_eyepos.x = 0.0
		velocity = velocity.slide(right_vector)
	if mode == 1:
		var directiony : float = Input.get_axis("jump", "down")
		if directiony:
			target_eyepos.y = Input.get_axis("jump", "down") * 70.0
			velocity = velocity.slide(dir) + directiony * speed * dir
		else:
			target_eyepos.y = 0.0
			velocity = velocity.slide(dir)
		var angle : float = Input.get_axis("rotateleft", "rotateright")
		if angle:
			rotation_degrees += angle * 2.0
			dir = Vector2.DOWN.rotated(rotation).normalized()
	elif mode == 0:
		$collider.disabled = false
		target_eyepos.y = 0.0
	elif mode == 2 and get_tree().current_scene.get_node(rocket) != null:
		$collider.disabled = true
		target_eyepos = Vector2()
		velocity = Vector2()
		position = get_tree().current_scene.get_node(rocket).position
		rotation_degrees = get_tree().current_scene.get_node(rocket).rotation_degrees

	if Input.is_action_pressed("jump") and is_on_floor():
		velocity = velocity.slide(dir) + dir * -jump_vel

	up_direction = -dir

	move_and_slide()

	if mode == 0:
		rotation_degrees = lerp_deg(rotation_degrees, rad_to_deg(dir.angle()) - 90.0, delta * 5.0)


	$suit/leye.position = lerp($suit/leye.position, target_eyepos + Vector2(-150.0, -500.0), delta * 10.0)
	$suit/reye.position = lerp($suit/reye.position, target_eyepos + Vector2(150.0, -500.0), delta * 10.0)
	$suit/leye.position.y = clamp($suit/leye.position.y, -650.0, 0.0)
	$suit/reye.position.y = clamp($suit/reye.position.y, -650.0, 0.0)


func _on_detect_area_entered(area: Area2D) -> void:
	if area.is_in_group("gravitational_field"):
		print(area.get_parent())
		currentplanet = area.get_parent()
		$canvas/planettitle.text = currentplanet.name.capitalize()
		$canvas/planetmessage.text = "'" + currentplanet.message.capitalize() + "'"
		if mode != 2:
			mode = 0
		$textanimator.stop()
		$textanimator.play("show")
	if area.is_in_group("interact"):
		interacting = true
		interacting_node = area.get_parent()

func detect_area_exited(area: Area2D) -> void:
	if area.is_in_group("gravitational_field"):
		print(area.get_parent())
		$canvas/planettitle.text = "Now exiting " + currentplanet.name.capitalize()
		$canvas/planetmessage.text = "into the vast reaches of space"
		$textanimator.play("show")
		currentplanet = null
	if area.is_in_group("interact"):
		interacting = false
		interacting_node = null

func pcent(what, out_of):
	return what / out_of * 100.0

func invert(val, max):
	return -val + max

func lerp_deg(angle : float, to : float, weight : float) -> float:
	return rad_to_deg(lerp_angle(deg_to_rad(angle), deg_to_rad(to), weight))

*rocket is a string var and not a node because I was experimenting with something a while ago

You definitely should put all physics relevant movement and rotation into _physics_process.

Also, for people to be able to help you, you should reduce your code to only the relevant sections.

1 Like

ye sorry about that

but I still don’t know whats causing it