- Edited
Box2D Controlled Character Movement
Hey Guys,
Integrating bodies/fixtures and [Spine's] bounding boxes seems trivial enough for simple collision detection, and I don't need any complicated rag doll effects, but where other posts seem to fall short of a full conclusion is when controlling a characters movement via Box2D as opposed to moving the skeleton directly. I only really need:
Needs: [Box2D] Collision detection; movement (apply force and velocity); friction, gravity, etc. [Spine] Animations.
Problem: Since I don't need joints for rag doll, I mapped a single body to a single skeleton with minimal bounding boxes for which fixtures were generated. When applying an animation, I simply reconstruct the fixture shapes by resetting the vertices (not super efficient I'm sure). This works fine when moving the skeleton, but as defined above, I want to use the physics engine to move my character. When I tried this, I could never get the shapes to properly align anymore.
Question: Whats the easiest way to do this? I could try using bodies instead of fixtures, but I never did understand how that could work, since I'd have to apply force / velocity to each body, e.g. head, body, arms, feet., which doesn't seem right. The alternative is joints, and simply move the central body (the characters body for example), but then it gets much more complicated and seems like super overkill for my needs.
Cheers,
Adrian
- http://esotericsoftware.com/forum/Getting-started-with-Box2D-1215 (relu had the closest to what I envisioned, but he/she is probably moving the skeleton directly which worked for me too)
- http://esotericsoftware.com/forum/Spine-Physics-box2d-with-more-accurate-collisions-3001?p=17776&hilit=fixtures#p17776
- http://esotericsoftware.com/forum/Help-With-Box2d-body-attachments-870?p=5140&hilit=fixtures#p5140
- https://github.com/EsotericSoftware/spine-workshop/blob/master/src/com/esotericsoftware/spine/workshop/I_Box2D.java[/list]
[EDIT]
If anyone is wants to know more about my fixture based solution, here are some details. tl;dr: when I apply linear velocity (body.setLinearVelocity), everything lines up and works nicely, but when I let go of the key controlling left or right movement, and the linear velocity is reset to 0, my fixtures are drawn... elsewhere. Illustrated below along with some code snippets. White lines are the physics fixtures and the rest is spine.
Linear Velocity Applied (Moving + Walking Animation)
No Linear Velocity Applied (No movement + Stationary Animation)
private Body LoadRigidBody(Skeleton skeleton) {
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(skeleton.getRootBone().getWorldX(), skeleton.getRootBone().getWorldY());
Gdx.app.log("Skeleton", "X: " + skeleton.getRootBone().getWorldX());
Gdx.app.log("Skeleton", "Y: " + skeleton.getRootBone().getWorldY());
Body body = physicsWorld.createBody(bodyDef);
for (Slot slot : skeleton.getSlots()) {
Attachment tmpAttachment = slot.getAttachment();
if (tmpAttachment != null && slot.getAttachment().getClass() == BoundingBoxAttachment.class) {
BoundingBoxAttachment attachment = (BoundingBoxAttachment)slot.getAttachment();
float[] vertices = new float[attachment.getVertices().length];
attachment.computeWorldVertices(slot.getBone(), vertices);
PolygonShape shape = new PolygonShape();
shape.set(vertices);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.friction = 1f;
fixtureDef.density = 1f;
fixtureDef.restitution = 0f;
fixtureDef.shape = shape;
body.createFixture(fixtureDef).setUserData(slot);
}
}
Gdx.app.log("Skeleton", "X: " + body.getPosition().x);
Gdx.app.log("Skeleton", "Y: " + body.getPosition().y);
return body;
}
Actual update code (Spine to Box2d and Box2d to Spine)
private void update(float deltaTime) {
mainCam.update();
physicsWorld.step(deltaTime, 6, 2);
UpdateAnimation(deltaTime);
... (apply linear velocity to player body and set animations)
UpdateSkeletonFromRigidBody(deltaTime);
}
private void UpdateAnimation(float deltaTime) {
animationState.update(deltaTime);
animationState.apply(playerSkeleton); // Poses playerSkeleton using current animations. This sets the bones' local SRT.
playerSkeleton.updateWorldTransform();
// TODO: Rigid body and skeleton not in sync unless animations are being played
//
for (Fixture fixture : playerBody.getFixtureList()) {
Slot slot = (Slot)fixture.getUserData();
BoundingBoxAttachment attachment = (BoundingBoxAttachment)slot.getAttachment();
if (fixture.getShape().getType() == Shape.Type.Polygon) {
float[] vertices = new float[attachment.getVertices().length];
attachment.computeWorldVertices(slot.getBone(), vertices);
PolygonShape shape = (PolygonShape)fixture.getShape();
shape.set(vertices);
}
}
}
private void UpdateSkeletonFromRigidBody(float deltaTime) {
Gdx.app.log("Skeleton", "X: " + playerSkeleton.getRootBone().getWorldX());
Gdx.app.log("Skeleton", "Y: " + playerSkeleton.getRootBone().getWorldY());
Gdx.app.log("Rigid Body", "X: " + playerBody.getPosition().x);
Gdx.app.log("Rigid Body", "Y: " + playerBody.getPosition().y);
playerSkeleton.getRootBone().setPosition(playerBody.getPosition().x, playerBody.getPosition().y);
playerSkeleton.updateWorldTransform(); // Uses the bones' local SRT to compute their world SRT.
}
[EDIT]
OK, [feels a bit strange talking to myself] I scrapped the code, bit the bullet and rewrote it using and array of bodies for contacts instead of fixtures allowing me to cleanly position bodies according to a bones world SRT, and it works nicely. The only time I reset a fixture's vertices now are when my skeleton has been flipped along the X axis.
I've still not been able to put my character under the control of the physics engine however (gravity, forces, etc.), which is a real downer. What I have tried so far is to draw a bounding box around the whole character in Spine, which sits under the root bone, then construct a completely separate dynamic physics body for this that is set as a sensor, with the sole purpose of simply reacting to forces/velocity changes (input) and re-positioning the skeleton accordingly every frame.
Unfortunately, I didn't think that because it's a sensor it will not collide, and thus sink through the ground and bring the skeleton with it! :bang:
I'm a bit stuck on this one for now, will have a long think about it, but if anyone has any advice to offer up on the following do let me know:
- Driving a a character via forces and having it react to gravity (jumps, etc.)
- Leveraging the bodies created as dynamic bodies so they react to collisions automatically (hitting walls, floor, etc.).
Cheers,
Adrian