So I'm going to assume 3 things, firstly; you want to click on the thing to move to start dragging and secondly; you want thorough explanations. But lastly and most importantly I assume you know how to adapt my solution to fit your needs.
Note: Numbers in brackets mean the term is explained at the bottom of the post.
Note: Where I have ellipses in the code I only have them for the sake of brevity.
I also apologise in advance for the wall of text...
Boilerplate code:
Code: Select all
function love.load()
end
function love.draw()
end
function love.update(dt)
end
Alright, so firstly add some global variables and some psuedo-constants:
Code: Select all
--globals
phys, gfx, mouse = nil, nil, nil
ball = {}
ground = {}
drag = false
--pseudo-contstants
BALL_RADIUS = 50
GRAVITY = 500
function love.load()
...
We will need to know when drag something so we have a
boolean (true/false) variable for that, we also know many functions will need access to our ball and the ground so we create variables for them too. For values we may need to tweak we have them declared globally at the top so we can easily change the value without looking through many lines of code, we have 2 here one for the radius of our ball and one for our gravity.
Next we use the
love.load() callback to set everything up:
Code: Select all
function love.load()
--i find it annoying to type love before everything :)
phys = love.physics
gfx = love.graphics
mouse = love.mouse
These first 4 lines are just some code I like to use so I know which modules I'm using and so I don't have to type as much. You could easily swap our
phys with
love.physics (etc.) wherever present in the code.
Code: Select all
--create physics world
world = phys.newWorld(gfx.getWidth()+500, gfx.getHeight()+500)
world:setGravity(0, GRAVITY)
When we create the physics world we need to set up an area where the physical objects exist, I use the windows height and width and add a few hundred pixels onto them so the world doesn't stop at the very edge of the window. Next we set the worlds gravity, the first argument to
setGravity() is the x gravity and the second is the y gravity. X axis goes from x -> n and y axis goes from y -> n. This means to get gravity that pulls downwards we need a positive number. Which is where our
GRAVITY pseudo-constant comes in to provide the value.
Code: Select all
--create the floor
ground["body"] = phys.newBody(world, 0, gfx.getHeight() - 10)
ground["shape"] = phys.newRectangleShape(ground.body, gfx.getWidth() / 2, 5, gfx.getWidth(), 10)
Now to create the ground so our ball doesn't fall into oblivion... To create a
newBody() we need to first specify what physical world it belongs to (ours is
world, conveniently), then we need to specify the X,Y coordinates of the body. Well, we want it flat against the right of the window so we set the X to 0 and we set the Y to the height of the window (which move is to the very bottom of the screen) and then take 10 pixels (which is going to be the height of our rectangle).
We next create the shape of our physical object. We must first specify it's body;
ground.body (remember we stored the body in our ground table as
'body'). Then the X,Y offset from the X,Y position of the body; we need to move half the screen across and 5 pixels up as the rectangle is created with 0,0 (the origin) being in the middle of the rectangle, we want it on the top right. Now we need to specify the width and height of the rectangle, which is the width of the screen (so the ground covers the entire bottom of the screen) and 10 pixels high.
Code: Select all
--create our ball
ball["body"] = phys.newBody(world, gfx.getWidth() / 2, gfx.getHeight() / 3)
ball["shape"] = phys.newCircleShape(ball.body, 0, 0, BALL_RADIUS)
ball.body:setMassFromShapes()
end
You should understand the first 2 lines, if not just look the up in the
LÖVE docs. Now what's happening on the 3rd line? We use the function
setMassFromShapes() which will automatically choose the best (or at least a very good) mass value for us.
Take a breather, make a cup of tea. Absorb this. If you understand this, awesome, time to move on. If you don't understand it don't hesitate to PM me!
Okay, ready to move on? Before you do,
run you program to make sure it works!
Code: Select all
function love.draw()
gfx.circle("fill", ball.body:getX(), ball.body:getY(), BALL_RADIUS)
gfx.rectangle("fill", ground.body:getX(), ground.body:getY(), gfx.getWidth(), 10)
end
All were doing here is drawing a circle and rectangle where they should be. You should glance over the documentation for the
body object.
Code: Select all
function love.update(dt)
if drag then
ball.body:setPosition(mouse.getPosition())
end
world:update(dt)
end
We check whether
drag is
true (
if drag then is equivalent to
if drag == true then) and if it is we must be dragging the object so we need to make sure the balls position is the same as you mouses position. So we call the
setPosition() function belonging to the
body object type. It takes 2 arguments X and Y, the
getPosition() takes care of both X and Y for us.
Next we call the
update() function of the world object, it needs to given the amount of passed since it last updated which, handily, the
love.update callback gets passed to it.
Code: Select all
function love.mousepressed(x,y,button)
if button == "l" then
if ball.shape:testPoint(mouse.getPosition()) then
drag = true
ball.body:setMass(0,0,0,0)
end
end
end
Now we check if the
'l' (LEFT) mouse button has been click (see the
MouseConstant doc page for a list of button constants).
Now for an extremely important bit, testing whether the mouse is over the ball when we click. We use the
testPoint() function of the
shape object type. We need to pass it the mouses position, if it is we set our global variable
drag to
true and set the mass of the ball to nothing using the
setMass() function belonging to the body object type. The first and second arguments are the X,Y offset from the center of the ball, the 3rd argument is the
mass setting this to 0 makes it unaffected by gravity (you may try setting a mass value to see the effect later on) and the last value is rotational inertia (a little advanced for this post).
Almost done! Just one last callback:
Code: Select all
function love.mousereleased()
if drag then
drag = false
ball.body:setLinearVelocity(0,0)
ball.body:setMassFromShapes()
end
end
All we are doing here is when the mouse is released we check whether we a dragging the ball (remember that we set
drag to
true when we are dragging) is we are we set
drag to
false (meaning we are no longer dragging the ball). The next line will give the ball zero liner velocity when you let go, so it no longer retains any of the velocity it had when you picked it up.
setLinearVelocity() takes 2 arguments, the X and Y velocity. This can be useful for making the ball continue travelling in the direction your mouse was moving, maybe you can add this in yourself! And finally we reset the mass to the automatically selected value, done!
Run it! You should have a fully functioning program! Have fun with it!
If yours isn't working have a glance over mine:
- draganddrop.love
- Completed drag and drop .love file
- (1.72 KiB) Downloaded 257 times