How can I make only one object follow the mouse?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By HarryCourt
:warning: Old Version Published before Godot 3 was released.

I have a small problem. I have two objects in my scene, which I can move with the mouse, drag and place them wherever I want. However, I can not figure out how to be able to pick up one object, and not pick up the other whilst still holding to the other one.

Here is the problem I am facing: Screen capture - 81b58c370c25c3abf23394e38a082fad - Gyazo

Item.gd:

extends KinematicBody2D


# Variables that make it work
onready var spawner = get_node("/root/Scene/Player/Spawner")
onready var player = get_node("/root/Scene/Player")
onready var mouseRotation = 0

# True or False Variables
onready var canDrag = false
onready var isDragging = false


# Sound Nodes
onready var itemDelete = get_node("/root/Scene/Player/Sounds/ItemDelete")
onready var itemRotate = get_node("Sounds/Rotate")

# Stats
onready var GRAVITY = 200.0
var velocity = Vector2()

func _process(delta):
	# In order to move the object, you have to click it.
	
	var devMode = checkBoxNode.devCheckBox
	
	if (Input.is_action_pressed("ui_grabItem") && canDrag == true && devMode == true):
		dragItem()
	
	# Once let go, it will lock.
	if (Input.is_action_just_released("ui_grabItem")):
		canDrag = false
		isDragging = false

# This has the ability to rotate items
func _input(event):
	
	if event is InputEventMouseButton:
		
		var devMode = checkBoxNode.devCheckBox
		
		if event.button_index == BUTTON_WHEEL_UP && isDragging == true && devMode == true:
			rotateRight()
		if event.button_index == BUTTON_WHEEL_DOWN && isDragging == true && devMode == true:
			rotateLeft()


# These are basic functions that have no need for changing.


func rotateRight():
			mouseRotation += 5
			itemRotate.play()
			
			self.set_rotation_degrees(mouseRotation)

func rotateLeft():
			mouseRotation -= 5
			itemRotate.play()
			
			self.set_rotation_degrees(mouseRotation)

func dragItem():
	isDragging = true
	
	var mousePos = get_global_mouse_position()
	self.set_position(mousePos)
	
	if (Input.is_action_just_pressed("ui_deleteOnItem")):
		itemDelete.play()

# If the mouse is hovering over the object, it can pick it up.
func _on_Item_mouse_entered():
	canDrag = true

func _on_Item_mouse_exited():
	canDrag = false
	var devMode = checkBoxNode.devCheckBox
	
	if (Input.is_action_pressed("ui_grabItem") && devMode == true):
		canDrag = true
		
	if (Input.is_action_pressed("ui_grabItem") && devMode == false):
		canDrag = false
:bust_in_silhouette: Reply From: Zylann

You end up dragging the other item for two reasons:

  1. because you are starting to drag in _process, while your drag action is not supposed to start every frame, right? is_action_pressed tells you if the button is held, not if it has just been pressed.

  2. you are not checking if you are dragging an item already, so even if you used _input to listen for the click event as it happens, you would potentially start dragging the two items because they could be under your mouse (though I didn’t test that situation, I could be wrong)

Thank you Zylann for your answer, and I have moved my chunk of code into _input. However, could you please give me an example of how to avoid the problem? I can’t think of any way.

HarryCourt | 2018-01-24 15:00

I tried the following, which works fine, except for point 2) which I had to workaround using groups:

extends KinematicBody2D

var _dragging = false

func _input(event):
	if event is InputEventMouseMotion:
		if _dragging:
			position += event.relative

func _on_Item_input_event( viewport, event, shape_idx ):
	if event is InputEventMouseButton:
		if event.pressed:
			if event.button_index == BUTTON_LEFT:
				if get_tree().get_nodes_in_group("dragged").empty():
					add_to_group("dragged")
					_dragging = true
		elif _dragging:
			remove_from_group("dragged")
			_dragging = false

The group usage is a bit hacky, in a more “standard” input approach, we should be able to mark the input event as handled (so it would not propagate to further items), which doesn’t seem to be possible at the moment.

Other approaches exist, such as picking stuff from a unique script instead of delegating that task to every item.

Zylann | 2018-01-24 20:19