[solved] Rotating object to world location using local axes

Hello there,



i am currently working on a project called “SpaceFight”. It’s a space shooter game with a gameplay-style like X-Wing vs. Tie-Fighter.

It’s making good progress as everything works pretty good. But I have a problem regarding AI turning to a target:



This is how the ships are controlled:


  • RigidBodyControl is used to apply physics to the spaceships.

  • Custom "FlightControl" keeps data like Top-Speed, Linear brake strength, Acceleration etc. and manipulates physics regarding to core values (elevator, aileron, rudder). It takes values from -1f to 1f.

  • Then a custom "PlayerControl" or "AIControl" manipulates the FlightControl's parameters to turn and fly towards targets



Up to now I was using "cheating" AI which just used Quaternions and "slerp" to turn the ship's Node directly in World space. Some reasons I want to change this:


  • I consider the AI "cheating" because they directly manipulate the node.

  • It breaks the workflow described above

  • It has side-effects on physics (rotations from collisions are overwritten by AI inputs)

  • Since I switched to multithreaded BulletAppState and Native bullet i've got serious problems with velocities when constantly modifying physicsRotation directly



So far here is the code I use to determine where the AI has to steer:

[java]
private Vector3f getSteeringData(Vector3f worldPosition, Vector3f up) {
Vector3f steeringPosition = new Vector3f();
getSpatial().getWorldRotation().inverse().multLocal(steeringPosition.set(worldPosition).subtractLocal(getSpatial().getWorldTranslation()));

Quaternion targetRotation = new Quaternion();
targetRotation.lookAt(steeringPosition, up);

Vector3f steeringData = new Vector3f();
float[] angles = targetRotation.toAngles(null);

steeringData.x = angles[0] / (FastMath.pow(flightControl.getTurnrate(), 2) / (2 * flightControl.getAngulardamp()));
steeringData.y = angles[1] / (FastMath.pow(flightControl.getTurnrate(), 2) / (2 * flightControl.getAngulardamp()));
steeringData.z = angles[2] / (FastMath.pow(flightControl.getTurnrate(), 2) / (2 * flightControl.getAngulardamp()));

return steeringData;
}
[/java]

Application to FlightControl works like this:

[java]
Vector3f steeringData = getSteeringData(position, up);
flightControl.setElevator(steeringData.x);
flightControl.setRudder(steeringData.y);
flightControl.setAileron(steeringData.z);
[/java]

Currently the code to determine the elevator, rudder and aileron values (from -1 to 1) is temporary...
It somewhat works but sometimes ships just start flying wild circles...
It would be very great if someone could point me into the right direction on how to calculate that correctly with angular values...

Greetings

P.S.: I am aware that the code above is not optimal... But if you got a tip for me how I can make it better you're welcome.
P.P.S: Here are some Screenshots of the project:

http://imgur.com/ryCo4,jOjGq,mElfR,AWOq2

Use direction vectors instead of trying to cope with rotations. The math for dummies tutorial shows how.

Yes, either that, or if you use physics for the spaceships do force calculations and applyTorque or similar.

Thank you both for your very quick replies!



@normen: you mean like building local 2D-Vectors, get forward/up-vector of ship and using the angle between those for calculation?



Like this (as example for rudder):

[java]

Vector3f steeringPosition = new Vector3f();

getSpatial().getWorldRotation().inverse().multLocal(steeringPosition.set(worldPosition).subtractLocal(getSpatial().getWorldTranslation()));



steeringPosition.y = 0;

steeringData.y = steeringPosition.angleBetween(Vector3f.UNIT_Z) / (FastMath.pow(flightControl.getTurnrate(), 2) / (2 * flightControl.getAngulardamp()));

[/java]



@Empire Phoenix: The FlightControl itself forces calculations based upon the settings for elevator, rudder and aileron (and some more values). This is also the case for players which control the rudders. The methods for elevator, rudder and aileron are just a layer on top like the flight-stick in the cockpit.



