Spaceship Thrust Movement – Rotation

Apologies if this is on the wrong board

Hello,

I’m aiming to acheive a simplistic spaceship thust movement. Similar to Elite, Freelancer, X3, that kind of thing. Probably newtonian and just with keyboard input for now. I guess the most important aspect to mention is lack of friction, once you start moving along a vector, you’ll keep moving until a force affects it.

I realise this subject is exhaustively discussed on this forum and Google (I think I’ve read most of it now) but I’d like some specific advice from some kindly soul on how this should be best accomplished with JMonkeyEngine, using its built-in functions appropriately, rather than avoiding them (which one resource suggested).

Ive already setup a basic scene that displays a shuttlecraft-esque model. I’ve applied a RigidBody control to it, setup the bullet phsyics space, set global gravity to zero - all stuff I’ve learned from the tutorials and other sources.

I’ve got key input working via an Analog listener - rotation of the vessel is currently handled thusly:

[java]if(name.equals(“Up”)) { ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(1, 0, 0)); }
if(name.equals(“Down”)) { ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(-1, 0, 0)); }
if(name.equals(“Left”)) { ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(0, 1, 0)); }
if(name.equals(“Right”)) { ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(0, -1, 0)); }[/java]

Forward / backward thrust is:

[java]… ship01.getControl(RigidBodyControl.class).applyImpulse(new Vector3f(0, 0, 1), new Vector3f(0, 0, 0)); …
… ship01.getControl(RigidBodyControl.class).applyImpulse(new Vector3f(0, 0, -1), new Vector3f(0, 0, 0)); …[/java]

This is great, except that I realise I’m applying these forces on a world-position basis; i.e when i rotate the vessel right and then rotate up - the up rotation is not correct respective to the orientation of the vessel - it still rotates it based on world axis. Hope that makes sense.

I think what I should be doing is some sort of conversion to local coords/rotations and applying an impulse with the resulting vector.

However I’m not clear on how to do that conversion, or where to do it. Or even if I’m right.

I read in the documentation somewhere aboutApplyPhysicsLocal() and have attempted to use it:

ship01.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);

It doesnt seem to have any affect though. I’m starting to read about ‘Pivot nodes’ now - perhaps that is the way to go?

Could someone point me in the right direction?

Thanks and um…hello everyone :smiley:
Dave

The full class is here:

[java]public class Game extends SimpleApplication {

private Spatial ship01;
private BulletAppState bulletAppState;

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

@Override
public void simpleInitApp() {

	bulletAppState = new BulletAppState();
	stateManager.attach(bulletAppState);

	ship01 = assetManager.loadModel("Models/spaceframe-01.obj");
	Material mat_default = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
	mat_default.setTexture("ColorMap", assetManager.loadTexture("Textures/Spaceframes/0001.jpg"));

	ship01.setMaterial(mat_default);
	rootNode.attachChild(ship01);

	DirectionalLight sun = new DirectionalLight();
	sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
	rootNode.addLight(sun);

	CollisionShape colShape = CollisionShapeFactory.createDynamicMeshShape(ship01);
	ship01.addControl(new RigidBodyControl(colShape, 1.0f));
	ship01.getControl(RigidBodyControl.class).setApplyPhysicsLocal(true);

	bulletAppState.getPhysicsSpace().add(ship01);
	bulletAppState.getPhysicsSpace().setGravity(Vector3f.ZERO);
	initInput();
}

private void initInput() {

	inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_UP));
	inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_DOWN));
	inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_LEFT));
	inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_RIGHT));
	inputManager.addMapping("Back", new KeyTrigger(KeyInput.KEY_Q));
	inputManager.addMapping("Forward", new KeyTrigger(KeyInput.KEY_E));

	inputManager.addListener(analogListener, new String[]{"Up", "Down", "Left", "Right", "Back", "Forward"});
	flyCam.setEnabled(false);
}

