{
  "$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()"
}