"Matrix cannot be inverted" error whilst using .setWalkDirection (Also code for swimming)

Hi all,
Recently, I have been trying to implement swimming in my game.
This is the code I have so far. It detects when a player is in water, and once it does, repurposes the jump key to a “swim key”.
What I wanted, was that if you are submerged, and press the swim key, for the player to move upwards to the surface of the water, and stick to the top of it, whilst being able to move around. Then sink if the swim key was released. What i attempted to do was give the player a walk direction that moves them upwards, however, upon testing if the swim key is held down, the game crashed and produces the following error.
If anyone could please explain this error, and help me fix it, or even suggest a better way, to make the character surface the water, please let me know.
All help will be apprecieated.

Code:

  if ((player.onGround() != true) && (player.getPhysicsLocation().y <= -18f)) {
      Waterborn = true;     
      
      if (jump) {
            player.setWalkDirection(new Vector3f(0f,2f,0f));
        }
      
  } else { 
      Waterborn = false;
            
      if (jump) {
            player.jump();
        }
      
  } 

Error:

java.lang.ArithmeticException: This matrix cannot be inverted
	at com.jme3.math.Matrix4f.invert(Matrix4f.java:1474)
	at com.jme3.math.Matrix4f.invert(Matrix4f.java:1446)
	at com.jme3.water.WaterFilter.preFrame(WaterFilter.java:154)
	at com.jme3.post.FilterPostProcessor.preFrame(FilterPostProcessor.java:326)
	at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:957)
	at com.jme3.renderer.RenderManager.render(RenderManager.java:1029)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:252)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
	at java.lang.Thread.run(Thread.java:744)

You appear to be using JME version 3.0. I suggest you upgrade to version 3.1, which doesn’t have this issue.

In fairness, I haven’t updated jMonkey since I initially downloaded it, I’ll try and update and report back my findings. Thanks for the advice!

1 Like

I do wonder how the newer version solves it, though… since the issue is that the walk direction is the same as the up vector and so the rest of the matrix cannot be properly created.

You might be better off just giving an up force to the character when swimming is on and let them continue to swim in the direction they are facing (perhaps exactly the direction they are facing but then you still run into the same issue if they look straight up).

1 Like

How do you give forces to Character Control, as I researched this and couldn’t find a solution.

I guess character control doesn’t expose the actual rigid body. You might be able to fake it by changing gravity:
http://javadoc.jmonkeyengine.org/com/jme3/bullet/objects/PhysicsCharacter.html#setGravity-float-

I’ve already tried this, and due to the nature of my if statement, it causes the “Waterborn” Boolean to constantly flicker between true and false which means gravity is constantly being changed. Although this doesn’t happen when the gravity is not changed.

UPDATE:
I have managed to do a lot of tweaking, and have come up with a solution to the swimming code.

if ((player.onGround() != true) && (player.getPhysicsLocation().y <= -19)) {
      Waterborn = true;     
      
      state.setText("sinking");
      player.setGravity(5);//values for sinking
      player.setFallSpeed(12);
      player.setJumpSpeed(19);
      
      if (jump) {
          state.setText("swimming");
          player.setGravity(-50);//values for swimming
          player.setFallSpeed(12);
          player.setJumpSpeed(5);
      }
  } 
  
  if (player.getPhysicsLocation().y >=-20) {
      Waterborn = false;
         
      state.setText("land");
      player.setGravity(50);//values for land
      player.setFallSpeed(70);
      player.setJumpSpeed(19);
      
      if (jump && !(Airborn) && !(Waterborn) && player.getPhysicsLocation().y > -17) {
            player.jump();
        }
  }

  
  if ((Waterborn == false) && (player.onGround() != true)){
      Airborn = true;
  }else{Airborn = false;}

Hope this is useful to people.

1 Like

I haven’t tried this yet but underwater and swimming are something I am interested in…

What about using a ghost control and check for overlapping?

If using SimpleWaterProcessor, can you attach a control with as close a shape to the underwater environment, like a BoxCollisionShape and add one to spatial and when overlapping do whatever?

Edit: Actually, could just use a node for the control and place it wherever you wanted for swimming, regardless of water type.