[SOLVED] Game Slows down with low FPS

Hi



I have a weired problem with my game: The more FPS I have, the faster objects move and vice verca. Of course I use the tpf-value I get with the update-methods in the amount I move the objects along their directions, but still it occurs. I am using StandardGame and a Move-Controller like the ThrottleController in jme2, but I used the code and modified it to serve my needs (I wanted to inherit from it, but everything was private g). Here Is the most important code:


   @Override
   public void update(float time) {
      if(System.currentTimeMillis() < zeroEncountered + 50) return;
            
      float value = getThrust();
      if(isStopping) {
         if(currentSpeed > 0f) value = -1f;
         else value = 0f;
      }
      if(wasBoosting) {
         if(currentSpeed > currentMaxSpeed) value = -1f + (value < 0 ? value : -value);
         else wasBoosting = false;
      }
      float delta = time * currentAcceleration;
      float max = currentMaxSpeed;
      if(value > 0f) max = currentMaxSpeed * value;
      else if(currentSpeed == 0f) max = 0f;
      if(value < 0f) delta = -delta;
      else if(value == 0f) {
         if(currentSpeed > 0f) delta *= -breaking;
         else if(currentSpeed < 0f) delta *= breaking;
      }
      if((currentSpeed > 0f) && currentSpeed + delta < 0f) {
         currentSpeed = 0f;
         zeroEncountered = System.currentTimeMillis();
      } else if(currentSpeed < 0f && currentSpeed + delta > 0f) {
         currentSpeed = 0f;
         zeroEncountered = System.currentTimeMillis();
      } else currentSpeed += delta;
      if(currentSpeed > max && !wasBoosting) currentSpeed = max;
      if(currentSpeed != 0f) move();
   }
   
   protected void move() {
      currentDir = node.getLocalTranslation().clone();
      Vector3f forwardStep = node.getLocalRotation().getRotationColumn(2).mult(currentSpeed);
      node.getLocalTranslation().addLocal(forwardStep);
      currentDir = node.getLocalTranslation().subtract(currentDir);
   }



As you see, its pretty much the same like ThrottleController, but I added the option to boost and some other things. The important thing is, that the objects do not move in constant speed with different fps which should not happen, right? Am I doing something wrong or do I misunderstand something here? I hope you can help me :)

This line in your code is causing the issue:


float delta = time * currentAcceleration;



The time variable passed into the update method is time per frame (its called tpf elsewhere in the engine, StandardGame calls it time).  This basically means "how long since the last time the update method was called".

If you're trying to get the objects to move in a constant speed separate from your FPS, simply stop multiplying your acceleration by your FPS! :p (FPS = 1/time)

Looks like I have a completly different understanding of the whole thing. If you don't make it dependend from the tpf, then those update-methods will be called fewer per second when the FPS are low and more often when FPS are high, which causes objects to move slower or faster because their translation is changed more often per second, right? So the amount the objects are moved every frame should be dependend from the tpf, so a high tpf (which indicates low FPS because the last time its updated is far away, thus the tpf-value is high, right?) causes the object to move a bit more, so it is compensating for the low fps by simply moving further than at high fps.

At least this is how I understand it. Of course I tested your advice, but I get still the same results (fast objects at high fps, slow objects at low fps). So something is wrong g.

I sayuse a fixed logic update anyways, cause else at low fps for example a collision detection might not work, as the object teleports through the other one, if you sue a fixed logic update, you can determine the maximum speed an object is allowed to have to prevent this, also it will spare you many problems calculating stuff. I suggest 60 updates, aka 60 frames as most monitors won't display more anyway.

But how do you fix the number of "logic updates"? All update-methods are called in the OpenGL-Thread of StandardGame, and these calls do vary in time because of the FPS, right? So how to lock them?

there is a fixed logicrate game already in the jme, else you can:



time for one frame = 1000/60



save time,

do processing

do rendering

get dif to saved time

wait untill time for frame is reached with Thread.sleep()

This is really weird. I really don't understand why jme doesn't do this itself in every implementation of AbstractGame. I mean, it's totally basic and every normal game works like this, right? Why does StandardGame not implement this? Instead we got this fixed logicrate game-thing, which is totally useless because it doesn't implement the whole AbstractGame. My game is build around StandardGame with mutliple GameStates and even RenderPasses. What should I do now? Is there no way to use StandardGame and still have a fixed logic rate? Am I the only one here who has this kind of problem?

