Dragging an object/image, making it clickable

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
gignat
Prole
Posts: 7
Joined: Mon Jan 30, 2012 7:03 am

Dragging an object/image, making it clickable

Post by gignat »

hi there,

how do I tell love, that a certain callback responds only within a fixed field, like 160X160 of a certain picture ? For example if id like to drag an object along, how do I specify that I want the callback to work only if i click within the confines of a specific field, or .png res, and not anywhere on the screen ?

I kinda have an idea how to do it with rectangles for example, you set it with delta rect.width, and hight, etc. But with image, I dont know how to make that interval...

hope that makes sense

kind regards

gignat
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Dragging an object/image, making it clickable

Post by MarekkPie »

https://love2d.org/wiki/Image

You do it the exact same way. Since you are providing a callback with the image I assume you are making it an object already, so just add a rectangular bounding box with the linked functions.
gignat
Prole
Posts: 7
Joined: Mon Jan 30, 2012 7:03 am

Re: Dragging an object/image, making it clickable

Post by gignat »

greetings Marekk, thank you for your reply.


been trying the whole day, with no results. I ended up using this code (https://gist.github.com/1196228) and just pasted an image on top of that rectangle. Im a complete noob otherwise. This works though, but I cheated :D

why does he use tables ? Is there no other way to drag something around than to put it on top of a rectangle ? What if a certain shape cannot cover the triangle?

EDIT : I wanted to try changing the color of the rectangle, but with this, for some reason, it colors both the rect and the picture on top of it

Code: Select all

function love.draw()
	
	love.graphics.rectangle("fill", rect.x, rect.y, rect.width, rect.height)
	love.graphics.setColor(0, 255, 0, 100)
	love.graphics.draw(dexter, rect.x-45, rect.y-68)
end

thank you for your input !

gignat
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Dragging an object/image, making it clickable

Post by MarekkPie »

That's kinda what you do, but you don't need to draw the rectangle. Your bounding box is a completely abstract structure. You could make a bounding box a circle, a hexagon, a donut, whatever. It's just data representing the clickable area of your image. The computer doesn't need a visual representation of it to decide if it is within the bounding box. Attached is a quick and dirty example of what you could do to make it a bit more extendable than your current process.

What I was getting at in my first post was something like this (taken from the attached example):

Code: Select all

function newDraggable(x, y, img)
	return {
		x = x,
		y = y,
		img = img,
		w = img:getWidth(),
		h = img:getHeight()
	}
end
This simply couples the position of the image with the properties you could already get from your image.

As for your second problem, think of love.graphics.setColor as a global variable color, so when you do:

Code: Select all

love.graphics.setColor(255,0,0)
It's "kinda" like doing this:

Code: Select all

color = {255,0,0}
Now every drawable object looks at this global variable and applies it. So let's walk through it:

Code: Select all

-- First iteration
function love.draw()
	love.graphics.rectangle(...)	-- reads the default color {255,255,255}
	love.graphics.setColor(255,0,0) -- sets the "color" variable to {255,0,0}
	love.graphics.draw(...)			-- reads {255,0,0} for its color
end

-- Second iteration
function love.draw()
	love.graphics.rectangle(...)	-- reads {255,0,0} for its color
	-- etc
end
The fix:

Code: Select all

function love.draw()
	love.graphics.setColor(255,255,255)	-- always sets the color back to default at the start of the draw loop
	love.graphics.rectangle(...)
	love.graphics.setColor(255,0,0)
	love.graphics.draw(...)
end
EDIT: changed the newDraggable so someone doesn't go "Hem, 'tis more efficient if thou werest to do it in such a way as this..."
Attachments
draggin.love
(4.56 KiB) Downloaded 238 times
User avatar
hryx
Party member
Posts: 110
Joined: Mon Mar 29, 2010 2:28 am
Location: SF<CA<USA

Re: Dragging an object/image, making it clickable

Post by hryx »

Here's a demo that does what MarekkPie's does, except that the icon doesn't center on the mouse whenever clicked. It does this by keeping track of the mouse's offset from the icon upon being clicked. It uses love.mousepressed() and love.mousereleased() to change the "being dragged" state, rather than checking the mouse button's state during love.update(). (I suppose this method could have its own drawbacks in a real application.)

Please check the code and let me know if there is a smarter way to do this. This is my first time sharing original code with people if I recall.
Attachments
drag-n-drop.love
Click-to-drag demo
(1.46 KiB) Downloaded 261 times
gignat
Prole
Posts: 7
Joined: Mon Jan 30, 2012 7:03 am

Re: Dragging an object/image, making it clickable

Post by gignat »

wow guys, youre the best :awesome: :awesome:

@Marekk : thank you so much for taking the time and writing the example, it has helped my understanding alot. I have a question though, well I have lots, but ill focus on one :))

why do you halve the graphics getiWidth and getHeight at the beggining ?

Code: Select all

pie = newDraggable(
		love.graphics.getWidth() / 2,
		love.graphics.getHeight() / 2,
		love.graphics.newImage("cherrypie_obey.jpg"))
	grabbed = false
