Well, I started to try gdx-ai without much success. Creating a simple agent that works with the engine was quite simple as I just had to translate the code in here. However, I’m haveing lot of troubles trying to get it work with bullet physics (for which I took a look at this).
My understanding about 3D operations, in special with orientations and angular velocities is really poor so I must have many mistakes but I can’t find them :S.
The problem is that If I try a simple wander steering my steerable starts to fly whatever I do :S (when it just have to move around).
I don’t really understand the angular velocity so I tried to return a constant value, but nothing, the same with the orientation get. I’m almost sure there is something bad with the applySteering method as if I don’t try to rotate the body, it just seems to work.
So… I suppose that my question is more how I get the agent physics to face the linear velocity without affecting it (however, any correction on the agent methods is a big help too).
The usecase is (I packed all classes in the same file to allow a “fast” copy-paste):
package mygame;
import com.badlogic.gdx.ai.GdxAI;
import com.badlogic.gdx.ai.steer.Steerable;
import com.badlogic.gdx.ai.steer.SteeringAcceleration;
import com.badlogic.gdx.ai.steer.behaviors.Wander;
import com.badlogic.gdx.ai.utils.Location;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;
import com.jme3.util.TempVars;
/**
*
*/
public class Main extends SimpleApplication {
public static void main(String[] args) {
Application app = new Main();
app.start();
}
Spatial obj;
BulletAppState bullet;
@Override
public void simpleInitApp() {
Box box1 = new Box(1,1,1);
Geometry blue = new Geometry("Box", box1);
blue.setLocalTranslation(new Vector3f(1,-1,1));
Material mat1 = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mat1.setColor("Color", ColorRGBA.Blue);
blue.setMaterial(mat1);
int quadSize = 1000;
Quad quad = new Quad(quadSize, quadSize);
Geometry floor = new Geometry("Floor", quad);
Material mat2 = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mat2.setColor("Color", ColorRGBA.Green);
floor.setMaterial(mat2);
obj = blue;
flyCam.setMoveSpeed(30);
rootNode.attachChild(obj);
rootNode.attachChild(floor);
bullet = new BulletAppState();
stateManager.attach(bullet);
bullet.setDebugEnabled(true);
floor.rotate(new Quaternion().fromAngleAxis(270 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X));
floor.setLocalTranslation(-quadSize/2, -2, quadSize/2);
floor.addControl(new RigidBodyControl(CollisionShapeFactory.createMeshShape(floor), 0));
bullet.getPhysicsSpace().addAll(floor);
// trySimpleSteering();
tryBulletSteering();
}
public void tryBulletSteering() {
obj.addControl(new RigidBodyControl(CollisionShapeFactory.createBoxShape(obj)));
final BulletSteeringAgent agent = new BulletSteeringAgent(obj.getControl(RigidBodyControl.class));
bullet.getPhysicsSpace().addAll(obj);
obj.addControl(new AbstractControl() {
@Override
protected void controlUpdate(float tpf) {
GdxAI.getTimepiece().update(tpf);
agent.update(tpf);
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) { }
}
);
}
public static class BulletSteeringAgent extends AbstractBulletSteeringAgent {
Wander<Vector3> wander = new Wander(this);
private final SteeringAcceleration<Vector3> steeringOutput = new SteeringAcceleration<>(new Vector3());
public BulletSteeringAgent(RigidBodyControl rigidBody) {
super(rigidBody);
wander.setWanderRadius(1);
wander.setWanderOffset(2);
wander.setWanderRate(MathUtils.PI2 * 2);
}
public void update(float tpf) {
wander.calculateSteering(steeringOutput);
applySteering(steeringOutput, tpf);
}
}
public static class AbstractBulletSteeringAgent implements Steerable<Vector3> {
boolean independentFacing;
RigidBodyControl rigidBody;
private final Vector3 linearVelocity = new Vector3();
private final Vector3 position = new Vector3();
private float boundingRadius;
private float zeroLinearSpeedThreshold;
private float maxLinearSpeed = 20f;
private float maxLinearAcceleration = 20f;
private boolean tagged;
private float maxAngularSpeed = 20f;
private float maxAngularAcceleration = 20f;
public AbstractBulletSteeringAgent(RigidBodyControl rigidBody) {
this.rigidBody = rigidBody;
}
@Override
public Vector3 getLinearVelocity() {
TempVars vars = TempVars.get();
rigidBody.getLinearVelocity(vars.vect1);
Vector3 vector = vector3fToVector3(vars.vect1, linearVelocity);
vars.release();
return vector;
}
@Override
public float getAngularVelocity() {
TempVars vars = TempVars.get();
rigidBody.getAngularVelocity(vars.vect1);
float value = vars.vect1.length();
vars.release();
return value;
}
@Override
public float getBoundingRadius() {
return boundingRadius;
}
@Override
public boolean isTagged() {
return tagged;
}
@Override
public void setTagged(boolean tagged) {
this.tagged = tagged;
}
@Override
public Vector3 getPosition() {
TempVars vars = TempVars.get();
Vector3 vector = vector3fToVector3(rigidBody.getPhysicsLocation(vars.vect1), position);
vars.release();
return vector;
}
@Override
public float getOrientation() {
TempVars vars = TempVars.get();
Vector3f vector = rigidBody.getPhysicsRotation(vars.quat1).multLocal(vars.vect1.set(Vector3f.UNIT_Z));
float orientation = SteeringUtil.vectorToAngle(vector.x, vector.y, vector.z);
vars.release();
return orientation;
}
@Override
public void setOrientation(float orientation) {
TempVars vars = TempVars.get();
rigidBody.setPhysicsRotation(rigidBody.getPhysicsRotation(vars.quat1).fromAngleAxis(-orientation, Vector3f.UNIT_Y));
vars.release();
}
@Override
public float vectorToAngle(Vector3 vector) {
return SteeringUtil.vectorToAngle(vector);
}
@Override
public Vector3 angleToVector(Vector3 outVector, float angle) {
return SteeringUtil.angleToVector(outVector, angle);
}
@Override
public Location<Vector3> newLocation() {
return new SteeringLocation();
}
@Override
public float getZeroLinearSpeedThreshold() {
return zeroLinearSpeedThreshold;
}
@Override
public void setZeroLinearSpeedThreshold(float zeroLinearSpeedThreshold) {
this.zeroLinearSpeedThreshold = zeroLinearSpeedThreshold;
}
@Override
public float getMaxLinearSpeed() {
return maxLinearSpeed;
}
@Override
public void setMaxLinearSpeed(float maxLinearSpeed) {
this.maxLinearSpeed = maxLinearSpeed;
}
@Override
public float getMaxLinearAcceleration() {
return maxLinearAcceleration;
}
@Override
public void setMaxLinearAcceleration(float maxLinearAcceleration) {
this.maxLinearAcceleration = maxLinearAcceleration;
}
@Override
public float getMaxAngularSpeed() {
return maxAngularSpeed;
}
@Override
public void setMaxAngularSpeed(float maxAngularSpeed) {
this.maxAngularSpeed = maxAngularSpeed;
}
@Override
public float getMaxAngularAcceleration() {
return maxAngularAcceleration;
}
@Override
public void setMaxAngularAcceleration(float maxAngularAcceleration) {
this.maxAngularAcceleration = maxAngularAcceleration;
}
protected void applySteering(SteeringAcceleration<Vector3> steering, float time) {
TempVars vars = TempVars.get();
rigidBody.getLinearVelocity(vars.vect1);
Vector3f bodyLinear = vars.vect1;
vector3fToVector3(bodyLinear, linearVelocity).mulAdd(steering.linear, time).limit(getMaxLinearAcceleration());
rigidBody.setLinearVelocity(vector3ToVector3f(linearVelocity, bodyLinear));
if (!linearVelocity.isZero(getZeroLinearSpeedThreshold())) {
if(!independentFacing) {
rigidBody.getPhysicsRotation(vars.quat1);
vars.quat1.lookAt(bodyLinear, Vector3f.UNIT_Y); //vars.quat1.lookAt(vars.vect3.set(bodyLinear).multLocal(vars.vect4.set(Vector3f.UNIT_XYZ).subtractLocal(Vector3f.UNIT_Y)), Vector3f.UNIT_Y);
rigidBody.setPhysicsRotation(vars.quat1);
}
}
vars.release();
rigidBody.activate();
}
public static Vector3 vector3fToVector3(Vector3f from, Vector3 to) {
return to.set(from.x, from.y, from.z);
}
public static Vector3f vector3ToVector3f(Vector3 from, Vector3f to) {
return to.set(from.x, from.y, from.z);
}
}
public static abstract class SteeringUtil {
public static float vectorToAngle(float x, float y, float z) {
return (float) Math.atan2(-z, x);
}
public static float vectorToAngle(Vector3 vector) {
return (float) Math.atan2(-vector.z, vector.x);
}
public static Vector3 angleToVector(Vector3 outVector, float angle) {
outVector.z = -(float) Math.sin(angle);
outVector.y = 0;
outVector.x = (float) Math.cos(angle);
return outVector;
}
public static float calculateOrientationFromLinearVelocity(Steerable character) {
// If we haven't got any velocity, then we can do nothing.
if (character.getLinearVelocity().isZero(character.getZeroLinearSpeedThreshold()))
return character.getOrientation();
return character.vectorToAngle(character.getLinearVelocity());
}
}
public static class SteeringLocation implements Location<Vector3> {
private final Vector3 position = new Vector3();
private float orientation;
public SteeringLocation() { }
@Override
public Vector3 getPosition() {
return position;
}
@Override
public float getOrientation() {
return orientation;
}
@Override
public void setOrientation(float orientation) {
this.orientation = orientation;
}
@Override
public float vectorToAngle(Vector3 vector) {
return SteeringUtil.vectorToAngle(vector);
}
@Override
public Vector3 angleToVector(Vector3 outVector, float angle) {
return SteeringUtil.angleToVector(outVector, angle);
}
@Override
public Location<Vector3> newLocation() {
return new SteeringLocation();
}
}
}
Any help with is appreciated.