diff --git a/spine-libgdx/test/com/esotericsoftware/spine/Box2DExample.java b/spine-libgdx/test/com/esotericsoftware/spine/Box2DExample.java new file mode 100644 index 000000000..1f855de61 --- /dev/null +++ b/spine-libgdx/test/com/esotericsoftware/spine/Box2DExample.java @@ -0,0 +1,218 @@ + +package com.esotericsoftware.spine; + +import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; +import com.esotericsoftware.spine.attachments.Attachment; +import com.esotericsoftware.spine.attachments.AttachmentType; +import com.esotericsoftware.spine.attachments.RegionAttachment; + +import com.badlogic.gdx.ApplicationAdapter; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.backends.lwjgl.LwjglApplication; +import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; +import com.badlogic.gdx.graphics.GL10; +import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.math.Matrix4; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.Body; +import com.badlogic.gdx.physics.box2d.BodyDef; +import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; +import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer; +import com.badlogic.gdx.physics.box2d.FixtureDef; +import com.badlogic.gdx.physics.box2d.PolygonShape; +import com.badlogic.gdx.physics.box2d.World; + +public class Box2DExample extends ApplicationAdapter { + SpriteBatch batch; + ShapeRenderer renderer; + SkeletonRenderer skeletonRenderer = new SkeletonRenderer(); + + TextureAtlas atlas; + Skeleton skeleton; + Animation animation; + float time; + + OrthographicCamera camera; + Box2DDebugRenderer box2dRenderer; + World world; + Body groundBody; + Matrix4 transform = new Matrix4(); + Vector2 vector = new Vector2(); + + public void create () { + batch = new SpriteBatch(); + renderer = new ShapeRenderer(); + + atlas = new TextureAtlas(Gdx.files.internal("spineboy.atlas")); + + // This loader creates Box2dAttachments instead of RegionAttachments for an easy way to keep + // track of the Box2D body for each attachment. + AtlasAttachmentLoader atlasLoader = new AtlasAttachmentLoader(atlas) { + public Attachment newAttachment (Skin skin, AttachmentType type, String name) { + Box2dAttachment attachment = new Box2dAttachment(name); + AtlasRegion region = atlas.findRegion(attachment.getName()); + if (region == null) throw new RuntimeException("Region not found in atlas: " + attachment); + attachment.setRegion(region); + return attachment; + } + }; + SkeletonJson json = new SkeletonJson(atlasLoader); + json.setScale(0.05f); + SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("spineboy.json")); + animation = skeletonData.findAnimation("walk"); + + skeleton = new Skeleton(skeletonData); + skeleton.x = -32; + skeleton.y = 1; + skeleton.updateWorldTransform(); + + // See Box2DTest in libgdx for more detailed information about Box2D setup. + camera = new OrthographicCamera(48, 32); + camera.position.set(0, 16, 0); + box2dRenderer = new Box2DDebugRenderer(); + createWorld(); + + // Create a body for each attachment. Note it is probably better to create just a few bodies rather than one for each + // region attachment, but this is just an example. + for (Slot slot : skeleton.getSlots()) { + if (!(slot.getAttachment() instanceof Box2dAttachment)) continue; + Box2dAttachment attachment = (Box2dAttachment)slot.getAttachment(); + + PolygonShape boxPoly = new PolygonShape(); + boxPoly.setAsBox(attachment.getWidth() / 2 * attachment.getScaleX(), + attachment.getHeight() / 2 * attachment.getScaleY(), vector.set(attachment.getX(), attachment.getY()), + attachment.getRotation() * MathUtils.degRad); + + BodyDef boxBodyDef = new BodyDef(); + boxBodyDef.type = BodyType.StaticBody; + attachment.body = world.createBody(boxBodyDef); + attachment.body.createFixture(boxPoly, 1); + + boxPoly.dispose(); + } + } + + public void render () { + float delta = Gdx.graphics.getDeltaTime(); + float remaining = delta; + while (remaining > 0) { + float d = Math.min(0.016f, remaining); + world.step(d, 8, 3); + time += d; + remaining -= d; + } + + camera.update(); + + Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + batch.setProjectionMatrix(camera.projection); + batch.setTransformMatrix(camera.view); + batch.begin(); + + animation.apply(skeleton, time, true); + skeleton.x += 8 * delta; + skeleton.updateWorldTransform(); + skeletonRenderer.draw(batch, skeleton); + + batch.end(); + + // Position each attachment body. + for (Slot slot : skeleton.getSlots()) { + if (!(slot.getAttachment() instanceof Box2dAttachment)) continue; + Box2dAttachment attachment = (Box2dAttachment)slot.getAttachment(); + if (attachment.body == null) continue; + float x = skeleton.x + slot.getBone().getWorldX(); + float y = skeleton.y + slot.getBone().getWorldY(); + float rotation = slot.getBone().getWorldRotation(); + attachment.body.setTransform(x, y, rotation * MathUtils.degRad); + } + + box2dRenderer.render(world, camera.combined); + } + + public void resize (int width, int height) { + batch.setProjectionMatrix(camera.projection); + renderer.setProjectionMatrix(camera.projection); + } + + private void createWorld () { + world = new World(new Vector2(0, -10), true); + + float[] vertices = {-0.07421887f, -0.16276085f, -0.12109375f, -0.22786504f, -0.157552f, -0.7122401f, 0.04296875f, + -0.7122401f, 0.110677004f, -0.6419276f, 0.13151026f, -0.49869835f, 0.08984375f, -0.3190109f}; + + PolygonShape shape = new PolygonShape(); + shape.set(vertices); + + // next we create a static ground platform. This platform + // is not moveable and will not react to any influences from + // outside. It will however influence other bodies. First we + // create a PolygonShape that holds the form of the platform. + // it will be 100 meters wide and 2 meters high, centered + // around the origin + PolygonShape groundPoly = new PolygonShape(); + groundPoly.setAsBox(50, 1); + + // next we create the body for the ground platform. It's + // simply a static body. + BodyDef groundBodyDef = new BodyDef(); + groundBodyDef.type = BodyType.StaticBody; + groundBody = world.createBody(groundBodyDef); + + // finally we add a fixture to the body using the polygon + // defined above. Note that we have to dispose PolygonShapes + // and CircleShapes once they are no longer used. This is the + // only time you have to care explicitely for memomry managment. + FixtureDef fixtureDef = new FixtureDef(); + fixtureDef.shape = groundPoly; + fixtureDef.filter.groupIndex = 0; + groundBody.createFixture(fixtureDef); + groundPoly.dispose(); + + PolygonShape boxPoly = new PolygonShape(); + boxPoly.setAsBox(1, 1); + + // Next we create the 50 box bodies using the PolygonShape we just + // defined. This process is similar to the one we used for the ground + // body. Note that we reuse the polygon for each body fixture. + for (int i = 0; i < 20; i++) { + // Create the BodyDef, set a random position above the + // ground and create a new body + BodyDef boxBodyDef = new BodyDef(); + boxBodyDef.type = BodyType.DynamicBody; + boxBodyDef.position.x = -24 + (float)(Math.random() * 48); + boxBodyDef.position.y = 10 + (float)(Math.random() * 100); + Body boxBody = world.createBody(boxBodyDef); + + boxBody.createFixture(boxPoly, 1); + } + + // we are done, all that's left is disposing the boxPoly + boxPoly.dispose(); + } + + public void dispose () { + atlas.dispose(); + } + + static class Box2dAttachment extends RegionAttachment { + Body body; + + public Box2dAttachment (String name) { + super(name); + } + } + + public static void main (String[] args) throws Exception { + LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); + config.title = "Box2D - Spine"; + config.width = 640; + config.height = 480; + new LwjglApplication(new Box2DExample(), config); + } +}