Drag and drop - Leakedbits/Codelabs GitHub Wiki
This tutorial tries to explain our DragAndDropSample, in which you can touch a ball and move it across the screen as if it were a yo-yo.
First off, we need to take into account a couple of concepts learned in our first tutorial, BouncingBall. We are going to need our OrtographicCamera
, World
, Box2DDebugRenderer
objects, and render()
, createWalls()
, createBall()
, dispose()
and show()
methods. For our current ball we are only changing ballShape.setRadius(1);
and bodyDef.position.set(0, 0);
, and also remember not to show the ramp.
We are going to need three more global variables: a MouseJoint
used to make a point on a body track a specified world point, its MouseJointDef
and a Vector3
in charge of storing where the user has last touched the screen.
/*
private MouseJoint mouseJoint;
private MouseJointDef mouseJointDef;
private Vector3 touchPosition;
In our show()
method let's instantiate that vector and also let's use our walls as the first point of our joint:
touchPosition = new Vector3();
Body walls = createWalls();
createMouseJointDefinition(walls);
How do we create that joint, you say? Easy enough. We only need to set the given parameter body
as the first attached body, establish that they should collide between each other and the maximum constraint force that our body is going to feel.
private void createMouseJointDefinition(Body body) {
mouseJointDef = new MouseJointDef();
mouseJointDef.bodyA = body;
mouseJointDef.collideConnected = true;
mouseJointDef.maxForce = 500;
}
Now we need to code what our ball is going to do when the user interacts with his screen. The framework provides us three very handy, self-explained methods: touchDown
, touchUp
and touchDragged
, so we just need to override them with a handful of lines.
For the first one, it receives four parameters, but we only need the first two for knowing the coordinates of the point the user has touched. Now it gets a little tricky: we need to define a QueryCallBack
that will be used to know in advanced if any fixture will overlap the touched point.
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
QueryCallback queryCallback = new QueryCallback() {
Inside our callback, it is necessary to override the method in charge of discovering if the hit point is inside the body's fixture, creating a joint between them if so.
@Override
public boolean reportFixture(Fixture fixture) {
boolean testResult;
if (testResult = fixture.testPoint(touchPosition.x,
touchPosition.y)) {
mouseJointDef.bodyB = fixture.getBody();
mouseJointDef.target.set(touchPosition.x, touchPosition.y);
mouseJoint = (MouseJoint) world.createJoint(mouseJointDef);
}
return testResult;
}
};
Then we unproject()
into our world
's camera
(remember this from SpawnBodiesOnTouch) the point where the user has touched, or translate the coordinates into our world's camera, and we create the ball with the coordinates our world understands. Also, we query the world
with our previous callback and the previous position where the user has touched.
camera.unproject(touchPosition.set(screenX, screenY, 0));
world.QueryAABB(queryCallback, touchPosition.x, touchPosition.y,
touchPosition.x, touchPosition.y);
return true;
}
Don't worry, the other two methods are easier to understand. Let's dig into what happens when the user stops touching the screen: nothing happens if there's no joint, but informs that the touchUp
event has finished and destroys the joint otherwise:
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
boolean processed = false;
if (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
processed = true;
}
return processed;
}
Something similar happens when the user is moving his finger across the screen. Nothing happens when there's no joint, but if there is one we unproject
where the user is touching and we set the joint's target to where the user touched before that:
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
boolean processed = false;
if (mouseJoint != null) {
camera.unproject(touchPosition.set(screenX, screenY, 0));
mouseJoint.setTarget(new Vector2(touchPosition.x, touchPosition.y));
}
return processed;
}
Enjoy your yo-yo!
Remember! You have all this working code in our DragAndDropSample class.