{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreickozc53ldit4gv7ur6gnorcmdzejke7vhpk6xo2kvb2usdh26dui",
"uri": "at://did:plc:dxjzgxe7cvirxkwfjr2tjspt/app.bsky.feed.post/3meesmthdnuk2"
},
"path": "/t/vehiclecontrol-not-removed-from-physicsspace-when-using-addcollisionobject/49408#post_1",
"publishedAt": "2026-02-08T18:49:44.000Z",
"site": "https://hub.jmonkeyengine.org",
"tags": [
"@sgold",
"TestFancyCar",
"@Override",
"@Override",
"@Override",
"@Override"
],
"textContent": "Hi @sgold ,\n\nI’m experimenting with `VehicleControl` and noticed a difference in behavior depending on how I add it to the `PhysicsSpace`.\n\nIf I add the vehicle like this:\n\n\n getPhysicsSpace().add(vehicle);\n\n\nthen disabling the control works as expected:\n\n\n vehicle.setEnabled(false);\n\n\nThe `VehicleControl` is automatically removed from the PhysicsSpace.\n\nHowever, if I add it like this:\n\n\n getPhysicsSpace().addCollisionObject(vehicle);\n\n\nand then disable it:\n\n\n vehicle.setEnabled(false);\n\n\nthe vehicle/rigidBody **remains** inside the PhysicsSpace and is not removed.\n\nHere is the relevant part of my test code:\n\n\n @Override\n public void onAction(String name, boolean isPressed, float tpf) {\n if (name.equals(\"ToggleVehicleEnabled\") && isPressed) {\n boolean enabled = vehicle.isEnabled();\n vehicle.setEnabled(!enabled);\n }\n }\n\n\n### **Question**\n\nIs this difference in behavior expected? Does `addCollisionObject()` bypass the enable/disable lifecycle of `PhysicsControl` objects? And if so, is the correct approach to always use `add()` for controls like `VehicleControl`?\n\nBelow is the full test class that I rewrote in a more compact and readable form. You can also find it on GitHub TestFancyCar\n\nThanks\n\nEdit:\nI’m using the latest version of Minie (9.0.3+big4)\n\n\n package jme3test.bullet;\n\n import com.jme3.app.SimpleApplication;\n import com.jme3.bounding.BoundingBox;\n import com.jme3.bullet.BulletAppState;\n import com.jme3.bullet.PhysicsSpace;\n import com.jme3.bullet.collision.shapes.CollisionShape;\n import com.jme3.bullet.control.VehicleControl;\n import com.jme3.bullet.objects.VehicleWheel;\n import com.jme3.bullet.util.CollisionShapeFactory;\n import com.jme3.input.KeyInput;\n import com.jme3.input.controls.ActionListener;\n import com.jme3.input.controls.KeyTrigger;\n import com.jme3.input.controls.Trigger;\n import com.jme3.light.DirectionalLight;\n import com.jme3.math.FastMath;\n import com.jme3.math.Matrix3f;\n import com.jme3.math.Vector3f;\n import com.jme3.renderer.queue.RenderQueue.ShadowMode;\n import com.jme3.scene.Geometry;\n import com.jme3.scene.Node;\n import com.jme3.scene.Spatial;\n\n public class TestFancyCar extends SimpleApplication implements ActionListener {\n\n private BulletAppState bulletAppState;\n private VehicleControl vehicle;\n private float steeringValue = 0;\n private float accelerationValue = 0;\n private Node carNode;\n\n private float stiffness = 120.0f;//200=f1 car\n private float compValue = 0.2f; //(lower than damp!)\n private float dampValue = 0.3f;\n private float suspensionRestLength = 0.2f;\n private float frictionSlip = 4f;\n private final float mass = 400;\n private Vector3f wheelDirection = new Vector3f(0, -1, 0);\n private Vector3f wheelAxle = new Vector3f(-1, 0, 0);\n\n public static void main(String[] args) {\n TestFancyCar app = new TestFancyCar();\n app.setPauseOnLostFocus(false);\n app.start();\n }\n\n @Override\n public void simpleInitApp() {\n bulletAppState = new BulletAppState();\n stateManager.attach(bulletAppState);\n bulletAppState.setDebugEnabled(true);\n\n configureCamera();\n\n DirectionalLight dl = new DirectionalLight();\n dl.setDirection(new Vector3f(-0.5f, -1f, -0.3f).normalizeLocal());\n rootNode.addLight(dl);\n\n PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, getPhysicsSpace());\n buildPlayer();\n setupKeys();\n }\n\n private void configureCamera() {\n flyCam.setDragToRotate(true);\n flyCam.setMoveSpeed(15f);\n\n // Adjust to near frustum to a very close amount.\n float aspect = (float) cam.getWidth() / cam.getHeight();\n cam.setFrustumPerspective(45, aspect, 0.1f, 1000);\n cam.lookAtDirection(Vector3f.UNIT_Z, Vector3f.UNIT_Y);\n }\n\n private PhysicsSpace getPhysicsSpace() {\n return bulletAppState.getPhysicsSpace();\n }\n\n private void buildPlayer() {\n\n // Load model and get chassis Geometry\n carNode = (Node) assetManager.loadModel(\"Models/Ferrari/Car.scene\");\n carNode.setShadowMode(ShadowMode.Cast);\n Geometry chassis = findGeom(carNode, \"Car\");\n\n // Create a hull collision shape for the chassis\n // CollisionShape collShape = CollisionShapeFactory.createDynamicMeshShape(chassis);\n CollisionShape collShape = CollisionShapeFactory.createBoxShape(chassis);\n\n // Create a vehicle control\n vehicle = new VehicleControl(collShape, mass);\n carNode.addControl(vehicle);\n\n // Setting default values for wheels\n vehicle.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));\n vehicle.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));\n vehicle.setSuspensionStiffness(stiffness);\n vehicle.setMaxSuspensionForce(10000);\n\n // Create four wheels and add them at their locations.\n // Note that our fancy car actually goes backward.\n addWheel(vehicle, findGeom(carNode, \"WheelFrontRight\"), true);\n addWheel(vehicle, findGeom(carNode, \"WheelFrontLeft\"), true);\n addWheel(vehicle, findGeom(carNode, \"WheelBackRight\"), false);\n addWheel(vehicle, findGeom(carNode, \"WheelBackLeft\"), false);\n\n // Apply friction to rear wheels\n vehicle.getWheel(2).setFrictionSlip(frictionSlip);\n vehicle.getWheel(3).setFrictionSlip(frictionSlip);\n\n rootNode.attachChild(carNode);\n getPhysicsSpace().add(vehicle);\n //getPhysicsSpace().addCollisionObject(vehicle); <----\n }\n\n private VehicleWheel addWheel(VehicleControl vehicle, Geometry wheel, boolean isFrontWheel) {\n wheel.center();\n BoundingBox box = (BoundingBox) wheel.getModelBound();\n float wheelRadius = box.getYExtent();\n float k = (isFrontWheel) ? 1.9f : 1.7f;\n float h = (wheelRadius * k) - 1f;\n Vector3f connectionPoint = box.getCenter().add(0, -h, 0);\n return vehicle.addWheel(wheel.getParent(), connectionPoint,\n wheelDirection, wheelAxle, suspensionRestLength, wheelRadius, isFrontWheel);\n }\n\n private Geometry findGeom(Spatial spatial, String name) {\n if (spatial instanceof Node) {\n for (Spatial child : ((Node) spatial).getChildren()) {\n Geometry result = findGeom(child, name);\n if (result != null) {\n return result;\n }\n }\n } else if (spatial instanceof Geometry) {\n if (spatial.getName().startsWith(name)) {\n return (Geometry) spatial;\n }\n }\n return null;\n }\n\n private void setupKeys() {\n addMapping(\"ToggleVehicleEnabled\", new KeyTrigger(KeyInput.KEY_P));\n addMapping(\"Lefts\", new KeyTrigger(KeyInput.KEY_H));\n addMapping(\"Rights\", new KeyTrigger(KeyInput.KEY_K));\n addMapping(\"Ups\", new KeyTrigger(KeyInput.KEY_U));\n addMapping(\"Downs\", new KeyTrigger(KeyInput.KEY_J));\n addMapping(\"Reset\", new KeyTrigger(KeyInput.KEY_RETURN));\n }\n\n private void addMapping(String mappingName, Trigger... triggers) {\n inputManager.addMapping(mappingName, triggers);\n inputManager.addListener(this, mappingName);\n }\n\n @Override\n public void onAction(String name, boolean isPressed, float tpf) {\n if (name.equals(\"ToggleVehicleEnabled\") && isPressed) {\n boolean enabled = vehicle.isEnabled();\n vehicle.setEnabled(!enabled);\n }\n\n if (name.equals(\"Lefts\")) {\n if (isPressed) {\n steeringValue += .5f;\n } else {\n steeringValue -= .5f;\n }\n vehicle.steer(steeringValue);\n } else if (name.equals(\"Rights\")) {\n if (isPressed) {\n steeringValue -= .5f;\n } else {\n steeringValue += .5f;\n }\n vehicle.steer(steeringValue);\n } // Note that our fancy car actually goes backward.\n else if (name.equals(\"Ups\")) {\n if (isPressed) {\n accelerationValue -= 800;\n } else {\n accelerationValue += 800;\n }\n vehicle.accelerate(accelerationValue);\n } else if (name.equals(\"Downs\")) {\n if (isPressed) {\n vehicle.brake(40f);\n } else {\n vehicle.brake(0f);\n }\n } else if (name.equals(\"Reset\")) {\n if (isPressed) {\n System.out.println(\"Reset\");\n vehicle.setPhysicsLocation(Vector3f.ZERO);\n vehicle.setPhysicsRotation(new Matrix3f());\n vehicle.setLinearVelocity(Vector3f.ZERO);\n vehicle.setAngularVelocity(Vector3f.ZERO);\n vehicle.resetSuspension();\n }\n }\n }\n\n @Override\n public void simpleUpdate(float tpf) {\n cam.lookAt(carNode.getWorldTranslation(), Vector3f.UNIT_Y);\n }\n }\n",
"title": "VehicleControl not removed from PhysicsSpace when using addCollisionObject()"
}