[SOLVED] PhysicsCollisionListener.collision() not always called when CCD is enabled (native bullet)

Hi,

In my game I have small projectiles of varying speeds, and they are not as fast as bullets fired from a real gun, but much more slow and actually well visible - so it doesn’t make sense to use simple ray casts to check for collision - these projectiles need to be physics objects. Since some of them move quite fast and are quite small, I need to enable CCD - without it I get a lot of missing collisions. However when I enable CCD my implementation of PhysicsCollisionListener suddenly is not called most of the time. In this PhysicsCollisionListener I remove these projectiles from the scene, display an explosion effect, apply damage etc. - so this needs to be called. Since it is not called most of time with CCD enabled, most of the projectiles just bounce off of the targets - so this means the bullet engine is registering a collision but just does not call PhysicsCollisionListener.collide in all instances.

Is this a bug? Can this be solved somehow? I tried playing with setCcdMotionThreshold and setCcdSweptSphereRadius for both the projectiles and the targets, but to no avail.

Let me know if you need to see some of my code.

Your code looks fine from here.

Ok, here is the code where I setup the basic physics:

	bulletAppState = new BulletAppState();
    bulletAppState.setThreadingType(ThreadingType.SEQUENTIAL);
	stateManager.attach(bulletAppState);
	bulletAppState.setEnabled(true);

	PhysicsSpace physicsSpace = bulletAppState.getPhysicsSpace();
	physicsSpace.setGravity(new Vector3f()); // its a space game...
	physicsSpace.addCollisionGroupListener(new CollisionGroupListener(), CollisionGroups.EVERYTHING); // I don't have specific collision groups, but I need a CollisionGroupListener so I can ignore collisions between guns an their own projectiles
	physicsSpace.addCollisionListener(new CollisionHandler());

Here is some sample code where a space ship is created:

    // 'ship' is a imported Blender-Model; instance of Node
	float radius = ((BoundingSphere)ship.getWorldBound()).getRadius();
	CollisionShape shape = new SphereCollisionShape(radius);

	float mass = ship.getUserData(EntityConstants.mass);
	RigidBodyControl bodyControl = new RigidBodyControl(shape, mass);
	// bodyControl.setCcdMotionThreshold(0.1f);
	// bodyControl.setCcdSweptSphereRadius(0.1f);
	bodyControl.setCollisionGroup(CollisionGroups.EVERYTHING);
	bodyControl.setFriction(50f);
	bodyControl.setAngularDamping(70f);
	bodyControl.setRestitution(0f);
	ship.addControl(bodyControl);

And here I created a projectile/weaponfire:

	Node weaponFire = new Node();
		CollisionShape shape = physics.createCollisionShape(weaponFire, firingShip);

	RigidBodyControl control = new RigidBodyControl(shape, 0.0001f);
	control.setCcdMotionThreshold(10f); // tried values between 0.1f and 10f
	control.setCcdSweptSphereRadius(10f); // tried values between 0.1f and 10f
	control.setCollisionGroup(CollisionGroups.EVERYTHING);
	control.setFriction(50f);
	control.setAngularDamping(70f);
	control.setRestitution(0f);
	weaponFire.addControl(control);

	control.setLinearVelocity(weaponFireDirection.normalize().mult(weaponFireSpeed));

This should be everything relevant I guess. Let me know if you have questions or need to see some other code.

1 Like

It’s a bit thread hijacking but does the ccd work at all? Like in: Maybe your Listener is not called because there is no collision? Because that is my problem with ccd: it sometimes just didn’t work.

I see that collisions are being registered by the engine because the projectiles bounce off of the targets.

2 Likes

I did some more searching and found this thread: Collision groups, why the 1st collision is unavoidable? (Ccd incompatibility)
Could this have something to do with my issue? I also have a simple CollisionGroupListener; I tried removing it but it didn’t change anything. When I changed this listener to always return false collisions still happened (projectiles still bounced off targets) but still the PhysicsCollisionListener.collide() method is not called. So I guess this is a different problem?
I also tried switching back to jbullet as I thought that this might be a native-bullet issue, but it seems jbullet is deprecated for jme3.1, so I guess I’m stuck with native-bullet.
I can’t believe that no one has encountered this problem before. CCD is probably used by a lot of projects here, and it’s hard to imagine not using a PhysicsCollisionListener to apply damage on collissons. So these two features really need to work together, else the whole physics engine seems kinda useless to me :confused:

I made a simple test case where this behavior can be seen instantly:

CCD Code
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.BulletAppState.ThreadingType;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.system.JmeContext.Type;

public class CollisionTest extends SimpleApplication {

	public static void main(String[] args) {
		CollisionTest app = new CollisionTest();
		app.start(Type.Display);
	}

	private BulletAppState bulletAppState;
	private PhysicsSpace physicsSpace;
	private Geometry targetGeom;

