1
\$\begingroup\$

In my game, I've been trying to use the physics body editor for libgdx, but I kept on getting an error. So I thought there was probably something wrong with my code so I took the example off of the website (http://www.aurelienribon.com/blog/projects/physics-body-editor/), and tried running that. But I got the same exact error on the same line of code.

The line of code from the website's example that's creating the error is

BodyEditorLoader loader = new BodyEditorLoader(Gdx.files.internal("data/test.json"));

and the error is

Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: java.lang.NoSuchMethodError: com.badlogic.gdx.utils.JsonReader.parse(Ljava/lang/String;)Ljava/lang/Object; at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:118) Caused by: java.lang.NoSuchMethodError: com.badlogic.gdx.utils.JsonReader.parse(Ljava/lang/String;)Ljava/lang/Object; at aurelienribon.bodyeditor.BodyEditorLoader.readJson(BodyEditorLoader.java:179) at aurelienribon.bodyeditor.BodyEditorLoader.(BodyEditorLoader.java:41) at com.Jon.App.App.createBottle(App.java:129) at com.Jon.App.App.create(App.java:76) at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:132) at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:112)

Any ideas on how to fix this?

\$\endgroup\$
0
\$\begingroup\$

Apparently the JSON API has changed recently, but there is a maintenance problem with the physics body editor.

More details at https://code.google.com/p/libgdx/issues/detail?id=1418.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

The problem is that, with newer versions of libGDX, their API's have changed. That is why you get the errors like java.lang.NoSuchMethodError.

The solution is to use an updated version of the BodyEditorLoader class. The original author, Aurelien Ribon, did not have enough time to update and replace the repository; others have made the changes.

This code should work with libGDX version 1.9.5. Just reference this class when you want to use the BodyEditorLoader, like you would with any other class.


Here is an updated version of the BodyEditorLoader class. Minor revisions include the new readJson method and replacing mul(scale) is changed to with scl(scale). You can copy paste this code, or find it at it's source, on GitHub.

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.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.JsonReader;
import com.badlogic.gdx.utils.JsonValue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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
// -------------------------------------------------------------------------

/**
 * 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 load.
 * @param fd The fixture parameters to apply to the created body fixture.
 * @param scale The desired scale of the body. The default width is 1.
 */
public void attachFixture(Body body, String name, FixtureDef fd, float scale)    {
    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] = newVec().set(polygon.vertices.get(ii)).scl(scale);
            vertices[ii].sub(origin);
        }

        polygonShape.set(vertices);
        fd.shape = polygonShape;
        body.createFixture(fd);

        for (int ii=0, nn=vertices.length; ii<nn; ii++) {
            free(vertices[ii]);
        }
    }

    for (int i=0, n=rbModel.circles.size(); i<n; i++) {
        CircleModel circle = rbModel.circles.get(i);
        Vector2 center = newVec().set(circle.center).scl(scale);
        float radius = circle.radius * scale;

        circleShape.setPosition(center);
        circleShape.setRadius(radius);
        fd.shape = circleShape;
        body.createFixture(fd);

        free(center);
    }
}

/**
 * Gets the image path attached to the given name.
 */
public String getImagePath(String name) {
    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 allocation in attachFixture()
}

public static class CircleModel {
    public final Vector2 center = new Vector2();
    public float radius;
}

// -------------------------------------------------------------------------
// Json reading process
// -------------------------------------------------------------------------

private Model readJson(String str) {
    Model m = new Model();

    JsonValue map = new JsonReader().parse(str);

    JsonValue bodyElem = map.getChild("rigidBodies");
    for (; bodyElem != null; bodyElem = bodyElem.next()) {
        RigidBodyModel rbModel = readRigidBody(bodyElem);
        m.rigidBodies.put(rbModel.name, rbModel);
    }

    return m;
}

private RigidBodyModel readRigidBody(JsonValue bodyElem) {
    RigidBodyModel rbModel = new RigidBodyModel();
    rbModel.name = bodyElem.getString("name");
    rbModel.imagePath = bodyElem.getString("imagePath");

    JsonValue originElem = bodyElem.get("origin");
    rbModel.origin.x = originElem.getFloat("x");
    rbModel.origin.y = originElem.getFloat("y");

    // polygons
    JsonValue polygonsElem = bodyElem.getChild("polygons"); 
    for (; polygonsElem != null ;polygonsElem = polygonsElem.next()){

        PolygonModel polygon = new PolygonModel();
        rbModel.polygons.add(polygon);

        JsonValue vertexElem = polygonsElem.child();
        for (; vertexElem != null; vertexElem = vertexElem.next()) {
            float x = vertexElem.getFloat("x");
            float y = vertexElem.getFloat("y");
            polygon.vertices.add(new Vector2(x, y));
        }

        polygon.buffer = new Vector2[polygon.vertices.size()];

    }

    // circles
    JsonValue circleElem = bodyElem.getChild("circles");

    for (; circleElem != null; circleElem = circleElem.next()) {
        CircleModel circle = new CircleModel();
        rbModel.circles.add(circle);

        circle.center.x = circleElem.getFloat("cx");
        circle.center.y = circleElem.getFloat("cy");
        circle.radius = circleElem.getFloat("r");
    }

    return rbModel;
}

// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------

private Vector2 newVec() {
    return vectorPool.isEmpty() ? new Vector2() : vectorPool.remove(0);
}

private void free(Vector2 v) {
    vectorPool.add(v);
}
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Sorry you are right. If I edit my answer with an explanation of what problems it solves precisely can you undo my answer as low quality? I mistakenly thought it was better to post the link because the amount of code in the class (which is where the link points to) is quit long.What do you think, shall I post the code in the answer? The answer is still relevant today. \$\endgroup\$ – Timothy Feb 9 '17 at 9:16
  • \$\begingroup\$ I changed it. Now it is specific enough I hope, pls vote up if you think so to \$\endgroup\$ – Timothy Feb 9 '17 at 11:20
  • \$\begingroup\$ Awesome. Just remember to source the original post, as I have included. You can still use links (and should, if it helps); provided your answer stands on its own legs without the link :) \$\endgroup\$ – Gnemlock Feb 9 '17 at 11:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.