Vector for left and right movement?

I have a bug in my control code for a flying saucer. It works initially but after I move the camera, forward and backwards movement work but movement to the left and to the right is lopsided. This is the code which works before I move the camera and then rightward and leftward movements don’t work:

[java] public void onAnalog(String name, float value, float tpf) {
// computing the normalized direction of the cam to move the node
int speed = 80000;
int rotation = 1;
direction.set(cam.getDirection()).normalizeLocal();
if (name.equals(“moveForward”)) {
direction.multLocal(speed * tpf);
ufoNode.move(direction);
}
if (name.equals(“moveBackward”)) {
direction.multLocal(-speed * tpf);
ufoNode.move(direction);
}
if (name.equals(“moveRight”)) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(speed * tpf);
ufoNode.move(direction);
}
if (name.equals(“moveLeft”)) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(-speed * tpf);
ufoNode.move(direction);
}
if (name.equals(“rotateRight”) && rotate) {
ufoNode.rotate(0, 1 * tpf, 0);
}
if (name.equals(“rotateLeft”) && rotate) {
ufoNode.rotate(0, -1 * tpf, 0);

	}
	if (name.equals("rotateUp") && rotate) {
		ufoNode.rotate(0, 0, -1 * tpf);
	}
	if (name.equals("rotateDown") && rotate) {
		ufoNode.rotate(0, 0, 1 * tpf);
	}
}[/java]

Can you help me? It looks like I would need a variable that is through the center of the ufoNode vertically.

[java]direction.crossLocal(craftSkewer.UNIT_Y).multLocal(movement * tpf);[/java]

But how can that be done? My complete code is

[java]public class SpaceStation extends SimpleApplication implements AnalogListener, ActionListener {

private PlanetAppState planetAppState;
private Geometry mark;
private Node ufoNode;		
private Node spaceStationNode;	
private Node jumpgateNode;
private Node jumpgateNode2;		
private BetterCharacterControl ufoControl;
CameraNode camNode;
ChaseCamera chaseCam;
boolean rotate = false;
Vector3f direction = new Vector3f();
private BulletAppState bulletAppState;

private void setupChaseCamera() {
	flyCam.setEnabled(false);
	chaseCam = new ChaseCamera(cam, ufoNode, inputManager);
	chaseCam.setDefaultDistance(37);

}

public static void main(String[] args) {
	AppSettings settings = new AppSettings(true);
	settings.setResolution(1024, 768);
	SpaceStation app = new SpaceStation();
	app.setSettings(settings);
	// app.showSettings = true;
	app.start();
}

@Override
public void simpleInitApp() {
	// Only show severe errors in log
	java.util.logging.Logger.getLogger("com.jme3").setLevel(java.util.logging.Level.SEVERE);
	bulletAppState = new BulletAppState();
	bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
	stateManager.attach(bulletAppState);
	bulletAppState.setDebugEnabled(false);
	DirectionalLight sun = new DirectionalLight();
	sun.setDirection(new Vector3f(-.1f, 0f, -1f));
	sun.setColor(new ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f));
	rootNode.addLight(sun);
	// Add sky
	Node sceneNode = new Node("Scene");
	sceneNode.attachChild(Utility.createSkyBox(this.getAssetManager(),"Textures/blue-glow-1024.dds"));
	rootNode.attachChild(sceneNode);
	// Create collision test mark
	Sphere sphere = new Sphere(30, 30, 5f);
	mark = new Geometry("mark", sphere);
	Material mark_mat = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
	mark_mat.setColor("Color", ColorRGBA.Red);
	mark.setMaterial(mark_mat);
	// Add planet app state
	planetAppState = new PlanetAppState(rootNode, sun);
	stateManager.attach(planetAppState);

	// Add planet
	FractalDataSource planetDataSource = new FractalDataSource(4);
	planetDataSource.setHeightScale(900f);
	Planet planet = Utility.createEarthLikePlanet(getAssetManager(), 293710.0f, null, planetDataSource);
	planetAppState.addPlanet(planet);
	rootNode.attachChild(planet);

	// Add moon
	FractalDataSource moonDataSource = new FractalDataSource(5);
	moonDataSource.setHeightScale(300f);
	Planet moon = Utility.createMoonLikePlanet(getAssetManager(), 50000, moonDataSource);
	planetAppState.addPlanet(moon);
	rootNode.attachChild(moon);
	moon.setLocalTranslation(new Vector3f(10f, 10f, 505000f));//-950000f, 0f, 0f);
	// add saucer
	ufoNode = (Node) assetManager.loadModel("usaucer_v01.j3o");
	ufoNode.setLocalScale(100f);
	ufoNode.setLocalTranslation((new Vector3f(1000f, -1000f, 328000f)));
	jumpgateNode = (Node) assetManager.loadModel("JumpGate.j3o");
	jumpgateNode.setLocalScale(10000f);
	jumpgateNode.setLocalTranslation((new Vector3f(10f, 10f, 708000f)));
	
	spaceStationNode = (Node) assetManager.loadModel("SpaceStation.j3o");
	spaceStationNode.setLocalScale(4000f);
	spaceStationNode.setLocalTranslation((new Vector3f(10000f, -10f, 425000f)));	
	
	jumpgateNode2 = (Node) assetManager.loadModel("JumpGate.j3o");
	jumpgateNode2.setLocalScale(10000f);
	jumpgateNode2.setLocalTranslation((new Vector3f(10f, 10f, 798300f)));

	/* This quaternion stores a 180 degree rolling rotation */
	// Quaternion roll180 = new Quaternion();
	// roll180.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1));
	/* The rotation is applied: The object rolls by 180 degrees. */
	// ufoNode.setLocalRotation(roll180);
	rootNode.attachChild(jumpgateNode);
	rootNode.attachChild(jumpgateNode2);
	rootNode.attachChild(spaceStationNode);		
	
	// creating the camera Node
	camNode = new CameraNode("CamNode", cam);
	// Setting the direction to Spatial to camera, this means the camera
	// will copy the movements of the Node
	camNode.setControlDir(ControlDirection.SpatialToCamera);
	// attaching the camNode to the teaNode
	ufoNode.attachChild(camNode);
	// setting the local translation of the cam node to move it away a bit
	camNode.setLocalTranslation(new Vector3f(-40, 0, 0));
	// setting the camNode to look at the teaNode
	camNode.lookAt(ufoNode.getLocalTranslation(), Vector3f.UNIT_Y);
	// disable the default 1st-person flyCam (don't forget this!!)
	ufoControl = new BetterCharacterControl(100000f, 80000f, 5000f);// (2, 4, 0.5f);
	// radius (meters), height (meters), gravity (mass)
	//ufoNode.addControl(ufoControl);
	//rootNode.attachChild(ninjaNode);
	//bulletAppState.getPhysicsSpace().add(ufoControl);
	//getPhysicsSpace().add(ufoControl);
	rootNode.attachChild(ufoNode);
	flyCam.setEnabled(false);
	registerInput();
}