	@Override
	public void simpleInitApp() {
		bulletAppState = new BulletAppState();
		bulletAppState.setEnabled(true);
		bulletAppState.setThreadingType(ThreadingType.PARALLEL);
		stateManager.attach(bulletAppState);

		physicsSpace = bulletAppState.getPhysicsSpace();
		physicsSpace.setGravity(new Vector3f());
		physicsSpace.addCollisionListener(new PhysicsCollisionListener() {
			@Override
			public void collision(PhysicsCollisionEvent event) {
				Spatial nodeA = event.getNodeA();
				Spatial nodeB = event.getNodeB();

				System.out.println("collision");

				if (targetGeom != nodeA) {
					nodeA.removeFromParent();
				} else {
					nodeB.removeFromParent();
				}
			}
		});

		float sceneZ = -4f;

		float targetSize = 1f;
		Box targetMesh = new Box(targetSize, targetSize, targetSize);
		targetGeom = new Geometry("Target Geom", targetMesh);
		targetGeom.setLocalTranslation(0f, 0f, sceneZ);
		Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
		material.setColor("Color", ColorRGBA.Yellow);
		targetGeom.setMaterial(material);
		RigidBodyControl targetBodyControl = new RigidBodyControl(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(targetSize / 2f)));
		targetBodyControl.setKinematic(true);
		targetGeom.addControl(targetBodyControl);
		rootNode.attachChild(targetGeom);
		physicsSpace.add(targetGeom);

		rootNode.addControl(new AbstractControl() {
			private float timer;

			@Override
			protected void controlUpdate(float tpf) {
				timer += tpf;

				if (timer >= 0.2f) {
					timer = 0f;

					Sphere projectileMesh = new Sphere(8, 8, 0.5f);
					Geometry projectile = new Geometry("Projectile Geom", projectileMesh);
					Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
					material.setColor("Color", ColorRGBA.Green);
					projectile.setMaterial(material);
					projectile.setLocalTranslation(-60f, 0f, sceneZ);
					RigidBodyControl projectileBodyControl = new RigidBodyControl(new SphereCollisionShape(0.1f), 0.0001f);
					projectileBodyControl.setCcdMotionThreshold(1f);
					projectileBodyControl.setCcdSweptSphereRadius(1f);
					projectileBodyControl.setFriction(50f);
					projectileBodyControl.setAngularDamping(70f);
					projectileBodyControl.setRestitution(0f);
					projectileBodyControl.setLinearVelocity(Vector3f.UNIT_X.mult(700f));
					projectile.addControl(projectileBodyControl);

					rootNode.attachChild(projectile);
					physicsSpace.add(projectile);
				}
			}

			@Override
			protected void controlRender(RenderManager rm, ViewPort vp) {

			}
		});
	}
}

You should see a yellow box in the center of the screen and green spheres coming from the left, colliding with the box. However, most spheres bounce off of the box - even though I have a PhysicsCollisionListener that removes anything that isn’t the box.

I testet this with Jme 3.1.0-stable and 3.2.1-stable, didn’t change anything.
Can anyone help me with this? Is this a bug in bullet engine, or do I need to set some other parameter or something?

1 Like

I eventually took matters into my own hands and did the whole CCD myself. Here is the Control I use:

CCD Control
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.math.Ray;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;

public class WeaponFireCCDControl extends AbstractControl {

	private final EventBus eventBus = EventBus.get();

	private final float physicsTickRate;

	public WeaponFireCCDControl(float physicsTickRate) {
		this.physicsTickRate = physicsTickRate;
	}

	@Override
	protected void controlUpdate(float tpf) {
		Node rootNode = spatial.getParent();
		RigidBodyControl bodyControl = spatial.getControl(RigidBodyControl.class);

		Vector3f linearVelocity = bodyControl.getLinearVelocity();
		float speed = linearVelocity.length();
		Vector3f direction = linearVelocity.normalize();

		Ray ray = new Ray(spatial.getLocalTranslation(), direction);
		CollisionResults results = new CollisionResults();
		rootNode.collideWith(ray, results);

		float threshold = speed / physicsTickRate;

		for (CollisionResult collisionResult : results) {
			if (collisionResult.getDistance() <= threshold) {
				spatial.setLocalTranslation(collisionResult.getContactPoint());
				bodyControl.setPhysicsLocation(collisionResult.getContactPoint());

				eventBus.fireEvent(new WeaponFireCollisionPredictedEvent(spatial, collisionResult.getGeometry().getParent()));

				setEnabled(false);
				break;
			}
		}
	}

	@Override
	protected void controlRender(RenderManager rm, ViewPort vp) {

	}
}

The WeaponFireCollisionPredictedEvent eventually triggers the same code that handles standard collisions, i.e. it removes the weaponfire from the scene, applies damage and so on. Works quite nice in my scenario. Feel free to copy and use :slight_smile:

That way you’re mixing physics collision and geometry collision but if it works for you then that should be fine. Just note that the geometry collision system adds a bit of memory overhead.