How to restrict the movement of a KinematicBody2D to a circle?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By solstice

I know how to restrict the movement to a circle by setting its position directly:

  func clamp_vector(vector, clamp_origin, clamp_length):
    var offset = vector - clamp_origin
    return clamp_origin + offset.clamped(clamp_length)

And then:

position = clamp_vector(position, Vector2.ZERO, 150)

This would force the body to only be able to move within a circle with radius 150 around the origin.

But I can’t figure out how to do that using move_and_slide.

Edit:
**Essentially, I want to mimic a distance joint, but since I have a kinematic body, they won’t work. **

Any help is appreciated!
Thanks.

I don’t understand why Joint2D cannot work with a KinematicBody2D.

BraindeadBZH | 2019-08-31 15:56

It’s because Joint2D only works with RigidBody2D or StaticBody2D but not with KinematicBody2D. I mean, it does “work” but the movement of the KinematicBody isn’t restricted by the Joint.

solstice | 2019-09-02 10:39

:bust_in_silhouette: Reply From: TraumaticHug

While not for the same purpose, I have a KinematicBody2D clamped to a radius around the player as a top-down crosshair, using the right-stick of a controller to move the crosshair.

The crosshair is a child node of the player, hence my using $Crosshair. It can move around within a circle around the player, like in Streets of Rogue.
get_action_strength() is used as I was using a controller, with the aim_right etc mapped to the right stick in the Input Map

get_player_aim() is called in _physics_process(delta)

func get_player_aim():
	var crosshair_speed = 1000

	player_aim = Vector2(
			float(Input.get_action_strength("aim_right")) -
			float(Input.get_action_strength("aim_left")),
			float(Input.get_action_strength("aim_down")) -
			float(Input.get_action_strength("aim_up"))
			).normalized() * crosshair_speed

	$Crosshair.position = $Crosshair.position.clamped(160)		
	$Crosshair.move_and_slide(player_aim)

Alternatively, the code below instantly snaps the crosshair to the circle / radius specified, nothing in-between.
(Annoyingly, it also snaps a little to the up, down, left and right, which I’m not 100% sure how to correct yet. It might just be the cheap controller I’m using or the deadzones.)
This doesn’t use move_and_slide(), though.

“crosshair_distance” is the distance the crosshair is from the player / it’s parent node

func get_player_aim():
	var crosshair_distance : = 250	

	player_aim = Vector2(
			float(Input.get_action_strength("aim_right")) -
			float(Input.get_action_strength("aim_left")),
			float(Input.get_action_strength("aim_down")) - 
			float(Input.get_action_strength("aim_up"))
			).normalized() * crosshair_distance

	$Crosshair.position = player_aim