adding magnetic behavior to a KinenematicBody2D

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

Is it possible to give magnetic properties to a KinematicBody2D?
I have some balls to attract each other.
Is there any code examples?
I have read something about a solution with Area2D.
I would need a little tip on how to proceed.
I feel like I’ve read the whole internet.
If there is a very simple solution, I’ll hit my head against the wall

:bust_in_silhouette: Reply From: kidscancode

Area2D can influence gravity in its region. However, that only works with physics, i.e. RigidBody2D.

If you’re using two KinematicBody2D nodes, just move them towards each other. Use the standard Equations of Motion.

Here’s a quick example:

extends KinematicBody2D

var acceleration = Vector2()
var velocity = Vector2()
var attract_force = 100

func _ready():
	add_to_group("magnets")
	
func _physics_process(delta):
	acceleration = Vector2()
	for other in get_tree().get_nodes_in_group("magnets"):
		if other != self:
			var dir = (other.position - position).normalized()
			acceleration += dir * attract_force
			
	velocity += acceleration * delta
	velocity = move_and_slide(velocity)

Result:

example

:bust_in_silhouette: Reply From: TheFamousRat

Hello OsOgar,

Don’t make promises about banging your head against the well, especially in topics related to programming :p…

Let’s just go from a basic principle : in real world physics, the gravitational force between two objects is Gm1m2/d². The most important part of this is d² : the force is decreased by the inverse square of the distance. It means that for most objects you’d use in game, the gravitational force is negligible beyond a certain not-so-big distance, and since we’re dealing with limited resources, we will be able to confidently use an Area2D with a circle CollisionShape2D : the results shouldn’t look weird at all.

The method from Area2D that is relevant to your case is “get_overlapping_bodies()”. It will allow you to add forces to bodies that are near. I would advise you to add a “add_central_force” method in your KinematicBody2D. For any number of balls, the solution I would use would be the following :


Assuming the tree looks like this (notice the bigger “GravityRadius” CollisionShape2D)

Ball.gd

extends KinematicBody2D

var currentTotalForce : Vector2 = Vector2(0,0)
const gravityStrength : float = 10000.0#Works fine, but you can tune it

func add_central_force(toAdd : Vector2) -> void:
	currentTotalForce += toAdd

func _physics_process(delta):
	for i in $Area2D.get_overlapping_bodies():
		if i != self:
			var dist : float = (i.get_position() - self.get_position()).length()
			var attractStrength : float = gravityStrength / (dist*dist)
			var attractDirection : Vector2 = (self.get_position() - i.get_position()).normalized()
			i.add_central_force(attractDirection * attractStrength)
			
	self.move_and_slide(currentTotalForce)
	currentTotalForce = Vector2(0,0)

I think it should work fine. Be careful with this code, I didn’t check it and it might have some errors, but the main principle is here.