also, the within function makes my brain cook :monocle:

Code: Select all

function within(x,l,u)
	if x < l then return -1
	elseif x > u then return 1
	else return 0 end
end
why do we need it ? doesnt the clause in love update, cover our condition of checking mouse click, and if that click was made in the confines of the pie area ?? Btw I LOLed when I saw the pie loaded up first time :joker:

@hryx : thank you very much also man. You have a different version, which is kind of similar to what I initially started with, checking only the presses, and releases.

Code: Select all

icon = {}
	icon.img = img_default
	icon.w = icon.img:getWidth()
	icon.h = icon.img:getHeight()
	icon.x = love.graphics.getWidth() / 2 - icon.w / 2
	icon.y = love.graphics.getHeight() / 2 - icon.h / 2
2 things bother me.

a)Why do you need to do icon.img = img_default ? Cant you simply check the properties with the img_default ??
b) again the half fraction...you divide the screen width and length with 2 but subtract our objects properties also


thanks very, very much. This community is becoming the greatest thing ive come across lately.

gignat
User avatar
hryx
Party member
Posts: 110
Joined: Mon Mar 29, 2010 2:28 am
Location: SF<CA<USA

Re: Dragging an object/image, making it clickable

Post by hryx »

gignat wrote:a)Why do you need to do icon.img = img_default ? Cant you simply check the properties with the img_default ??
I used two images in the demo just for fun. love.draw() will always draw whatever is referenced in icon.img. When you click on the icon it changes that value to the image img_clicked, and when you release the mouse it returns to img_default.

A different way of doing it:

Code: Select all

...
love.draw()
    if icon.is_being_dragged then
        love.graphics.draw(img_clicked, icon.x, icon.y)
    else
        love.graphics.draw(img_default, icon.x, icon.y)
    end
end
...
...but I try to avoid putting conditional statements in love.draw() whenever possible. Anyway, that's not really to the point of your original question, so you can ignore that part for now!
gignat wrote:b) again the half fraction...you divide the screen width and length with 2 but subtract our objects properties also
If you're talking about this:

Code: Select all

icon.x = love.graphics.getWidth() / 2 - icon.w / 2
icon.y = love.graphics.getHeight() / 2 - icon.h / 2
...then it's a trivial bit. All it does is center the image on the window. The X position is set to half the window width, compensating for the width of the image itself, and the next line of code does likewise for the Y position.
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Dragging an object/image, making it clickable

Post by MarekkPie »

The width/height is just the x,y coordinates of my image at the start of the demo.

The "within" is my little way of making n-dimensional containment easier. The normal containment function (for 2 dimensions, assuming axis-alignment (fancy way of saying "not at an angle")) would be:

Code: Select all

function contains(x,y,rect)
    if x > rect.x and x < rect.x+rect.w and y > rect.y and y < rect.y+rect.h then
      return true
    end
    return false
end
...which I find both terribly annoying and hard to read. What are you really doing here? You are saying "Is x within the left of the rectangle and the right of the rectangle, and is y within the top of the rectangle and the bottom of the rectangle?"

So I made "within", which is just containment in one dimension. I also made it give you three response, -1 if you are less than the lower bound, 0 is you are within the bounds, and 1 if you are greater than the upper bound. So now, if I ever need to check positioning, I have a function for that already.

Example:

Code: Select all

function getRelativePosition(x,y,rect)
    return within(x,rect.x,rect.x+rect.w), within(y,rect.y,rect.y+rect.h) -- <= returns 9 possible positions relative to the rect
end
A "visual example" of why hryx subtracted half of his image. I'd rather not draw at the moment, so ASCII it is:

Code: Select all

======
=    =
=    =
======
We have a rectangle of size 6x4. We want the image in the center of our screen. If we just put it at (windowWidth/2,windowHeight/2), then it places the top-left corner of the image at that position, which is not exactly the center. We are trying to put the center of the picture in the center of the screen, but we have overshot by (3,2) (half the width of our rectangle, half the height of our rectangle).

(I didn't bother on mine, because...it's a demo, who really cares?)
coffee
Party member
Posts: 1206
Joined: Wed Nov 02, 2011 9:07 pm

Re: Dragging an object/image, making it clickable

Post by coffee »

Well nice work with the small demo because if both of you didn't notice if you add a mouse over state and a table for multiple clickable objects to your demos you actually have a valuable and complete drag and drop mouse library. You should add it to wiki snippets.
User avatar
MarekkPie
Inner party member
Posts: 587
Joined: Wed Dec 28, 2011 4:48 pm
Contact:

Re: Dragging an object/image, making it clickable

Post by MarekkPie »

I could probably build it out a bit more, make it more generalized, but I guess you're right.

I also just noticed that getRelativePosition is a superset of containment. So you could just go:

Code: Select all

function contains(x,y,rect)
    local xVal, yVal = getRelativePosition(x,y,rect)
    if xVal == 0 and yVal == 0 then return true end
    return false
end
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 8 guests