Do I have to implement my own Game-class now? I really don't understand that mess.

Sry for swearing around but this is just wrong in my eyes.

maybe your root problem is:

if(System.currentTimeMillis() < zeroEncountered + 50) return;



I'm not sure what zeroEncountered is, but maybe you could miss some updates because of it.

Fixed logic games can also cause problems, if the cpu is too slow to run at this fixed speed.

In StandardGame you can also set a 'preferred FPS', so that you always get max 60 FPS for example. (which is kinda like a fixed logic game).
But if the cpu cannot render 60 FPS per second the game will run slower then it should.

I know the preferred FPS option in StandardGame, but as you said thats not the way it should be. Even if you have 10 FPS, objects still should move at the same actual speed (though they will jump from frame to frame, but moving from A to B still does take the same time as with 60 FPS). So how do you do it? There must be a simple way since normal games work that way. Would be awful if in an multiplayer game one player has 30 FPS and objects move rather slow and in the same game another player has 80 FPS and objects move a lot faster - this just would not work (ok I ignored the syncing here, but you get the idea, right?). Why is jme basicly ignoring this and why are there no tutorials about that? O.o

Sry for complaining and swearing again, but this is really bothering me :confused:

Assuming you want an object to move at a constant speed regardless of framerate, you have the TPF parameter included with every update method for that purpose. TPF stands for "time per frame" and it is the time it took for the last frame to complete in seconds. If you want your object to move X units per second, multiply X by the TPF to get the amount of units to move per frame.



Using System.currentTimeMillis() is strongly discouraged in jME applications because this method is inaccurate in some operating systems and because jME already provides a facility for tracking time with the Timer class.



Other than that I can't help you, the code looks very confusing to me.

Momoko_Fan said:

Assuming you want an object to move at a constant speed regardless of framerate, you have the TPF parameter included with every update method for that purpose. TPF stands for "time per frame" and it is the time it took for the last frame to complete in seconds. If you want your object to move X units per second, multiply X by the TPF to get the amount of units to move per frame.


This is exactly what I was doing the whole time (and this is what I explained before, and also that's what's happening in my code above), but still I get the (very significant) effect that objects do not move constantly fast at different FPS.

Momoko_Fan said:

Other than that I can't help you, the code looks very confusing to me.


This code is essentially in ThrottleController in jme2 :)

Ok here is my implementation of moving objects and I think it is working quite fine (although I didn't really reach lower fps than 60 to confirm that works fine in any situation :)):


   public void update(float tpf)
   {
      if (currentState == State.MOVING)
      {
         float distance = moveSpd * tpf;
         float remainingDist = getLocalTranslation().distance(endPos);
         if (distance >= remainingDist)
         {
            setLocalTranslation(endPos.clone());
            currentState = State.END_POS;
         }
         else
         {
            getLocalTranslation().interpolate(endPos, distance / remainingDist);
         }
      }
      else if (currentState == State.MOVING_BACK)
      {
         float distance = moveSpd * tpf;
         float remainingDist = getLocalTranslation().distance(startPos);
         if (distance >= remainingDist)
         {
            setLocalTranslation(startPos.clone());
            currentState = State.START_POS;
         }
         else
         {
            getLocalTranslation().interpolate(startPos, distance / remainingDist);
         }
      }
   }


Maybe the code needs some optimization, because in every update I practically:

  • calculate how much this object will move depending on his speed and tpf: float distance = moveSpd * tpf;

  • calculate the distance to his stopping point: float remainingDist = getLocalTranslation().distance(endPos);

  • if it will move more than it can, I just stop it, else I move it, based again on distance relations


Don't mind states, they are implementation dependant, also I should probably mention that startPos and endPos are Vector3f. :)
Hope it helps. :)

Thanks for all your responses, but I finally found my bug: I only multiply the acceleration with tpf, but not the resulting speed. So if my object is at max speed, tpf does not matter any more, because there is no acceleration, and each frame I add the same amount to the localtranslation of the object.

So now I simply multiply the speed with tpf, and now it seems that objects move (more or less) constantly fast at different frame rates.

Sry for complaining, turned out it was my own fault… :confused: Thanks for your replies anyway ^^.