Update-logic of the FlightControl is built like this (I don’t post code because its really long and pretty unimportant):

  1. fetch linear and angular velocity of RigidBody
  2. apply dampening on velocities (braking thrusters)
  3. compute linear and angular velocity vectors from FlightControl properties (topspeed, turnrate, acceleration, userinput etc.)
  4. “combine” computed vectors with previously fetched vectors
  5. force velocities on physics (setLinearVelocity, setAngularVelocity)



    This model already works pretty well and I decided against working with forces. Forces make things very complex since there is no friction in Space. It’s just about computation of the AI behaviour.



    Greetings!

I mean storing the data as direction vectors but basically yes :slight_smile:

Thank you for the tip.



Working with direction vectors made it a lot easier. Now I could spot the problem. It was the UP vector which was causing trouble. Forgot to transform it to the local spaceship’s coordinate system!



JME is a really great thing!

Never got playable results this fast with equal quality when creating a game!



For everyone who want’s to know:

If the up vector is transformed the same way as the direction to target…

[java]Vector3f steeringUp = getSpatial().getWorldRotation().inverse().mult(up);[/java]

…and then used in the lookAt…

[java]targetRotation.lookAt(steeringPosition, steeringUp);[/java]

…it works.



But the same results can be achieved comparing direction vectors directly:

(unoptimized code ahead!)

[java]

private Vector3f getSteeringData(Vector3f worldPosition, Vector3f up) {

// RETREIVE LOCAL DIRECTION TO TARGET POSITION

Vector3f steeringPosition = new Vector3f();

getSpatial().getWorldRotation().inverse().multLocal(steeringPosition.set(worldPosition).subtractLocal(getSpatial().getWorldTranslation()));



// RETREIVE LOCAL UP VECTOR DIRECTION

Vector3f upPosition = new Vector3f(up);

getSpatial().getWorldRotation().inverse().multLocal(upPosition);



// CREATE 2D-VECTORS TO COMPARE

Vector3f elevatorPos = new Vector3f(steeringPosition).normalizeLocal();

elevatorPos.x = 0;

Vector3f rudderPos = new Vector3f(steeringPosition).normalizeLocal();

rudderPos.y = 0;

Vector3f aileronPos = new Vector3f(upPosition).normalizeLocal();

aileronPos.z = 0;



// CALCULATE ANGLES BETWEEN VECTORS AND INVERT STEERING DIRECTION IF NEEDED

Vector3f steeringData = new Vector3f();

steeringData.x = Vector3f.UNIT_Z.angleBetween(elevatorPos);

if (elevatorPos.y > 0) {

steeringData.x *= -1;

}

steeringData.y = Vector3f.UNIT_Z.angleBetween(rudderPos);

if (rudderPos.x < 0) {

steeringData.y *= -1;

}

steeringData.z = Vector3f.UNIT_Y.angleBetween(aileronPos);

if (aileronPos.x > 0) {

steeringData.z *= -1;

}



// APPLY SOME “LOOK AHEAD” TO THE STEERING TO MAKE IT SMOOTHER (PREVENTS OVERSHOOTING)

if (steeringData.x != 0) {

steeringData.x = steeringData.x / (FastMath.pow(flightControl.getTurnrate(), 2) / (2 * flightControl.getAngulardamp()));

}

if (steeringData.y != 0) {

steeringData.y = steeringData.y / (FastMath.pow(flightControl.getTurnrate(), 2) / (2 * flightControl.getAngulardamp()));

}

if (steeringData.z != 0) {

steeringData.z = steeringData.z / (FastMath.pow(flightControl.getTurnrate(), 2) / (2 * flightControl.getAngulardamp()));

}



// RETURN THE DATA

return steeringData;

}

[/java]



What the above method does is just computing the local amounts a Spatial has to turn to face towards a world location. Additionally the values consider braking time and turnrate to prevent overshooting. The computed values can be used to set angular velocity on a RigidBody or as in my case to tell the AI-pilot where the control-stick has to go. :slight_smile:



Greetings!



Edit: How can I mark the Topic as solved?! :smiley:

1 Like

Just use the edit topic button at the top and put [solved] or similar at the start of the subject.