private PhysicsSpace getPhysicsSpace() {
	return bulletAppState.getPhysicsSpace();
}

public void registerInput() {
	inputManager.addMapping("moveForward", new KeyTrigger(keyInput.KEY_UP),
			new KeyTrigger(keyInput.KEY_W));
	inputManager.addMapping("moveBackward", new KeyTrigger(
			keyInput.KEY_DOWN), new KeyTrigger(keyInput.KEY_S));
	inputManager.addMapping("moveRight",
			new KeyTrigger(keyInput.KEY_RIGHT), new KeyTrigger(
					keyInput.KEY_D));
	inputManager.addMapping("moveLeft", new KeyTrigger(keyInput.KEY_LEFT),
			new KeyTrigger(keyInput.KEY_A));
	inputManager.addMapping("toggleRotate", new MouseButtonTrigger(
			MouseInput.BUTTON_LEFT));
	inputManager.addMapping("rotateRight", new MouseAxisTrigger(
			MouseInput.AXIS_X, true));
	inputManager.addMapping("rotateLeft", new MouseAxisTrigger(
			MouseInput.AXIS_X, false));
	inputManager.addMapping("rotateUp", new MouseAxisTrigger(
			MouseInput.AXIS_Y, true));
	inputManager.addMapping("rotateDown", new MouseAxisTrigger(
			MouseInput.AXIS_Y, false));
	inputManager.addListener(this, "moveForward", "moveBackward",
			"moveRight", "moveLeft");
	inputManager.addListener(this, "rotateRight", "rotateLeft", "rotateUp",
			"rotateDown", "toggleRotate");
	// Toggle mouse cursor
	inputManager.addMapping("TOGGLE_CURSOR", new MouseButtonTrigger(
			MouseInput.BUTTON_LEFT), new KeyTrigger(KeyInput.KEY_SPACE));
	inputManager.addListener(actionListener, "TOGGLE_CURSOR");
	// Toggle wireframe
	inputManager.addMapping("TOGGLE_WIREFRAME", new KeyTrigger(
			KeyInput.KEY_T));
	inputManager.addListener(actionListener, "TOGGLE_WIREFRAME");
	// Collision test
	inputManager.addMapping("COLLISION_TEST", new MouseButtonTrigger(
			MouseInput.BUTTON_RIGHT));
	inputManager.addListener(actionListener, "COLLISION_TEST");
}

