I’m experiencing issues with my spaceship game: i’m using physics to move it as a dynamic object (RigidBodyControl on a BulletAppState) represented by a Node and I’m applying different forces on them, such as applyTorqueImpulse for rotations( which works perfectly) and applyForce for moving it forward and “backwards”.
However I’ve got a strange jittering problem with my ship whenever I try to apply any type of force to move it forward (applyImpulse, applyForce) and a desync between its physics and local translation resulting to it.
Don’t have any clue about how to solve it, already tried to isolate and find the problem but without any result. Tried even with a simple BoxCollisionShape in case it was some collision issue but the problem wasn’t gone.
Happy to receive any kind of help, support or advices. Thanks in advance.
Video about the issue here : https://www.youtube.com/watch?v=AMzdWTxwMLk
Main class preview
package mygame;
import planetsLogic.Pianeti;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix3f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Cylinder;
import com.jme3.scene.shape.Sphere;
import com.leapmotion.leap.*;
/**
* test
* @author Dige
*/
public class Main extends SimpleApplication {
BulletAppState bulletAppState;
Pianeti planets;
Geometry[] planetsGeometries;
Geometry saturnRing;
Spatial shipSpatial;
Node ship;
LeapMotionListener listener;
Controller controller;
int time;
private AnalogListener analogListener = new AnalogListener() {
public void onAnalog(String name, float value, float tpf)
{
if (name.equals("destra")) //YAW RIGHT
{
Vector3f torqueYaxis = ship.getControl(RigidBodyControl.class).getPhysicsRotation().mult(Vector3f.UNIT_Y);
ship.getControl(RigidBodyControl.class).applyTorqueImpulse(torqueYaxis.negate());
}
if (name.equals("sinistra")) //YAW LEFT
{
Vector3f torqueYaxis = ship.getControl(RigidBodyControl.class).getPhysicsRotation().mult(Vector3f.UNIT_Y);
ship.getControl(RigidBodyControl.class).applyTorqueImpulse(torqueYaxis);
}
if (name.equals("su")) //PITCH UP
{
Vector3f torqueXaxis = ship.getControl(RigidBodyControl.class).getPhysicsRotation().mult(Vector3f.UNIT_X);
ship.getControl(RigidBodyControl.class).applyTorqueImpulse(torqueXaxis.negate().mult(10));
}
if (name.equals("giu")) //PITCH DOWN
{
Vector3f torqueXaxis = ship.getControl(RigidBodyControl.class).getPhysicsRotation().mult(Vector3f.UNIT_X);
ship.getControl(RigidBodyControl.class).applyTorqueImpulse(torqueXaxis.mult(10));
}
if (name.equals("inclinaSX")) //ROLL LEFT
{
Vector3f torqueZaxis = ship.getControl(RigidBodyControl.class).getPhysicsRotation().mult(Vector3f.UNIT_Z);
ship.getControl(RigidBodyControl.class).applyTorqueImpulse(torqueZaxis.mult(10).negate());
}
if (name.equals("inclinaDX")) //ROLL RIGHT
{
Vector3f torqueZaxis = ship.getControl(RigidBodyControl.class).getPhysicsRotation().mult(Vector3f.UNIT_Z);
ship.getControl(RigidBodyControl.class).applyTorqueImpulse(torqueZaxis.mult(10));
}
if(name.equals("avanti")) //FORWARD
{
float[] angles = new float[3];
angles = ship.getLocalRotation().toAngles(angles);
ship.getControl(RigidBodyControl.class).applyImpulse(new Vector3f((float)Math.sin(angles[1])*100,0f,(float)Math.cos(angles[1])*100),new Vector3f(0, 0, 0));
}
if(name.equals("indietro")) //BACKWARDS OR BRAKE, STILL TO DEFINE AND COMPLETE
{
/*if(value>0)
nave.getControl(RigidBodyControl.class).setFriction(8000f);
else
nave.getControl(RigidBodyControl.class).setFriction(1f);*/
}
if (name.equals("Time forward")) //ACCELERATE PLANET UPDATE
{
if(value > 0)
if(time < 1)
aggiornaSistema(time++);
else
aggiornaSistema(time *= 2);
}
else if (name.equals("Time backwards")) //DECELERATE PLANET UPDATE
{
if(value > 0)
{
if(time <= 1)
aggiornaSistema(time--);
else
aggiornaSistema(time /= 2);
}
if(time < 0)
time = 0;
}
}
};
public static void main(String[] args) {
Main app = new Main();
app.setDisplayStatView(false);
app.start();
}
@Override
public void simpleInitApp() {
//PHYSICS
setPhysics();
//SHIP
setShip();
//UI
//setUI();
//LEAPMOTION
//setLeap();
//CAMERA
setCam();
//PLANETS
setPlanets();
//INPUT MANAGER
setMappings();
//SATURN RING
setSaturnRing();
//STARS
setStars();
}
@Override
public void simpleUpdate(float tpf) {
//PLANETS UPDATE
aggiornaSistema(time);
//CAM ROTATION UPDATE
cam.setRotation(ship.getLocalRotation());
rotateCamera(this.getCamera(), 0, tpf, ship.getLocalTranslation());
//CHECKING DISTANCE DIFFERENCE ON Z AXIS
if(FastMath.abs(ship.getLocalTranslation().getZ())- FastMath.abs(ship.getControl(RigidBodyControl.class).getPhysicsLocation().getZ())>3)
System.out.println("distance too high");
System.out.println("local" + ship.getLocalTranslation());
System.out.println("phys" + ship.getControl(RigidBodyControl.class).getPhysicsLocation());
/* LEAP MOTION CONTROL
Frame frame = controller.frame();
for(Hand hand : frame.hands())
{
String handType = hand.isLeft() ? "Left hand" : "Right hand";
System.out.println(handType + " - " + hand.palmPosition());
if(hand.isLeft() && hand.palmPosition().getY()> 40)
analogListener.onAnalog("avanti", 0.5f, tpf);
if(hand.isRight()){
if(hand.palmPosition().getX()>80)
analogListener.onAnalog("destra", 0.5f, tpf);
else if(hand.palmPosition().getX()<-40)
analogListener.onAnalog("sinistra", 0.5f, tpf);
}
}*/
/* COLLISION TO IMPLEMENT
* for(int i=0; i<geom.length;i++)
{
CollisionResults results = new CollisionResults();
geom[i].collideWith(nave, results);
// Use the results
if (results.size() > 0)
{
System.out.println("eeeeeeeeee");
}
}*/
}
@Override
public void simpleRender(RenderManager rm)
{
}
private void aggiornaSistema(int giorniDaTrascorrere){
for(int giorno = 0; giorno < giorniDaTrascorrere; giorno++)
{
planets.aggiorna();
for(int i=1;i<planetsGeometries.length;i++)
{
if(i==7)
saturnRing.setLocalTranslation((float)planets.getPosizioneCorpo(i-1).getY(),0f, (float)planets.getPosizioneCorpo(i-1).getX());
planetsGeometries[i].setLocalTranslation((float)planets.getPosizioneCorpo(i-1).getY(),0f, (float)planets.getPosizioneCorpo(i-1).getX());
planetsGeometries[i].rotate(0, 0, (float)planets.getLunghezzaRotazioneCorpo(i-1));
}
}
}
/*------------------------------------------------------------------------------------------
*--- SETTERS
*------------------------------------------------------------------------------------------
*/
void setPhysics() {
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
bulletAppState.getPhysicsSpace().setGravity(new Vector3f(0, 0, 0));
bulletAppState.setDebugEnabled(true);
}
void setMappings(){
inputManager.addMapping("Time forward", new KeyTrigger(KeyInput.KEY_N));
inputManager.addMapping("Time backwards", new KeyTrigger(KeyInput.KEY_B));
inputManager.addMapping("sinistra", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("destra", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("su", new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping("giu", new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping("avanti", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping("indietro", new KeyTrigger(KeyInput.KEY_LMENU));
inputManager.addMapping("inclinaSX", new KeyTrigger(KeyInput.KEY_LEFT));
inputManager.addMapping("inclinaDX", new KeyTrigger(KeyInput.KEY_RIGHT));
inputManager.addMapping("su", new KeyTrigger(KeyInput.KEY_DOWN));
inputManager.addMapping("giu", new KeyTrigger(KeyInput.KEY_UP));
inputManager.addListener(analogListener, new String[]{"Time forward","Time backwards","sinistra","destra","su","giu","avanti","indietro","inclinaSX","inclinaDX"});
}
void setUI() {
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
BitmapText ch = new BitmapText(guiFont, false);
ch.setSize(guiFont.getCharSet().getRenderedSize());
ch.setText("W, E, Space, LeftMouse, MiddleMouse, RightMouse"); // crosshairs
ch.setColor(new ColorRGBA(1f, 0.8f, 0.1f, 1f));
ch.setLocalTranslation(settings.getWidth() * 0.3f, settings.getHeight() * 0.1f, 0);
guiNode.attachChild(ch);
}
void setCam() {
flyCam.setEnabled(false);
cam.setFrustumFar(30000);
}
void setShip(){
shipSpatial = assetManager.loadModel("Models/Wraith Raider Starship/Wraith Raider Starship.j3o");
ship = (Node)shipSpatial;
ship.setLocalScale(0.01f);
ship.setLocalTranslation(new Vector3f(2000,0,500));
rootNode.attachChild(ship);
CollisionShape shape = CollisionShapeFactory.createBoxShape(shipSpatial);
RigidBodyControl nave_phys = new RigidBodyControl( shape , 100f );
nave_phys.setKinematic(false);
nave_phys.setFriction(100f);
nave_phys.setAngularDamping(0.8f);
nave_phys.setPhysicsLocation(new Vector3f(2000,0,500));
ship.addControl(nave_phys);
bulletAppState.getPhysicsSpace().add(nave_phys);
}
void setStars(){
AmbientLight ambient = new AmbientLight();
ambient.setColor(ColorRGBA.White);
rootNode.addLight(ambient);
final ParticleEmitter stars = new ParticleEmitter("Emitter", Type.Point, 90000);
stars.setShape(new EmitterSphereShape(Vector3f.ZERO, 100000f));
stars.setGravity(0, 0, 0);
stars.setLowLife(60);
stars.setHighLife(600);
stars.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
stars.setImagesX(15);
stars.setStartSize(0.05f);
stars.setEndSize(0.05f);
stars.setStartColor(ColorRGBA.White);
stars.setEndColor(ColorRGBA.White);
stars.setSelectRandomImage(true);
stars.emitAllParticles();
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
mat.setBoolean("PointSprite", true);
//mat.setTexture("Texture", assetManager.loadTexture("Effects/Smoke/Smoke.png"));
stars.setMaterial(mat);
rootNode.attachChild(stars);
}
void setSaturnRing(){
saturnRing = new Geometry("Anello", new Cylinder(100,100,141000/1500,0.5f,true,true));
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setTexture("ColorMap", assetManager.loadTexture("Textures/full_saturn_ring.png"));
saturnRing.rotate((float)Math.toRadians(270), 0f, 0f);
saturnRing.setMaterial(mat);
rootNode.attachChild(saturnRing);
}
void setPlanets(){
planets = new Pianeti();
time = 1;
Sphere[] sfere = new Sphere[10];
planetsGeometries = new Geometry[10];
Material mat;
for(int i = 0; i < sfere.length;i++)
{
if(i==0)
{
mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setTexture("ColorMap", assetManager.loadTexture("Textures/texture_sun.jpg"));
sfere[i] = new Sphere(100,100,(int)planets.getGrandezzaStella());
planetsGeometries[i] = new Geometry("Sole", sfere[i]);
PointLight sole = new PointLight();
sole.setPosition(planetsGeometries[i].getLocalTranslation());
sole.setColor(ColorRGBA.White);
rootNode.addLight(sole);
}
else
{
mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/"+planets.getTexture(i-1)+".jpg"));
sfere[i] = new Sphere(100, 100, (int)planets.getGrandezzaCorpo(i-1));
planetsGeometries[i] = new Geometry("Sphere", sfere[i]);
}
planetsGeometries[i].setMaterial(mat);
planetsGeometries[i].rotate((float)Math.toRadians(270), 0f, 0f);
rootNode.attachChild(planetsGeometries[i]);
}
}
void setLeap()//-Djava.library.path=K:/MilkyWay/assets/Natives
{
// Create a sample listener and controller
listener = new LeapMotionListener();
controller = new Controller();
controller.enableGesture(Gesture.Type.TYPE_SWIPE);
// Have the sample listener receive events from the controller
controller.addListener(listener);
// Remove the sample listener when done
controller.removeListener(listener);
}
/*------------------------------------------------------------------------------
*--- AUXILIARY METHODS
*------------------------------------------------------------------------------
*/
Quaternion toQuaternion(float x, float y, float z)
{
Quaternion aus;
double h = y*Math.PI/360;
double a = z*Math.PI/360;
double b = x*Math.PI/360;
double c1 = Math.cos(h);
double c2 = Math.cos(a);
double c3 = Math.cos(b);
double s1 = Math.sin(h);
double s2 = Math.sin(a);
double s3 = Math.sin(b);
float qw = (float)(c1*c2*c3 - s1*s2*s3);
float qx = (float)(s1*s2*c3 + c1*c2*s3);
float qy = (float)(s1*c2*c3 + c1*s2*s3);
float qz = (float)(c1*s2*c3 - s1*c2*s3);
aus = new Quaternion(qx,qy,qz,qw);
return aus;
}
protected void rotateCamera(Camera cam, float value, float tpf, Vector3f centre){
float rotationSpeed = 1000;
cam.setLocation(ship.getLocalTranslation());
Vector3f axis = cam.getUp();
Matrix3f mat = new Matrix3f();
mat.fromAngleNormalAxis(rotationSpeed * value * tpf, axis);
Vector3f upDir = cam.getUp();
Vector3f leftDir = cam.getLeft();
Vector3f dir = cam.getDirection();
mat.mult(upDir, upDir);
mat.mult(leftDir, leftDir);
mat.mult(dir, dir);
Quaternion q = new Quaternion();
q.fromAxes(leftDir, upDir, dir);
q.normalizeLocal();
cam.setAxes(q);
dir = cam.getDirection();
dir = dir.mult(12.0f);
Vector3f invDir;
invDir = new Vector3f(-dir.x, -dir.y+3, -dir.z);
Vector3f position = invDir.add(centre);
cam.setLocation(position);
}
}