private AnalogListener analogListener = new AnalogListener() {

	public void onAnalog(String name, float value, float tpf) {

		if(name.equals("Up")) {
			System.out.println("Up");
			ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(1, 0, 0));
		}
		if(name.equals("Down")) {
			System.out.println("Down");
			ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(-1, 0, 0));
		}
		if(name.equals("Left")) {
			System.out.println("Left");
			ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(0, 1, 0));
		}
		if(name.equals("Right")) {
			System.out.println("Right");
			ship01.getControl(RigidBodyControl.class).applyTorque(new Vector3f(0, -1, 0));
		}
		if(name.equals("Back")) {
			System.out.println("Back");
			ship01.getControl(RigidBodyControl.class).applyImpulse(new Vector3f(0, 0, 1), new Vector3f(0, 0, 0));
		}
		if(name.equals("Forward")) {
			System.out.println("Forward");
			ship01.getControl(RigidBodyControl.class).applyImpulse(new Vector3f(0, 0, -1), new Vector3f(0, 0, 0));
		}
	}
};

}[/java]

you’ll want to convert local coords to global.

To move forward in the local Z axis for example, you would do:

[java]Vector3f impulseDirection = rigidBodyControl.getPhysicsRotation ().mult (Vector3f.UNIT_Z); // gives local Z axis direction in world space[/java]

Same thing for torque, to rotate around the local X axis (up) in world space :

[java]Vector3f torqueXAxis = rigidBodyControl.getPhysicsRotation().mult (Vector3f.UNIT_X);[/java]

1 Like

Thanks wezrule, thats very very helpful.

I’ve only just implemented up & down so far but am confident I can work it out based on your code. Just a quick aside; I got ‘down’ working like this:

[java]torqueXAxis = ship01.getControl(RigidBodyControl.class).getPhysicsRotation().mult(Vector3f.UNIT_X); // What you said for ‘up’
ship01.getControl(RigidBodyControl.class).applyTorque(torqueXAxis.mult(new Vector3f(-1,-1,-1))); // Im negating/inversing this [/java]

I’ve some experience with 2D games so I figured negating the up for down would work. It does. I’m not sure if there is a more efficient way though? It seems wasteful to do two multiplications to me, so I think I’m missing something perhaps :smiley:

Thanks again
Dave

lol never mind, I just found the negate() method of Vector3f :smiley:

Thanks so much for your help :smiley:

You can also .mult(scalar) with scalar = -1 to negate, but also -0.1 to reduce its negative strength or -10 to increase it…

1 Like

X would be sideways, Y up.

Other than that what @wezrule said :slight_smile:

I would have the analog listener set the rotation/acceleration/whatever values on a custom control, and have the control resolve the rotations/accelerations based on the set values, in it’s prePhysicsTick method.

Something you may want to consider is to “setAngularVelocity(Vector3f.ZERO);” in the prePhysicsTick method, because, at least at the start, it is hard to control a ship whose rotations perdure, using forces and torques. After that, you can add complexity.

2 Likes

Thanks for the awesome input; with the advice here Ive been able to implement a Spaceship class with attributes such as ‘rotationSpeed’, ‘thrustPower’ and the like. Brilliant stuff!

Loopies, I’m in total agreement there - with zero friction / no constraints its extremely difficult keeping the spaceship on a reasonably steady heading without it spinning out of control. I plan to remedy this with some kind of simplistic ‘Auto-Pilot’ feature.

Ive got another problem now which I suppose I should open a new topic for, but its a pretty simple question that just takes some explaining…

My spaceship model (.obj format, made in blender) is hollow, but its collisionShape (made with createDynamicCollisionShape) is not hollow; it behaves like a solid object. My aim is to have a player controlled pilot avatar, located inside the ship model and bound within it. I’ve read that this isnt possible with Dynamic meshes and instead I should use a ‘normal’ mesh for the spaceship. But I believe this would have some pretty serious drawbacks (ie. no longer being able to apply physical forces to the spaceship).

So im thinking i should scrap my model and instead build a cube out of six seperate ‘wall panel’ models, put them all into a node and treat that node as my spaceship. But that starts making things quite complicated imho. In the future I may want a space station with a ‘hangar bay’ that a humanoid avatar can pilot a spaceship into. It would be nice if that station was one model, not six (plus garnishing to make it look less like a box, and more like a Salyut).

Any ideas on the best way to accomplish this?

Thanks again
Dave

For posterity - @Zarch, I believe rotating on the X axis is indeed equivalent to up/down rotation like what @wezrule initially said. At least that is how the simulation comes out for me :slight_smile:

Ahh. Rotation. Sorry, I was thinking translation. i.e. if you were doing a “strafe” type sideways movement you would apply thrust along the X axis…to rotate the nose up/down you would indeed rotate around the X axis.