Hello,
I have two projects. In both I use JME3. Sometimes my character falls thought other physics bodies.
The character can be a capsule or a car.
I publish a video in my closed channel and a screenshot of one of my test projects. Maybe I should change something (physic calculations quality or something else).
In video and screenshot I use Minnie for the physics simulation.
What can be reason of this? If the code is important it is bellow:
public class Main extends SimpleApplication implements ActionListener {
private Joystick viewedJoystick;
private Node joystickInfo;
private float yInfo = 0;
private JoystickButton lastButton;
private BulletAppState bulletAppState;
private Spatial autoModel;
private VehicleControl vehicle;
private final float accelerationForce = 1000.0f;
private final float brakeForce = 100.0f;
private float steeringValue = 0;
private float accelerationValue = 0;
final private Vector3f jumpForce = new Vector3f(0, 3000, 0);
private Node scene;
private PointLight red, blue;
private final Vector3f redPos = new Vector3f();
private final Vector3f bluePos = new Vector3f();
private Vector3f redDir = new Vector3f();
private Vector3f blueDir = new Vector3f();
private final Vector3f LIGHT_OFFSET_TO_BLUE = new Vector3f(0.5f,-2,0f);
private final float lightsAlpha = 0.75f;
private boolean throttle;
public static void main(String[] args) {
Main app = new Main();
AppSettings settings = new AppSettings(true);
settings.setUseJoysticks(true);
app.setSettings(settings);
app.start();
}
@Override
public void simpleInitApp() {
scene = (Node)assetManager.loadModel("Scenes/Scene_2.j3o");
rootNode.attachChild(scene);
flyCam.setMoveSpeed(flyCam.getMoveSpeed()*30f);
loadPhysic();
Node terrain = (Node)scene.getChild("Terrain");
terrain.addControl(new RigidBodyControl(0));
bulletAppState.getPhysicsSpace().addAll(terrain);
bulletAppState.getPhysicsSpace().setGravity(bulletAppState.getPhysicsSpace().getGravity(redPos).mult(4));
setupKeys();
buildPlayer();
loadLights();
terrain.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
loadShield();
loadGamepad();
}
private void setupKeys() {
inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_LEFT));
inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_RIGHT));
inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_UP));
inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_DOWN));
inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addListener(this, "Lefts");
inputManager.addListener(this, "Rights");
inputManager.addListener(this, "Ups");
inputManager.addListener(this, "Downs");
inputManager.addListener(this, "Space");
inputManager.addListener(this, "Reset");
}
@Override
public void simpleUpdate(float tpf) {
Spatial child = autoModel;
child.setLocalTranslation(vehicle.getPhysicsLocation());
child.setLocalRotation(vehicle.getPhysicsRotation());
redPos.x = child.getLocalTranslation().x-LIGHT_OFFSET_TO_BLUE.x;
redPos.y = child.getLocalTranslation().y+LIGHT_OFFSET_TO_BLUE.y;
redPos.z = child.getLocalTranslation().z+LIGHT_OFFSET_TO_BLUE.z;
bluePos.x = child.getLocalTranslation().x+LIGHT_OFFSET_TO_BLUE.x;
bluePos.y = child.getLocalTranslation().y+LIGHT_OFFSET_TO_BLUE.y;
bluePos.z = child.getLocalTranslation().z+LIGHT_OFFSET_TO_BLUE.z;
red.setPosition(redPos);
blue.setPosition(bluePos);
flyCam.setEnabled(false);
}
@Override
public void simpleRender(RenderManager rm) {
//TODO: add render code
}
private void loadPhysic() {
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
bulletAppState.getPhysicsSpace().setAccuracy(1f/60f);
}
private void buildPlayer() {
Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.getAdditionalRenderState().setWireframe(true);
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
mat.setTransparent(true);
mat.setColor("Color", ColorRGBA.Red);
CompoundCollisionShape compoundShape = new CompoundCollisionShape();
BoxCollisionShape box = new BoxCollisionShape(new Vector3f(1.0f, 0.5f, 2.4f));
compoundShape.addChildShape(box, new Vector3f(0, 1, 0));
//create vehicle node
Node vehicleNode =new Node("vehicleNode");
vehicle = new VehicleControl(compoundShape, 150);
vehicleNode.addControl(vehicle);
float stiffness = 60.0f;//200=f1 car
float compValue = .3f; //(should be lower than damp)
float dampValue = .4f;
vehicle.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));
vehicle.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));
vehicle.setSuspensionStiffness(stiffness);
vehicle.setMaxSuspensionForce(10000.0f);
//Create four wheels and add them at their locations
Vector3f wheelDirection = new Vector3f(0, -1, 0); // was 0, -1, 0
Vector3f wheelAxle = new Vector3f(-1, 0, 0); // was -1, 0, 0
float radius = 0.55f;
float restLength = 0.40f; //dist to the wheel from the body
float yOff = 0.65f;
float xOff = 2.095f;
float zOff = 2f;
Cylinder wheelMesh = new Cylinder(16, 16, radius, radius * 2.6f, true);
wheelMesh.setMode(Mesh.Mode.Points);
Node node1 = new Node("wheel 1 node");
Geometry wheels1 = new Geometry("wheel 1", wheelMesh);
node1.attachChild(wheels1);
wheels1.rotate(0, FastMath.HALF_PI, 0);
wheels1.setMaterial(mat);
//wheels1.
vehicle.addWheel(node1, new Vector3f(-xOff, yOff, zOff),
wheelDirection, wheelAxle, restLength, radius, true);
Node node2 = new Node("wheel 2 node");
Geometry wheels2 = new Geometry("wheel 2", wheelMesh);
node2.attachChild(wheels2);
wheels2.rotate(0, FastMath.HALF_PI, 0);
wheels2.setMaterial(mat);
vehicle.addWheel(node2, new Vector3f(xOff, yOff, zOff),
wheelDirection, wheelAxle, restLength, radius, true);
Node node3 = new Node("wheel 3 node");
Geometry wheels3 = new Geometry("wheel 3", wheelMesh);
node3.attachChild(wheels3);
wheels3.rotate(0, FastMath.HALF_PI, 0);
wheels3.setMaterial(mat);
vehicle.addWheel(node3, new Vector3f(-xOff, yOff, -zOff),
wheelDirection, wheelAxle, restLength, radius, false);
Node node4 = new Node("wheel 4 node");
Geometry wheels4 = new Geometry("wheel 4", wheelMesh);
node4.attachChild(wheels4);
wheels4.rotate(0, FastMath.HALF_PI, 0);
wheels4.setMaterial(mat);
vehicle.addWheel(node4, new Vector3f(xOff, yOff, -zOff),
wheelDirection, wheelAxle, restLength, radius, false);
vehicleNode.attachChild(node1);
vehicleNode.attachChild(node2);
vehicleNode.attachChild(node3);
vehicleNode.attachChild(node4);
rootNode.attachChild(vehicleNode);
getPhysicsSpace().add(vehicle);
autoModel = (Spatial)scene.getChild("Auto_2");
autoModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
vehicleNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
vehicle.setPhysicsLocation(autoModel.getLocalTranslation());
ChaseCamera chaseCam = new ChaseCamera(cam, autoModel, inputManager);
chaseCam.setSmoothMotion(true);
chaseCam.setDefaultVerticalRotation(3.14f/12f);
}
private PhysicsSpace getPhysicsSpace(){
return bulletAppState.getPhysicsSpace();
}
@Override
public void onAction(String binding, boolean value, float tpf) {
if (binding.equals("Lefts")) {
if (value) {
steeringValue += .25f;
} else {
steeringValue += -.25f;
}
vehicle.steer(steeringValue);
} else if (binding.equals("Rights")) {
if (value) {
steeringValue += -.25f;
} else {
steeringValue += .25f;
}
vehicle.steer(steeringValue);
} else if (binding.equals("Ups")) {
if (value) {
accelerationValue += accelerationForce;
} else {
accelerationValue -= accelerationForce;
}
vehicle.accelerate(accelerationValue);
} else if (binding.equals("Downs")) {
if (value) {
vehicle.brake(brakeForce);
} else {
vehicle.brake(0f);
}
} else if (binding.equals("Space")) {
if (value) {
vehicle.applyImpulse(jumpForce, Vector3f.ZERO);
}
} else if (binding.equals("Reset")) {
if (value) {
System.out.println("Reset");
vehicle.setPhysicsLocation(Vector3f.ZERO);
vehicle.setPhysicsRotation(new Matrix3f());
vehicle.setLinearVelocity(Vector3f.ZERO);
vehicle.setAngularVelocity(Vector3f.ZERO);
vehicle.resetSuspension();
} else {
}
}
}
private void loadLights() {
red = new PointLight(new Vector3f(), new ColorRGBA(1,0,0,lightsAlpha));
blue = new PointLight(new Vector3f(), new ColorRGBA(0,0,1,lightsAlpha));
float LIGHTS_RADIUS = 3000;
red.setRadius(LIGHTS_RADIUS);
blue.setRadius(LIGHTS_RADIUS);
try{
System.out.println(" Start to load lights ");
var lights = scene.getLocalLightList();
for (int i = 0; i < lights.size(); i++){
if (lights.get(i) instanceof PointLight point){
PointLightShadowRenderer sunShadowRenderer = new PointLightShadowRenderer(getAssetManager(), 1024*2);
sunShadowRenderer.setLight(point);
viewPort.addProcessor(sunShadowRenderer);
System.out.println("Light added");
}
else if (lights.get(i) instanceof DirectionalLight point){
DirectionalLightShadowRenderer sunShadowRenderer = new DirectionalLightShadowRenderer(getAssetManager(), 1024*2,2);
sunShadowRenderer.setLight(point);
viewPort.addProcessor(sunShadowRenderer);
System.out.println("Dir Light added");
}
}
}
catch (Exception e){
System.out.println("Can not load shadows. " + e.getLocalizedMessage());
}
}
private void loadShield() {
scene.getChild("Shield").setShadowMode(RenderQueue.ShadowMode.Cast);
}
private void loadGamepad() {
Joystick[] joysticks = inputManager.getJoysticks();
if (joysticks == null)
throw new IllegalStateException("Cannot find any joysticks!");
try {
PrintWriter out = new PrintWriter( new FileWriter( "joysticks-" + System.currentTimeMillis() + ".txt" ) );
dumpJoysticks( joysticks, out );
out.close();
} catch( IOException e ) {
throw new RuntimeException( "Error writing joystick dump", e );
}
int gamepadSize = cam.getHeight() / 2;
float scale = gamepadSize / 512.0f;
joystickInfo = new Node( "joystickInfo" );
joystickInfo.setLocalTranslation( 0, cam.getHeight(), 0 );
// Add a raw listener because it's easier to get all joystick events
// this way.
inputManager.addRawInputListener( new JoystickEventListener() );
// add action listener for mouse click
// to all easier custom mapping
inputManager.addMapping("mouseClick", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if(isPressed){
}
}
}, "mouseClick");
}
protected void dumpJoysticks( Joystick[] joysticks, PrintWriter out ) {
}
protected void addInfo( String info, int column ) {
BitmapText t = new BitmapText(guiFont);
t.setText( info );
t.setLocalTranslation( column * 200, yInfo, 0 );
//joystickInfo.attachChild(t);
yInfo -= t.getHeight();
}
protected void setViewedJoystick( Joystick stick ) {
if( this.viewedJoystick == stick )
return;
if( this.viewedJoystick != null ) {
joystickInfo.detachAllChildren();
}
this.viewedJoystick = stick;
if( this.viewedJoystick != null ) {
// Draw the hud
yInfo = 0;
addInfo( "Joystick:\"" + stick.getName() + "\" id:" + stick.getJoyId(), 0 );
yInfo -= 5;
float ySave = yInfo;
// Column one for the buttons
addInfo( "Buttons:", 0 );
for( JoystickButton b : stick.getButtons() ) {
addInfo( " '" + b.getName() + "' id:'" + b.getLogicalId() + "'", 0 );
}
yInfo = ySave;
// Column two for the axes
addInfo( "Axes:", 1 );
for( JoystickAxis a : stick.getAxes() ) {
addInfo( " '" + a.getName() + "' id:'" + a.getLogicalId() + "' analog:" + a.isAnalog(), 1 );
}
}
}
private void log(String log){
System.out.println("DATA: " + log);
}
protected class JoystickEventListener implements RawInputListener {
final private Map<JoystickAxis, Float> lastValues = new HashMap<>();
@Override
public void onJoyAxisEvent(JoyAxisEvent evt) {
Float last = lastValues.remove(evt.getAxis());
float value = evt.getValue();
// Check the axis dead zone. InputManager normally does this
// by default but not for raw events like we get here.
float effectiveDeadZone = Math.max(inputManager.getAxisDeadZone(), evt.getAxis().getDeadZone());
if( Math.abs(value) < effectiveDeadZone ) {
if( last == null ) {
// Just skip the event
return;
}
// Else set the value to 0
lastValues.remove(evt.getAxis());
value = 0;
}
setViewedJoystick( evt.getAxis().getJoystick() );
//gamepad.setAxisValue( evt.getAxis(), value );
log("Axis event: " + value + "; Axis: " + evt.getAxis().getName());
if (evt.getAxis().getName().equals("0")){
log("Axis!");
if (value>(-0.1f) && value < 0.1f) {
steeringValue= 0f;
} else {
steeringValue= value/4f;
}
vehicle.steer(steeringValue);
}
if( value != 0 ) {
lastValues.put(evt.getAxis(), value);
}
}
@Override
public void onJoyButtonEvent(JoyButtonEvent evt) {
setViewedJoystick( evt.getButton().getJoystick() );
}
@Override
public void beginInput() {}
@Override
public void endInput() {}
@Override
public void onMouseMotionEvent(MouseMotionEvent evt) {}
@Override
public void onMouseButtonEvent(MouseButtonEvent evt) {}
@Override
public void onKeyEvent(KeyInputEvent evt) {}
@Override
public void onTouchEvent(TouchEvent evt) {}
}
}
I have enabled the debug mode and recorded a new video.