public void onAnalog(String name, float value, float tpf) {
	// computing the normalized direction of the cam to move the node
	int speed = 80000;
	int rotation = 1;
	direction.set(cam.getDirection()).normalizeLocal();		
	Vector3f camDir = cam.getDirection().clone().multLocal(8f);
	Vector3f camLeft = cam.getLeft().clone().multLocal(800f);		
	camDir.y = 0;
	camLeft.y = 0;	
	/*walkDirection.set(0, 0, 0);
	if (left) {
		walkDirection.addLocal(camLeft);
	}
	if (right) {
		walkDirection.addLocal(camLeft.negate());
	}*/
	
	if (name.equals("moveForward")) {
		direction.multLocal(speed * tpf);
		ufoNode.move(direction);
	}
	if (name.equals("moveBackward")) {
		direction.multLocal(-speed * tpf);
		ufoNode.move(direction);
	}
	if (name.equals("moveRight")) {
		System.out.println("vector: "+Vector3f.UNIT_Y);
		direction.crossLocal(ufoNode.getLocalTranslation()).multLocal(speed * tpf);
		//direction.multLocal(speed * tpf);
		//direction = Vector3f.UNIT_Y.multLocal(speed * tpf);
		//direction.set(0, 0, 0);
		//direction.addLocal(camLeft.negate());
		ufoNode.move(direction);
	}
	if (name.equals("moveLeft")) {
		direction.crossLocal(direction.UNIT_Y).multLocal(-speed * tpf);
		//direction.multLocal(-speed * tpf);			//
		//direction.set(0, 0, 0);
		//direction.addLocal(camLeft);			
		ufoNode.move(direction);
	}
	if (name.equals("rotateRight") && rotate) {
		ufoNode.rotate(0, 1 * tpf, 0);
		//camNode.rotate(0, 1 * tpf, 0);
	}
	if (name.equals("rotateLeft") && rotate) {
		ufoNode.rotate(0, -1 * tpf, 0);
		//camNode.rotate(0, -1 * tpf, 0);
		
	}
	if (name.equals("rotateUp") && rotate) {
		ufoNode.rotate(0, 0, -1 * tpf);
		//camNode.rotate(0, 0, -1 * tpf);
	}
	if (name.equals("rotateDown") && rotate) {
		ufoNode.rotate(0, 0, 1 * tpf);
		//camNode.rotate(0, 0, 1 * tpf);
	}
}

public void onAction(String name, boolean keyPressed, float tpf) {
	// toggling rotation on or off
	if (name.equals("toggleRotate") && keyPressed) {
		rotate = true;
		inputManager.setCursorVisible(false);
	}
	if (name.equals("toggleRotate") && !keyPressed) {
		rotate = false;
		inputManager.setCursorVisible(true);
	}
	if (name.equals("TOGGLE_CURSOR") && !keyPressed) {
		if (inputManager.isCursorVisible()) {
			inputManager.setCursorVisible(false);
		} else {
			inputManager.setCursorVisible(true);
		}
	}
	if (name.equals("TOGGLE_WIREFRAME") && !keyPressed) {
		for (Planet planet : planetAppState.getPlanets()) {
			planet.toogleWireframe();
		}
	}
	if (name.equals("COLLISION_TEST") && !keyPressed) {
		CollisionResults results = new CollisionResults();
		Ray ray = new Ray(cam.getLocation(), cam.getDirection());
		// Test collision with closest planet's terrain only
		planetAppState.getNearestPlanet().getTerrainNode()
				.collideWith(ray, results);
		System.out.println("----- Collisions? " + results.size() + "-----");
		for (int i = 0; i < results.size(); i++) {
			// For each hit, we know distance, impact point, name of
			// geometry.
			float dist = results.getCollision(i).getDistance();
			Vector3f pt = results.getCollision(i).getContactPoint();
			String hit = results.getCollision(i).getGeometry().getName();
			System.out.println("* Collision #" + i);
			System.out.println("  You shot " + hit + " at " + pt + ", "
					+ dist + " wu away.");
		}
		if (results.size() > 0) {
			// The closest collision point is what was truly hit:
			CollisionResult closest = results.getClosestCollision();
			// Let's interact - we mark the hit with a red dot.
			mark.setLocalTranslation(closest.getContactPoint());
			rootNode.attachChild(mark);
		} else {
			// No hits? Then remove the red mark.
			rootNode.detachChild(mark);
		}
	}
}

