Getting a Body2D (checker) to snap to a Shape2D's position (checkerboard square) once it's dragged and dropped

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

Hi all, I’ve done a few Godot tutorials and am now trying to do my first simple independent project, so sorry for the noob question.

I’m creating a very basic game of checkers. I’ve created the game board and a single token (KinematicBody2d with Sprite and CollisionShape2D children), and I’ve put an Area2D with a CollisionShape2D child over one single square (which will later be used on all squares).

I’ve figured out the code to move the checker around using my mouse. My question is how can I make it so that when I let go of the mouse on top of the square (i.e. anywhere within the Area2D Square), how can I get the checker to snap into place precisely at the same position as the square?

Here is my code so far for the mouse drag and drop:

extends KinematicBody2D

# Pickable needs to be selected from the inspector

var can_grab = false
var grabbed_offset = Vector2()

func _input_event(viewport, event, shape_idx):
	if event is InputEventMouseButton:
		can_grab = event.pressed
		grabbed_offset = position - get_global_mouse_position()

func _process(delta):
	if Input.is_mouse_button_pressed(BUTTON_LEFT) and can_grab:
		position = get_global_mouse_position() + grabbed_offset

And here’s my (undoubtedly bad) try at connecting the two nodes and getting them into the same position on release of the mouse, which doesn’t work:

func _on_Square_body_entered(body):
	if not Input.is_mouse_button_pressed(BUTTON_LEFT) and can_grab:
		body.global_position = $Square.global_position
:bust_in_silhouette: Reply From: Thomas Karcher

The “body entered” signal is only emitted once, right when the checker touches the square for the first time. That’s why by the time you release the mouse button, there’s no event triggering the repositioning.

Also, I’d assume the checker can touch more than one square at the same time, so I’d rather check for the mouse than for the checker touching the square.

Assuming your tree looks like this:

Main

  • Square
  • Checker
  1. Create a variable “target_square” in Main.gd

  2. In Square.gd:

func _on_Square_mouse_entered():
	get_parent().target_square = self
	
func _on_Square_mouse_exited():
	if get_parent().target_square == self:
		get_parent().target_square = null
  1. Change _input_event in Checker.gd as follows:
func _input_event(viewport, event, shape_idx):
	if event is InputEventMouseButton:
		# snap to target square if released next to it
		var ts = get_parent().target_square
		if can_grab and !event.pressed and is_instance_valid(ts):
			global_position = ts.global_position
		can_grab = event.pressed
		grabbed_offset = position - get_global_mouse_position()