Drag and Drop zone

Hello, I’m trying to make a draggable object stay only in a specific zone or return to its original position. I’ve already implemented the return functionality, but I can’t figure out how to detect collisions with the target zone. I’ve tried setting up triggers, checking group/mask properties via messaging, but nothing works. Please guide me in the right direction and correct my example so I can understand what I’m doing wrong. Thank you in advance.

I downloaded your example and indeed there was no interaction between cursor and drop zone.

Luckily the issue is easy to fix :slight_smile:

Your cursor’s collision object have group “cursor” and mask “drop_zone, draggable”.
Your draggable object’s collision object have group “draggable” and mask “cursor, drop_zone”.
Your drop zone’s collision object have group “drop_zone” and mask “draggable”.

I believe that there are collisions happening between draggable object and drop zone. However your script is connected only to the cursor object, therefore only messages related to cursor are routed to it.

Since cursor is not in the drop zone’s mask, there is no collision between drop zone and cursor.

Simply change drop zone’s collision object mask from “draggable” to “draggable, cursor” or just “cursor” and your print statement will work:
DEBUG:SCRIPT: зона сброса

However remember that trigger will only emit message when collision between kinematic and trigger object changes. You may have to track there messages to decide if cursor is inside trigger zone.

Luckily again, only 2 lines have to be changed. You have to add proper flag and set it state in response to the message. Find the modified cursor script below. Modified line are number 8 and 38.

function init(self)
	msg.post(".", "acquire_input_focus")
	self.collision_id = nil     -- id of gameobject which the cursor collided with
	self.dragged_id = nil       -- id of gameobject currently being dragged
	self.dragged_pos = nil		-- The position of the dragged_id game object
	self.pressed = false		-- Binary flag indicating if the user has touched the screen

	self.is_inside = false
end

function update(self, dt)
	-- Add update code here
	-- Learn more: https://defold.com/manuals/script/
	-- Remove this function if not needed

	-- Set the render clear color--aka, screen color when nothing is rendered
	msg.post("@render:", "clear_color",
	{ color = vmath.vector4(95 /256, 129 / 256, 161 / 256, 1 ) })

	-- Get the current position of the cursor
	local pos = go.get_position()

	-- Set the label to echo the cursor position (pos.x and pos.y)
	label.set_text("#cursorLabel", "---- x: " .. pos.x .. "  y: " .. pos.y)

	-- Reset the control variable for collision_id each frame
	self.collision_id = nil

	if not self.pressed and self.dragged_id then
		local s1 = vmath.vector3(1.0)
		go.set_scale(s1, self.dragged_id)
		self.dragged_id = nil
	end
end

function on_message(self, message_id, message, sender)
	if message_id == hash("trigger_response") and message.other_group == hash("drop_zone") then
		self.is_inside = not self.is_inside
		print('Is inside drop zone?', self.is_inside)
	elseif message_id == hash("collision_response") then
		if not self.collision_id or
		go.get_position(self.collision_id).z < message.other_position.z then
			self.collision_id = message.other_id
			self.touchedGroup = message.other_group

		end
	end
end

function on_input(self, action_id, action)
	-- Add input-handling code here. The game object this script is attached to
	-- must have acquired input focus:
	--
	--    msg.post(".", "acquire_input_focus")
	--
	-- All mapped input bindings will be received. Mouse and touch input will
	-- be received regardless of where on the screen it happened.
	-- Learn more: https://defold.com/manuals/input/
	-- Remove this function if not needed
	if not action_id or action_id == hash("touch") then
		local action_pos = vmath.vector3(action.x, action.y, 0)
		go.set_position(action_pos)
		
		if action.pressed then
			msg.post("#collisionobject", "enable")
			self.pressed = true
			print("TOUCH")
		elseif action.released then
			msg.post("#collisionobject", "disable")
			self.pressed = false
			print("DROP")
		end
		-- Обработка выбора объекта
		if self.pressed and self.collision_id and not self.dragged_id then
			self.dragged_id = self.collision_id
			self.dragged_pos = action_pos
			go.set_scale(vmath.vector3(1.2), self.dragged_id)
		end

		-- Активация перетаскивания при смещении
		if self.dragged_id and not self.dragging then
			if vmath.length(self.dragged_pos - action_pos) > 20 then
				self.dragging = true
			end
		end

		-- Перемещение объекта
		if self.dragging then
			go.set_position(action_pos, self.dragged_id)
		end
	end

end

Expected console output:

The rest is up to you, you just need to implement object return to original position if it was dropped outside dropzone, but I’m sure you can do it yourself. Nevertheless, do not hestiate to ask for more help if needed :slight_smile:

3 Likes