Jittery movement controller with physics node

i am trying to create a simple homing missile controller.

something like:


        target.updateWorldVectors();
        missile.lookAt(target.getWorldTranslation(), Vector3f.UNIT_Y);
        missile.getLocalTranslation().addLocal(missile.getLocalRotation().getRotationColumn(2).mult(time * speed));



This code works as expected if 'missile' is a simple Node.
But wen i use a DynamicsNode the movement is very jittery, it seems that the DynamicsNode always wants to fly into the same direction.
Generally it should be possible to move and rotate a DynamicsNode like this right?

I also tried to clear any dynamics on the missile just to be sure, but it didn't help.

I believe you have to drive the DynamicsNode using forces and torques, as it takes its position from the body. Doing so (and limiting the max torque for turning) will create a more believable physical behavior, too.



If you want to set the velocity and direction yourself, you don't need the simulation of DynamicsNode anyway.

yes i don't really need physics for the missile, but it makes collision detection very easy.

i'll try it with forces, but i think simulating the lookAt method with forces is not so easy.

If you don't want to abandon your dynamic physics node, then reset the forces on that node at every update. That should stop it from wanting to go in its own direciton.

If you just want collision detection you can

  1. make the node a static node and check collisions with other static each frame (see physics pick tests) (+ usgin your your lookAt)
  2. make the node a dynamic node, disable gravity for that node and clear forces each frame (+ usgin your your lookAt)



    or if you want more physical behavior (not that hard either)


  3. make the node a dynamic node, disable gravity, apply rotational forces to face the correct direction, apply acceleration force, use some strong dampening (e.g. with FrictionCallback) (not using lookAt)

i tried 2) but that didn't fix the jittery movement.



But anyway, it would be cool the have a movement with forces. That way it would be easier to evade the missile.



So i will implement 3), my missile already accelerates to a max speed with forces now.

I just need to figure out how to handle the rotation, so that it faces towards its target.  I'll play with it more after work.

The jitter may be an update problem. Do you use SimplePhysicsGame? If no, how and when do you update physics, when do you render and when do you update the scenegraph? Can you reproduce the jittering in a SimplePhysicsGame?

i am bad at math, i can’t figure it out …







I want to rotate my missile using addTorque(Vector3f), but how can i calculate the needed Vector?



I tried something like this, but its obviously wrong.


   // simulate a lookAt() to the target, q is set to the desired rotation
   Vector3f vec = target.getWorldTranslation().subtract(missile.getPhysicsNode().getWorldTranslation());
   Quaternion q = new Quaternion(missile.getPhysicsNode().getLocalRotation());
   q.lookAt(vec, Vector3f.UNIT_Y);

   // get the difference between the actual and desired direction
   Vector3f change = q.getRotationColumn(2).subtract(missile.getPhysicsNode().getLocalRotation().getRotationColumn(2));

   // now use change as input for addTorque(change) ...



could anyone help me out of my misery?  :)

The simplest approach, which still works well is this:



delta = target.location - rocket.location

localDelta = target.rotation.invert() x delta

(assuming your rocket faces into z direction, you now need to steer localDelta.x to the left and localDelta.y upwards)

torque = [localDelta.x; localDelta.y; 0] x target.rotation

rocket.torque = rocket.torque0.9 + torque100  (you need to play with the factors here to get the desired steering behavior)

i can't seem to get any useful values :frowning:

I will make a simple test case when i get home.

Well first, somehow it seems i cannot reproduce my 'jittery movement' problem, i talked about in my first post.

But anyway, my question about rotation with torque is still there.



Following is a little test to show the problem.

If you press SPACE, a missile is spawned which chases the target (the Box) and dies after a few seconds.

It works as it is now, because i use missile.lookAt(target) in the MissileMover.



But the code that is commented out (surrounded by TODO) in HomingDevice.update() does not work (and probably does not make much sense). The commented out code should replace the lookAt() call.

