[Solved] Just upgraded from 3.2.2 to 3.3.2 stable and I have a problem with physics listeners

I have a PhysicsCollisionListener on each component of my ship builder. I relied on the collision() method being called on every physics tick if the components where attached but now it seems it’s only called on initial contact. This is a problem because I find out if the components are not attached by checking if the listener was not called within a one second interval. Why did we change the way the PhysicsCollisionListener is called? This is using native bullet by the way.

2 Likes

I’m not really a bullet user but I took a quick look at the diff between 3.2.2 and 3.3.2 for the bullet modules and the only related thing I see is support added for collision flags and a native method implementation added for isNonMoving().

git diff v3.2.2-stable…v3.3.2-stable jme3-bullet
git diff v3.2.2-stable…v3.3.2-stable jme3-bullet-native

Maybe you will spot something relevant that I didn’t.

1 Like

This might be related to PR 1031 which was integrated into JME 3.2.3 in order to fix issue 1029.

1 Like

Here is a test program that displays the problem. Notice once the contact point is triggered the message said “colliding” and then a split second later switches from “colliding” to “Not colliding”.

/*
 * Copyright (c) 2009-2019 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.bullet;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.PhysicsSpace.BroadphaseType;
import com.jme3.bullet.debug.BulletDebugAppState;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * An app state to manage a single Bullet physics space.
 * <p>
 * This class is shared between JBullet and Native Bullet.
 *
 * @author normenhansen
 */
