Java tutorial
/** * * Copyright 2013 Martijn Brekhof * * This file is part of Catch Da Stars. * * Catch Da Stars is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Catch Da Stars is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Catch Da Stars. If not, see <http://www.gnu.org/licenses/>. * */ package com.strategames.engine.utils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.physics.box2d.CircleShape; import com.badlogic.gdx.physics.box2d.Fixture; import com.badlogic.gdx.physics.box2d.FixtureDef; import com.badlogic.gdx.physics.box2d.PolygonShape; import com.badlogic.gdx.utils.JsonReader; import com.badlogic.gdx.utils.JsonValue; /** * Loads the collision fixtures defined with the Physics Body Editor * application. You only need to give it a body and the corresponding fixture * name, and it will attach these fixtures to your body. * * @author Aurelien Ribon | http://www.aurelienribon.com * * Martijn Brekhof: Updated to support breaking objects * TODO: create list of fixtures in advance to increase performance when recreating objects */ public class BodyEditorLoader { // Model private final Model model; // Reusable stuff private final List<Vector2> vectorPool = new ArrayList<Vector2>(); private final PolygonShape polygonShape = new PolygonShape(); private final CircleShape circleShape = new CircleShape(); private final Vector2 vec = new Vector2(); // ------------------------------------------------------------------------- // Ctors // ------------------------------------------------------------------------- public BodyEditorLoader(FileHandle file) { if (file == null) throw new NullPointerException("file is null"); model = readJson(file.readString()); } public BodyEditorLoader(String str) { if (str == null) throw new NullPointerException("str is null"); model = readJson(str); } // ------------------------------------------------------------------------- // Public API // ------------------------------------------------------------------------- public void setupVertices(String name, float scale) throws RuntimeException { RigidBodyModel rbModel = model.rigidBodies.get(name); if (rbModel == null) throw new RuntimeException("Name '" + name + "' was not found."); Vector2 origin = vec.set(rbModel.origin).scl(scale); for (int i = 0, n = rbModel.polygons.size(); i < n; i++) { PolygonModel polygon = rbModel.polygons.get(i); Vector2[] vertices = polygon.buffer; for (int ii = 0, nn = vertices.length; ii < nn; ii++) { vertices[ii] = new Vector2().set(polygon.vertices.get(ii)).scl(scale); vertices[ii].sub(origin); } } for (int i = 0, n = rbModel.circles.size(); i < n; i++) { CircleModel circle = rbModel.circles.get(i); circle.centerBuffer = newVec().set(circle.center).scl(scale); circle.radiusBuffer = circle.radius * scale; } } /** * Creates and applies the fixtures defined in the editor. The name * parameter is used to retrieve the right fixture from the loaded file. * <br/><br/> * * The body reference point (the red cross in the tool) is by default * located at the bottom left corner of the image. This reference point * will be put right over the BodyDef position point. Therefore, you should * place this reference point carefully to let you place your body in your * world easily with its BodyDef.position point. Note that to draw an image * at the position of your body, you will need to know this reference point * (see {@link #getOrigin(java.lang.String, float)}. * <br/><br/> * * Also, saved shapes are normalized. As shown in the tool, the width of * the image is considered to be always 1 meter. Thus, you need to provide * a scale factor so the polygons get resized according to your needs (not * every body is 1 meter large in your game, I guess). * * @param body The Box2d body you want to attach the fixture to. * @param name The name of the fixture you want to loadSync. * @param uniqueIdentifier will be added to the fixture. Retrieve using fixture.getUserData() * @param fd The fixture parameters to apply to the created body fixture. */ public void attachFixture(Body body, String name, int uniqueIdentifier, FixtureDef fd) throws RuntimeException { RigidBodyModel rbModel = model.rigidBodies.get(name); if (rbModel == null) throw new RuntimeException("Name '" + name + "' was not found."); for (int i = 0, n = rbModel.polygons.size(); i < n; i++) { PolygonModel polygon = rbModel.polygons.get(i); polygonShape.set(polygon.buffer); fd.shape = polygonShape; Fixture fixture = body.createFixture(fd); // Added to support breakable objects fixture.setUserData(new Integer(uniqueIdentifier)); // Added to support breakable objects } for (int i = 0, n = rbModel.circles.size(); i < n; i++) { CircleModel circle = rbModel.circles.get(i); circleShape.setPosition(circle.centerBuffer); circleShape.setRadius(circle.radiusBuffer); fd.shape = circleShape; Fixture fixture = body.createFixture(fd); // Added to support breakable objects fixture.setUserData(new Integer(uniqueIdentifier)); // Added to support breakable objects } } /** * Gets the image path attached to the given name. */ public String getImagePath(String name) throws RuntimeException { RigidBodyModel rbModel = model.rigidBodies.get(name); if (rbModel == null) throw new RuntimeException("Name '" + name + "' was not found."); return rbModel.imagePath; } /** * Gets the origin point attached to the given name. Since the point is * normalized in [0,1] coordinates, it needs to be scaled to your body * size. Warning: this method returns the same Vector2 object each time, so * copy it if you need it for later use. */ public Vector2 getOrigin(String name, float scale) { RigidBodyModel rbModel = model.rigidBodies.get(name); if (rbModel == null) throw new RuntimeException("Name '" + name + "' was not found."); return vec.set(rbModel.origin).scl(scale); } /** * <b>For advanced users only.</b> Lets you access the internal model of * this loader and modify it. Be aware that any modification is permanent * and that you should really know what you are doing. */ public Model getInternalModel() { return model; } // ------------------------------------------------------------------------- // Json Models // ------------------------------------------------------------------------- public static class Model { public final Map<String, RigidBodyModel> rigidBodies = new HashMap<String, RigidBodyModel>(); } public static class RigidBodyModel { public String name; public String imagePath; public final Vector2 origin = new Vector2(); public final List<PolygonModel> polygons = new ArrayList<PolygonModel>(); public final List<CircleModel> circles = new ArrayList<CircleModel>(); } public static class PolygonModel { public final List<Vector2> vertices = new ArrayList<Vector2>(); private Vector2[] buffer; // used to avoid recalculation in attachFixture() } public static class CircleModel { public final Vector2 center = new Vector2(); public float radius; private Vector2 centerBuffer; // used to avoid recalculation in attachFixture() private float radiusBuffer; // used to avoid recalculation in attachFixture() } // ------------------------------------------------------------------------- // Json reading process // ------------------------------------------------------------------------- private Model readJson(String str) { Model m = new Model(); JsonValue rootElem = new JsonReader().parse(str); JsonValue bodiesElems = rootElem.get("rigidBodies"); for (int i = 0; i < bodiesElems.size; i++) { JsonValue bodyElem = bodiesElems.get(i); RigidBodyModel rbModel = readRigidBody(bodyElem); m.rigidBodies.put(rbModel.name, rbModel); } return m; } private RigidBodyModel readRigidBody(JsonValue bodyElem) { RigidBodyModel rbModel = new RigidBodyModel(); rbModel.name = (String) bodyElem.get("name").asString(); rbModel.imagePath = (String) bodyElem.get("imagePath").asString(); JsonValue originElem = bodyElem.get("origin"); rbModel.origin.x = originElem.get("x").asFloat(); rbModel.origin.y = originElem.get("y").asFloat(); // polygons JsonValue polygonsElem = bodyElem.get("polygons"); for (int i = 0; i < polygonsElem.size; i++) { PolygonModel polygon = new PolygonModel(); rbModel.polygons.add(polygon); JsonValue verticesElem = polygonsElem.get(i); for (int ii = 0; ii < verticesElem.size; ii++) { JsonValue vertexElem = verticesElem.get(ii); float x = vertexElem.get("x").asFloat(); float y = vertexElem.get("y").asFloat(); polygon.vertices.add(new Vector2(x, y)); } polygon.buffer = new Vector2[polygon.vertices.size()]; } // circles JsonValue circlesElem = bodyElem.get("circles"); for (int i = 0; i < circlesElem.size; i++) { CircleModel circle = new CircleModel(); rbModel.circles.add(circle); JsonValue circleElem = circlesElem.get(i); circle.center.x = circleElem.get("cx").asFloat(); circle.center.y = circleElem.get("cy").asFloat(); circle.radius = circleElem.get("r").asFloat(); } return rbModel; } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- private Vector2 newVec() { return vectorPool.isEmpty() ? new Vector2() : vectorPool.remove(0); } }