private ActionListener actionListener = new ActionListener() {
	public void onAction(String name, boolean pressed, float tpf) {
		if (name.equals("TOGGLE_CURSOR") && !pressed) {
			if (inputManager.isCursorVisible()) {
				inputManager.setCursorVisible(false);
			} else {
				inputManager.setCursorVisible(true);
			}
		}
		if (name.equals("TOGGLE_WIREFRAME") && !pressed) {
			for (Planet planet : planetAppState.getPlanets()) {
				planet.toogleWireframe();
			}
		}
		if (name.equals("COLLISION_TEST") && !pressed) {
			CollisionResults results = new CollisionResults();
			Ray ray = new Ray(cam.getLocation(), cam.getDirection());
			// Test collision with closest planet's terrain only
			planetAppState.getNearestPlanet().getTerrainNode()
					.collideWith(ray, results);
			System.out.println("----- Collisions? " + results.size()
					+ "-----");
			for (int i = 0; i < results.size(); i++) {
				// For each hit, we know distance, impact point, name of
				// geometry.
				float dist = results.getCollision(i).getDistance();
				Vector3f pt = results.getCollision(i).getContactPoint();
				String hit = results.getCollision(i).getGeometry()
						.getName();
				System.out.println("* Collision #" + i);
				System.out.println("  You shot " + hit + " at " + pt + ", "
						+ dist + " wu away.");
			}
			if (results.size() > 0) {
				// The closest collision point is what was truly hit:
				CollisionResult closest = results.getClosestCollision();
				// Let's interact - we mark the hit with a red dot.
				mark.setLocalTranslation(closest.getContactPoint());
				rootNode.attachChild(mark);
			} else {
				// No hits? Then remove the red mark.
				rootNode.detachChild(mark);
			}
		}
	}
};

}
[/java]

Instead of taking a cross product with +Y (which distorts when you tilt the camera up or down) how about using cam.getLeft()?

1 Like

Yes, much better but then it goes up when I press right and down when I press left regardless of camera so it’s in the right direction and I can also use upwards and downwards control. I suppose that I must manipulate the up/north vector to become a right/east vector but how?

[java] if (name.equals(“moveRight”)) {
direction.crossLocal(cam.getLeft()).multLocal(speed * tpf);
ufoNode.move(direction); //goes straight up
}
if (name.equals(“moveLeft”)) {
direction.crossLocal(cam.getLeft()).multLocal(-speed * tpf);
ufoNode.move(direction); //goes down
}[/java]

It’s good for a UFO to fly directly upwards as well as straight forward but what I want now is right and left and I think that code can be based on up/north and down/south. Can you tell me how to get the east direction (right) now that I have the up (north) direction right? Thanks.

I think you should skip crossLocal() entirely because that changes the vector’s direction. Something like this:

[java]
if (name.equals(“moveRight”)) {
direction.set(cam.getLeft()).multLocal(-speed * tpf);
ufoNode.move(direction);
}
if (name.equals(“moveLeft”)) {
direction.set(cam.getLeft().multLocal(speed * tpf));
ufoNode.move(direction);
}
[/java]

1 Like

Perfect. It works. Thanks a lot!

@sgold said: I think you should skip crossLocal() entirely because that changes the vector's direction. Something like this:

[java]
if (name.equals(“moveRight”)) {
direction.set(cam.getLeft()).multLocal(-speed * tpf);
ufoNode.move(direction);
}
if (name.equals(“moveLeft”)) {
direction.set(cam.getLeft().multLocal(speed * tpf));
ufoNode.move(direction);
}
[/java]

1 Like