public class BulletAppState
        extends AbstractAppState
        implements PhysicsTickListener {

    /**
     * true if-and-only-if the physics simulation is running (started but not
     * yet stopped)
     */
    protected volatile boolean isRunning = false;
    protected Application app;
    /**
     * manager that manages this state, set during attach
     */
    protected AppStateManager stateManager;
    /**
     * executor service for physics tasks, or null if parallel simulation is not
     * running
     */
    protected ScheduledThreadPoolExecutor executor;
    /**
     * physics space managed by this state, or null if no simulation running
     */
    protected PhysicsSpace pSpace;
    /**
     * threading mode to use (not null)
     */
    protected ThreadingType threadingType = ThreadingType.SEQUENTIAL;
    /**
     * broadphase collision-detection algorithm for the physics space to use
     * (not null)
     */
    protected BroadphaseType broadphaseType = BroadphaseType.DBVT;
    /**
     * minimum coordinate values for the physics space when using AXIS_SWEEP
     * broadphase algorithms (not null)
     */
    protected Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f);
    /**
     * maximum coordinate values for the physics space when using AXIS_SWEEP
     * broadphase algorithms (not null)
     */
    protected Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
    /**
     * simulation speed multiplier (default=1, paused=0)
     */
    protected float speed = 1;
    /**
     * true if-and-only-if this state is enabled
     */
    protected boolean active = true;
    /**
     * true if-and-only-if debug visualization is enabled
     */
    protected boolean debugEnabled = false;
    /**
     * app state to manage the debug visualization, or null if none
     */
    protected BulletDebugAppState debugAppState;
    /**
     * time interval between frames (in seconds) from the most recent update
     */
    protected float tpf;
    /**
     * current physics task, or null if none
     */
    protected Future physicsFuture;

    /**
     * Instantiate an app state to manage a new PhysicsSpace with DBVT collision
     * detection.
     * <p>
     * Use getStateManager().addState(bulletAppState) to start physics.
     */
    public BulletAppState() {
    }

    /**
     * Instantiate an app state to manage a new PhysicsSpace.
     * <p>
     * Use getStateManager().addState(bulletAppState) to start physics.
     *
     * @param broadphaseType which broadphase collision-detection algorithm to
     * use (not null)
     */
    public BulletAppState(BroadphaseType broadphaseType) {
        this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
    }

    /**
     * Instantiate an app state to manage a new PhysicsSpace with AXIS_SWEEP_3
     * collision detection.
     * <p>
     * Use getStateManager().addState(bulletAppState) to start physics.
     *
     * @param worldMin the desired minimum coordinate values (not null,
     * unaffected, default=-10k,-10k,-10k)
     * @param worldMax the desired maximum coordinate values (not null,
     * unaffected, default=10k,10k,10k)
     */
    public BulletAppState(Vector3f worldMin, Vector3f worldMax) {
        this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
    }

    /**
     * Instantiate an app state to manage a new PhysicsSpace.
     * <p>
     * Use getStateManager().addState(bulletAppState) to enable physics.
     *
     * @param worldMin the desired minimum coordinate values (not null,
     * unaffected, default=-10k,-10k,-10k)
     * @param worldMax the desired maximum coordinate values (not null,
     * unaffected, default=10k,10k,10k)
     * @param broadphaseType which broadphase collision-detection algorithm to
     * use (not null)
     */
    public BulletAppState(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) {
        this.worldMin.set(worldMin);
        this.worldMax.set(worldMax);
        this.broadphaseType = broadphaseType;
    }

    /**
     * Allocate the physics space and start physics for ThreadingType.PARALLEL.
     *
     * @return true if successful, otherwise false
     */
    private boolean startPhysicsOnExecutor() {
        if (executor != null) {
            executor.shutdown();
        }
        executor = new ScheduledThreadPoolExecutor(1);
        final BulletAppState app = this;
        Callable<Boolean> call = new Callable<Boolean>() {
            public Boolean call() throws Exception {
                detachedPhysicsLastUpdate = System.currentTimeMillis();
                pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType);
                pSpace.addTickListener(app);
                return true;
            }
        };
        try {
            return executor.submit(call).get();
        } catch (InterruptedException ex) {
            Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        } catch (ExecutionException ex) {
            Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    private Callable<Boolean> parallelPhysicsUpdate = new Callable<Boolean>() {
        public Boolean call() throws Exception {
            pSpace.update(tpf * getSpeed());
            return true;
        }
    };
    long detachedPhysicsLastUpdate = 0;
    private Callable<Boolean> detachedPhysicsUpdate = new Callable<Boolean>() {
        public Boolean call() throws Exception {
            pSpace.update(getPhysicsSpace().getAccuracy() * getSpeed());
            pSpace.distributeEvents();
            long update = System.currentTimeMillis() - detachedPhysicsLastUpdate;
            detachedPhysicsLastUpdate = System.currentTimeMillis();
            executor.schedule(detachedPhysicsUpdate, Math.round(getPhysicsSpace().getAccuracy() * 1000000.0f) - (update * 1000), TimeUnit.MICROSECONDS);
            return true;
        }
    };

    /**
     * Access the PhysicsSpace managed by this state. Normally there is none
     * until the state is attached.
     *
     * @return the pre-existing instance, or null if no simulation running
     */
    public PhysicsSpace getPhysicsSpace() {
        return pSpace;
    }

    /**
     * Allocate a physics space and start physics.
     * <p>
     * Physics starts automatically after the state is attached. To start it
     * sooner, invoke this method.
     */
    public void startPhysics() {
        if (isRunning) {
            return;
        }

        switch (threadingType) {
            case PARALLEL:
                boolean success = startPhysicsOnExecutor();
                assert success;
                assert pSpace != null;
                break;

            case SEQUENTIAL:
                pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType);
                pSpace.addTickListener(this);
                break;

            default:
                throw new IllegalStateException(threadingType.toString());
        }

        isRunning = true;
    }

    /**
     * Stop physics after this state is detached.
     */
    public void stopPhysics() {
        if (!isRunning) {
            return;
        }
        if (executor != null) {
            executor.shutdown();
            executor = null;
        }
        pSpace.removeTickListener(this);
        pSpace.destroy();
        isRunning = false;
    }

    /**
     * Initialize this state prior to its 1st update. Should be invoked only by
     * a subclass or by the AppStateManager.
     *
     * @param stateManager the manager for this state (not null)
     * @param app the application which owns this state (not null)
     */
    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        this.app = app;
        this.stateManager = stateManager;
        startPhysics();
    }

    /**
     * Alter whether debug visualization is enabled.
     *
     * @param debugEnabled true &rarr; enable, false &rarr; disable
     */
    public void setDebugEnabled(boolean debugEnabled) {
        this.debugEnabled = debugEnabled;
    }


    /**
     * Test whether debug visualization is enabled.
     *
     * @return true if enabled, otherwise false
     */
    public boolean isDebugEnabled() {
        return debugEnabled;
    }

    /**
     * Transition this state from detached to initializing. Should be invoked
     * only by a subclass or by the AppStateManager.
     *
     * @param stateManager (not null)
     */
    @Override
    public void stateAttached(AppStateManager stateManager) {
        super.stateAttached(stateManager);
        if (!isRunning) {
            startPhysics();
        }
        if (threadingType == ThreadingType.PARALLEL) {
            PhysicsSpace.setLocalThreadPhysicsSpace(pSpace);
        }
        if (debugEnabled) {
            debugAppState = new BulletDebugAppState(pSpace);
            stateManager.attach(debugAppState);
        }
    }

    /**
     * Update this state prior to rendering. Should be invoked only by a
     * subclass or by the AppStateManager. Invoked once per frame, provided the
     * state is attached and enabled.
     *
     * @param tpf the time interval between frames (in seconds, &ge;0)
     */
    @Override
    public void update(float tpf) {
        super.update(tpf);
        if (debugEnabled && debugAppState == null && pSpace != null) {
            debugAppState = new BulletDebugAppState(pSpace);
            stateManager.attach(debugAppState);
        } else if (!debugEnabled && debugAppState != null) {
            stateManager.detach(debugAppState);
            debugAppState = null;
        }
        if (!active) {
            return;
        }
        pSpace.distributeEvents();
        this.tpf = tpf;
    }

    /**
     * Render this state. Should be invoked only by a subclass or by the
     * AppStateManager. Invoked once per frame, provided the state is attached
     * and enabled.
     *
     * @param rm the render manager (not null)
     */
    @Override
    public void render(RenderManager rm) {
        super.render(rm);
        if (!active) {
            return;
        }
        if (threadingType == ThreadingType.PARALLEL) {
            physicsFuture = executor.submit(parallelPhysicsUpdate);
        } else if (threadingType == ThreadingType.SEQUENTIAL) {
            pSpace.update(active ? tpf * speed : 0);
        } else {
        }
    }

    /**
     * Update this state after all rendering commands are flushed. Should be
     * invoked only by a subclass or by the AppStateManager. Invoked once per
     * frame, provided the state is attached and enabled.
     */
    @Override
    public void postRender() {
        super.postRender();
        if (physicsFuture != null) {
            try {
                physicsFuture.get();
                physicsFuture = null;
            } catch (InterruptedException ex) {
                Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ExecutionException ex) {
                Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Transition this state from terminating to detached. Should be invoked
     * only by a subclass or by the AppStateManager. Invoked once for each time
     * {@link #initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application)}
     * is invoked.
     */
    @Override
    public void cleanup() {
        if (debugAppState != null) {
            stateManager.detach(debugAppState);
            debugAppState = null;
        }
        stopPhysics();
        super.cleanup();
    }

    /**
     * Read which type of threading this app state uses.
     *
     * @return the threadingType (not null)
     */
    public ThreadingType getThreadingType() {
        return threadingType;
    }

    /**
     * Alter which type of threading this app state uses. Not allowed after
     * attaching the app state.
     *
     * @param threadingType the desired type (not null, default=SEQUENTIAL)
     */
    public void setThreadingType(ThreadingType threadingType) {
        this.threadingType = threadingType;
    }

    /**
     * Alter the broadphase type the physics space will use. Not allowed after
     * attaching the app state.
     *
     * @param broadphaseType an enum value (not null, default=DBVT)
     */
    public void setBroadphaseType(BroadphaseType broadphaseType) {
        this.broadphaseType = broadphaseType;
    }

    /**
     * Alter the coordinate range. Not allowed after attaching the app state.
     *
     * @param worldMin the desired minimum coordinate values when using
     * AXIS_SWEEP broadphase algorithms (not null, alias created,
     * default=-10k,-10k,-10k)
     */
    public void setWorldMin(Vector3f worldMin) {
        this.worldMin = worldMin;
    }

    /**
     * Alter the coordinate range. Not allowed after attaching the app state.
     *
     * @param worldMax the desired maximum coordinate values when using
     * AXIS_SWEEP broadphase algorithms (not null, alias created,
     * default=10k,10k,10k)
     */
    public void setWorldMax(Vector3f worldMax) {
        this.worldMax = worldMax;
    }

    /**
     * Read the simulation speed.
     *
     * @return speed (&ge;0, default=1)
     */
    public float getSpeed() {
        return speed;
    }

    /**
     * Alter the simulation speed.
     *
     * @param speed the desired speed (&ge;0, default=1)
     */
    public void setSpeed(float speed) {
        this.speed = speed;
    }
    /**
     * Callback from Bullet, invoked just before the physics is stepped. A good
     * time to clear/apply forces.
     *
     * @param space the space that is about to be stepped (not null)
     * @param f the time per physics step (in seconds, &ge;0)
     */
    public void prePhysicsTick(PhysicsSpace space, float f) {
    }

    /**
     * Callback from Bullet, invoked just after the physics is stepped. A good
     * time to clear/apply forces.
     *
     * @param space the space that is about to be stepped (not null)
     * @param f the time per physics step (in seconds, &ge;0)
     */
    public void physicsTick(PhysicsSpace space, float f) {
    }

    /**
     * Enumerate threading modes.
     */
    public enum ThreadingType {

        /**
         * Default mode: user update, physics update, and rendering happen
         * sequentially. (single threaded)
         */
        SEQUENTIAL,
        /**
         * Parallel threaded mode: physics update and rendering are executed in
         * parallel, update order is maintained.
         */
        PARALLEL,
    }
}
1 Like

Did you mean to attach a test application or just attach BulletAppState?

1 Like

Oops wrong file. ** brain fart **. This test app was created originally by @sgold while he helped me with the design pattern to identify when a part is detached.

package banana;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.HullCollisionShape;
import com.jme3.bullet.control.GhostControl;
import com.jme3.font.BitmapText;
import com.jme3.math.FastMath;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;

/**
 * Test collision detection: 2 ghost controls and hull shapes.
 *
 * @author Stephen Gold sgold@sonic.net
 */
public class GhostHull extends SimpleApplication implements PhysicsCollisionListener, PhysicsTickListener
{

	private BitmapText statusText;

	private volatile boolean collisionFlag = false;

	final private float size = 5f;

	private GhostControl fixedGhost;

	private GhostControl movingGhost;

	private volatile int numOverlaps;

	final private Node fixedNode = new Node("fixed");

	final private Node movingNode = new Node("moving");

	private PhysicsSpace physicsSpace;

	public static void main(String[] arguments)
	{
		new GhostHull().start();
	}

	@Override
	public void simpleInitApp()
	{
		configureCamera();
		configurePhysics();

		guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
		statusText = new BitmapText(guiFont, false);
		guiNode.attachChild(statusText);
		statusText.move(0f, cam.getHeight() - statusText.getHeight(), 0f);

		float[] cubeVertices = new float[]
		{ +size, +size, +size, +size, +size, -size, +size, -size, +size, +size, -size, -size, -size, +size, +size, -size, +size, -size, -size, -size, +size,
				-size, -size, -size };
		HullCollisionShape cube = new HullCollisionShape(cubeVertices);
		cube.setMargin(0.001f);

		rootNode.attachChild(fixedNode);
		fixedGhost = new GhostControl(cube);
		fixedNode.addControl(fixedGhost);
		fixedNode.rotate(0f, 0f, FastMath.QUARTER_PI);
		physicsSpace.add(fixedNode);

		rootNode.attachChild(movingNode);
		movingGhost = new GhostControl(cube);
		movingNode.addControl(movingGhost);
		physicsSpace.add(movingNode);
	}

	@Override
	public void simpleUpdate(float tpf)
	{
		String text;
		if(collisionFlag && (System.nanoTime() - collisionFired < 100000000))
		{
			text = "colliding, ";
		} else
		{
			text = "not colliding, ";
		}
		if(numOverlaps > 0)
		{
			text += "overlapping";
		} else
		{
			text += "not overlapping";
		}
		statusText.setText(text);

		Vector2f cursorPos = inputManager.getCursorPosition();
		Vector3f location = cam.getWorldCoordinates(cursorPos, 0f);
		location.z = 0f;
		movingNode.setLocalTranslation(location);
	}

	private volatile long collisionFired = 0;

	@Override
	public void collision(PhysicsCollisionEvent event)
	{
		// if(event.getObjectA() != movingGhost && event.getObjectB() !=
		// movingGhost)
		// collisionFlag = false;
		// else
		collisionFlag = true;

		collisionFired = System.nanoTime();
	}

	@Override
	public void physicsTick(PhysicsSpace space, float timeStep)
	{
		// do nothing
	}

	@Override
	public void prePhysicsTick(PhysicsSpace space, float timeStep)
	{
		numOverlaps = fixedGhost.getOverlappingObjects().size() + movingGhost.getOverlappingObjects().size();

	}

	private void configureCamera()
	{
		flyCam.setEnabled(false);
		cam.setParallelProjection(true);
		cam.setFrustumTop(5f * size);
		cam.setFrustumBottom(-5f * size);
	}

	private void configurePhysics()
	{
		BulletAppState bulletAppState = new BulletAppState();
		stateManager.attach(bulletAppState);
		bulletAppState.setDebugEnabled(true);

		physicsSpace = bulletAppState.getPhysicsSpace();
		physicsSpace.addCollisionListener(this);
		physicsSpace.addTickListener(this);
	}
}
1 Like

This change is a show stopper for myself and possibly for other people. For example now that collision events do not fire after the first collision there is no way to allow an object to overlap another and identify the level of overlay unless you are using broad phase which is a useless aabb box it you want accuracy. Is there any reason why we decided to, in my view, cripple the physics library?

1 Like

@sgold after looking at it closer, it definitely looks like the code change for that PR broke the way collision events work. Now they are only fired on gContactStartedCallback instead of gContactProcessedCallback. Basically only ONE event fires on initial collision and no updated future events fire even when collision continues after the first event. Can this be fixed please? Either ensure events continue to fire on gContactProcessedCallback or give us another method in the PhysicsCollisionListener that we can use for gContactProcessedCallback events. Basically this change broke my entire game.

2 Likes

I can’t speak for @sgold personally, but it’s entirely possible that sometimes mistakes are made. I wish we had the ability to rigorously test each change made; we can only do our best. Sometimes issues like this slip through.

Having said that it doesn’t happen very often at all - which is a testament to those that take the time to review changes.

4 Likes

I apologize to everyone if I came across harsh in any way. I was just having a “holly cow” moment when I saw the ramifications of the change through my code base. After calming down and thinking about the best path forward I recommend we have both an initialCollision method and the normal collision method in our listener. I can see use cases for both.

3 Likes

3 Likes

The change to the semantics of collision listeners was unintentional. If I’d known about it, I wouldn’t have integrated it into JME 3.2.3.

I’m thinking about how to proceed. While I feel responsible for the situation, I have scant interest in maintaining jme3-bullet at this point, let alone adding features.

Adding an interface (such as you propose) to Minie 2.0 has a lot more appeal; might that work for you?

Also, have you considered the following solutions?

  • stick with JME 3.2.2
  • use jme3-jbullet instead of jme3-bullet
1 Like

Sticking with JME 3.2.2 would effectively End of Life JME for me. Nobody would do that. In order to switch to jbullet I would have to see what I am loosing in performance and features … not a trivial set of performance and regression tests.

Now, if you are stating that we will not be supporting bullet in the future, then that is a totally new matter all together. At that point, I would have to look for alternatives. Minie possible being one of them.

Your comment was vague thought. Are you essentially stating that this bug will not be fixed and I should look elsewhere for a physics solution? Fixing it is literally a few lines of code and a re-build.

1 Like

As I said, “I’m thinking about how to proceed.” I wasn’t making official pronouncements about the future of JMonkeyEngine.

No good will come of rushing a fix. The fix you propose can’t go into JME 3.3.3, so it’s hard to predict when it might appear in a release. We haven’t even identified a release manager for JME 3.4. So it seems worth taking some time to come up with the best possible solution.

For one thing, reverting the change or adding an 2nd interface would probably mean re-opening issue 1029.

1 Like

Understood and holding off does make sense. I would have not moved forward with 3.3.2 at this time but alas I use all of @pspeed’s libraries and this was a good time to take the leap and upgrade on mass. Since I feel like you will be focusing on Minie more in the future (It does seem to be your passion and I don’t blame you), maybe this is a good time for me to look into Minie :slight_smile: I am all for conformance to long term supported and upgraded assets… even though I still use cvs as a repository locally lol

1 Like

I’m building the physics in MyWorld entirely on Minie and have had no issues whatsoever yet. Granted, physics in MyWorld is still in its infancy, but @sgold has put a great deal of effort and dedication into Minie and it really shows.

2 Likes

I already removed the bullet libraries from my build and started looking into Minie :slight_smile: I don’t mind that it’s not android compatible because my physics is 100% server authoritative.

3 Likes

Minie has included Android support since v1.6 (April). Unfortunately, it has had the (flawed) fix for issue 1029 even longer, so switching to Minie won’t solve your issue (at this time).

The good news is that Minie is on a much faster release cycle than JMonkeyEngine, so the changes you want can be integrated and released sooner than they would be in jme3-bullet.

2 Likes

@sgold actually I should have looked at Minie closer a while back. This 1029 issue is a blessing in disguise for me. After looking at what you are doing in Minie there are definately some very interesting features that I can use in my game that will never make it into native bullet. As for the short term, I assume I can temporarely switch to jbullet to continue development and then switch to Minie when you get around to looking at 1029. Thankfully my code is properly modular in design and it allows me to go down this path.

Thank you for all you have done for JMonkey and all the work you have put into Minie. It looks VERY promising!

3 Likes

I think can also use the older jme3 bullet with the newer “rest of JME”.

…but it could depend on when the app state interface was changed.

2 Likes