It would be cool to rotate the missile using torque instead of lookAt().



So if anyone wants to try and fix it, i would be most grateful :slight_smile:



@irrisor you said "localDelta = target.rotation.invert() x delta"

x means crossproduct right? but Quaternion has no cross Method which takes a Vector3f. I am not sure how to correctly get your localDelta Value.


import com.jme.bounding.BoundingBox;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.shape.Arrow;
import com.jme.scene.shape.Box;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.callback.FrictionCallback;
import com.jmex.physics.util.SimplePhysicsGame;

public class HomingMissileTest extends SimplePhysicsGame {
    private Node targetNode;

    /**
     * creates a new missile, represented by an Arrow.
     * The missile is accellerated by the HomingDevice Controller.
     */
    private void spawnMissile() {
        // create missile
        Arrow missileModel = new Arrow("arrow", 2, 0.3f);
        missileModel.setModelBound(new BoundingBox());
        missileModel.updateModelBound();
        missileModel.getLocalRotation().fromAngleAxis(90 *
                FastMath.DEG_TO_RAD, Vector3f.UNIT_X);

        DynamicPhysicsNode missileNode =
            getPhysicsSpace().createDynamicNode();
        missileNode.attachChild(missileModel);
        missileNode.generatePhysicsGeometry();
        missileNode.addController(new HomingDevice(missileNode,
                targetNode));
        // place it infront of the camera
        missileNode.setLocalTranslation(cam.getLocation().add(cam.getDirection().mult(5)));
        missileNode.getLocalRotation().fromAngleAxis(FastMath.DEG_TO_RAD * 180, Vector3f.UNIT_X);
       
        FrictionCallback fc = new FrictionCallback();
        fc.add(missileNode, 100, 80);
        getPhysicsSpace().addToUpdateCallbacks(fc);

        rootNode.attachChild(missileNode);
        rootNode.updateRenderState();
    }

    /**
     * creates a moving box as a target for our missile.
     */
    private void createTarget() {
        // create target
        Box targetModel = new Box("target", new Vector3f(-1, -1, -1),
                new Vector3f(1f, 1f, 4f));
        targetModel.setModelBound(new BoundingBox());
        targetModel.updateModelBound();

        targetNode = new Node("targetNode");
        targetNode.attachChild(targetModel);

        targetNode.addController(new TargetMover(targetNode));
        targetNode.setLocalTranslation(-30, -7, -40);

        rootNode.attachChild(targetNode);
        rootNode.updateGeometricState(0, true);
        rootNode.updateRenderState();
    }

    @Override
    protected void simpleInitGame() {
        display.setVSyncEnabled(true);
        createTarget();
        getPhysicsSpace().setDirectionalGravity(Vector3f.ZERO);
       
        input.addAction( new InputAction() {
            public void performAction( InputActionEvent evt ) {
                if ( evt.getTriggerPressed() ) {
                    spawnMissile();
                }
            }
        }, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_SPACE,
                                InputHandler.AXIS_NONE, false );
    }

    /**
     * Controller to move the missile with force and torque.
     */
    public class HomingDevice extends Controller {
        private DynamicPhysicsNode missile;
        private Node target;
        private float lifetime = 50;
        private float speed = 230;

        public HomingDevice(final DynamicPhysicsNode missile,
                            final Node target) {
            this.target = target;
            this.missile = missile;
        }

        /**
         * Alter the objects Rotation, so that it points towards
         * the Target. Move the object forward.
         *
         *  it should be something like this :
         * 
         *  delta = target.location - rocket.location
         *  localDelta = target.rotation.invert() x delta
         *  now steer localDelta.x to the left and localDelta.y upwards
         *  torque = [localDelta.x; localDelta.y; 0] x target.rotation
         *  rocket.torque = rocket.torque*0.9 + torque*100
         */
        @Override
        public void update(float time) {

            lifetime -= time;
            if (lifetime < 0) {
                missile.removeFromParent();
                missile.delete();
                return;
            }

            // add torque to rotate the missile to point towards the target
            target.updateWorldVectors();
            missile.updateWorldVectors();
           
            // TODO
            Vector3f delta = target.getWorldTranslation().subtract(missile.getWorldTranslation());
            Vector3f localDelta = missile.getLocalRotation().inverse().mult(delta);
            Vector3f torque = target.getLocalRotation().mult(new Vector3f(localDelta.x, localDelta.y, 0));
            torque.multLocal(5.5f);
            missile.addTorque(torque);
            // TODO
           
            // move the missile forward, along its direction
            float currentSpeed = missile.getLinearVelocity(null).dot(
                        missile.getPhysicsNode().getLocalRotation().getRotationColumn(2));
            float thrust = speed - currentSpeed;
            if (Math.round(thrust) > 0) {
                missile.addForce(new
                        Vector3f(missile.getLocalRotation().getRotationColumn(2).mult(thrust)));
            }
        }
    }

    /**
     * The Controller for the target.
     * Moves the Target around in a circle.
     */
    public class TargetMover extends Controller {
        private Node object;
        private float speed = 35;
        private float roationSpeed = 60;

        public TargetMover(final Node object) {
            this.object = object;
        }
        @Override
        public void update(float time) {
            object.getLocalRotation().multLocal(new Quaternion().fromAngleAxis(roationSpeed * time * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y));
            object.getLocalTranslation().addLocal(
                    object.getLocalRotation().getRotationColumn(2).mult(time * speed));
        }
    }

    public static void main(String[] args) {
        new HomingMissileTest().start();
    }
}


edit: updated with more or less working code

yep, missile not target



and the same multiplication type: quat.mult(vector)

Core-Dump said:

@irrisor you said "localDelta = target.rotation.invert() x delta"
x means crossproduct right? but Quaternion has no cross Method which takes a Vector3f. I am not sure how to correctly get your localDelat Value.

I just meant multiply with inverse rotation. In Java that'd be something like


Vector3f localDelta = rocket.getWorldRotation().inverse().mult( delta );


(where you can optimize the creation, of course)
and I meant rocket not target, sorry

ok that makes sense.



And whats with this one:

you said: "torque = [localDelta.x; localDelta.y; 0] x target.rotation"



But i cant multiply a Vector3f with a quaternion. This should also be missile instead of target?



so thats what i have for now:


            Vector3f delta = target.getWorldTranslation().subtract(missile.getWorldTranslation());
            Vector3f localDelta = missile.getLocalRotation().inverse().mult(delta);
            // TODO Vector3f torque = new Vector3f(localDelta.x, localDelta.y, 0).multtarget.getLocalRotation());
            torque.multLocal(someValue);
            missile.addTorque(torque);


irrisor said:

and the same multiplication type: quat.mult(vector)

doh right

thanks a lot irrisor, it seems to work now more or less, i just need to figure out some values.

Some of my ‘Homing Arrows’ seem to be a bit disoriented :slight_smile:



I updated the HomingMissileTest above with the (more or less) working code if anyone is interested.



homing arrows wmv



Once i understand out how it works correctly, i will post it on the physics wiki.

But that might take a while :slight_smile:

The algorithm does not compensate for existing rotational velocity. That's why your "homing arrows" are "confused" by any rotational forces (e.g. by collisions).

If you want to sort that out you need to subtract the current angular velocity from your torque with an appropriate factor.

But shouldn't the force from the collisions mostly get cleared by the friction callback with relatively high angular friction?

Some arrows rotate wildly even if they didn't collide at all.

Core-Dump said:

But shouldn't the force from the collisions mostly get cleared by the friction callback with relatively high angular friction?

If the added torque is higher...


Some arrows rotate wildly even if they didn't collide at all.

Probably the initial added torque is too high?