Problem with SpatialTranslation timing

Having a bit of trouble getting SpatialTranslation to work like I want. I'm writing a multiplayer space-shooter game, where the server sends out updates on entities' (ships, missiles, bolts etc) positions and speeds every 0.1 second. On the client side, after a new update is received, I position each entity accordingly using a SpatialTransformer for each entity and make them move at the given "speed vector" for the following 0.1 seconds.



if (trans != null) {
   trans.setActive(false);
   model.removeController(trans);
}
trans = new SpatialTransformer(1);
trans.setObject(model, 0, -1);
model.addController(trans);

Vector3f p0 = new Vector3f(newPos);
trans.setPosition(0, 0, p0);
Vector3f p1 = new Vector3f(newPos.x + newSpeed.x * 0.1f, newPos.y + newSpeed.y * 0.1f, 0);
trans.setPosition(0, 0.1f, p1);
      
Quaternion x0 = new Quaternion();
x0.fromAngleAxis(dir, new Vector3f(0,0,1));
trans.setRotation(0, 0f, x0);
Quaternion x1 = new Quaternion();
x1.fromAngleAxis(newDir, new Vector3f(0,0,1));
trans.setRotation(0, 0.1f, x1);
trans.setRepeatType(Controller.RT_CLAMP);

trans.interpolateMissing();



First problem is that I seem to have to recreate the SpatialTransformer every time there's an update to the Entity. Not doing so results in the Entity jittering back and forth in position and rotation. I suspect that the SpatialTransformer simply keeps some of the old data, but I can't find any way to "reset" the transformer in the api.

The other (more serious) problem is that setting 0.1f in "trans.setPosition(0, 0.1f, p1)" doesn't seem to mean 0.1 second. The entities move forward much slower than they're supposed to, so they "skip forward" each time there's an update. I've tried lowering this argument to different values, but all of them seem to be a bit off. How exactly does that "time" argument work? Would be handy to be able to calculate what value should go there, instead of just trial-and-error until I get the almost correct value. :)

Thanks for any help!

Well what you need to do sounds very simple. Did you consider extending your own Controller for it?

Hmm actually no, I haven't considered that. SpatialTransformer sounded pretty much exactly like what I wanted. I'll look into writing my own.



Just for future reference tho, how does that time attribute work? And am I doing something wrong since I have to recreate the SpatialTransformer all the time?

Well, I'm not very familiar with it, but it's used to interpolate movement between a series different points, rotations, etc. Not quite what you need since you're moving more in one direction as I understand it.



As for the reusing of SpatialTransformer, in any case it keeps all the data after it's done. Calling update with a time big enough to pass the last key frame seems to reset it's internal clock though, so it seems to me it'll behave like RT_WRAP in all cases.



As for the timing problem… are you sure it's not taking 0.1 seconds? how big is the difference in your perception? The problem is not with your own code? (network delays or something) The only other think I could think of is the frametimes not adding up correctly. Do you run with a high FPS and if so, on what platform/JDK?

I've written a quick and dirty Controller to do this now, but oddly I got roughly the same result as with the SpatialTransformer, so I started doing some testing.



The network code is steady at one update packet every 0.1 second (sometimes lagged slightly to 0.12-0.13 seconds). However, when summing up timer.getTimePerFrame() between each update packet, I only get around 0.03 seconds. Very odd. I must have misconfigured the Timer somehow, but don't understand how. Most of my initialization is stolen straight from SimpleGame, the Timer being no exception:



timer = Timer.getTimer(properties.getRenderer());



timer.getResolution() reports 1000 ticks per second. timer.getTimePerFrame() generally returns values around 0.0015.
Running Win XP SP1 on an Athlon 1.4ghz with Geforce 4 TI 4200, JDK version 1.5.0. Checked out jME from CVS yesterday.

Hm, well it's what I suspected then… now the question is why.

I would try 2 things. First just do a simple calculation for the frame time yourself, using timer.getTime(), and don't use a "smoothed" approach like getTimePerFrame(). (just the time between 2 frames). If that doesn't work, put a Thread.sleep(5) in simpleUpdate so it doesn't go above 200 fps for now. When you're running above 500 fps at a 1000 tick resolution errors could start to creep in.

Manually calculating the frame time improved the timing problems, but didn't get really good until I added a Thread.sleep(5) as well. I ended up doing a quick FPS-cap by just doing Thread.sleep() dependant on how much time had passed since last frame. Not the best way to do it (since it slows down the current frame depending on the last frame's performance), but it works decently. And I want this thing up and running soon. :slight_smile:

Hm… well the alternative is some sort of self correcting timing mechanism, that checks how much time has elapsed and how much time has been used for updating geometric data. That should be a lot more reliable. However, since your controller only has a life time of 0.1 seconds, this will also do.

Here's the one I wrote. It only supports movement and rotation in the xy-plane, since that's all I need for my space shooter game. :slight_smile:



public class TimedSpatialController extends Controller {
   private Node node;
   private Vector3f startPos;
   private float startDir;
   
   private float totTime;
   private float curTime = 0;
   private float addX;
   private float addY;
   private float addDir;
   private Quaternion quat = new Quaternion();
   private static Vector3f rotVector = new Vector3f(0,0,1);

   public TimedSpatialController(Node node) {
      this.node = node;
      node.addController(this);
      setActive(false);
   }
   public TimedSpatialController(Node node, Vector3f start, Vector3f end, float startDir, float endDir, float time) {
      this.node = node;
      reinit(start, end, startDir, endDir, time);
      node.addController(this);
   }

   public void reinit(Vector3f start, Vector3f end, float startDir, float endDir, float time) {
      this.startPos = start;
      this.startDir = startDir;
      this.totTime = time;
      curTime = 0;

      addX = end.x - start.x;
      addY = end.y - start.y;

      float a = startDir;
      float o = endDir;
      float diff = Math.abs(a - o);
      if (diff <= Math.PI) {
         addDir = o - a;
      } else {
         if (a > o) {
            a -= FastMath.TWO_PI;
         } else {
            o -= FastMath.TWO_PI;
         }
         diff = Math.abs(a - o);
         addDir = o - a;
      }

      setActive(true);
   }

   public void release() {
      node.removeController(this);
      setActive(false);
   }

   public void update(float time) {
      if (!isActive()) {
         return;
      }
      curTime += time;
      if (curTime > totTime) {
         curTime = totTime;
      }

      float dTime = curTime / totTime;

      quat.fromAngleAxis(startDir + addDir * dTime, rotVector);

      node.setLocalTranslation(new Vector3f(startPos.x + addX * dTime, startPos.y + addY * dTime, 0));
      node.setLocalRotation(quat);
   }
}

i have the same timing problem…

is there another solution ???

(I'd like not to use Thread.sleep…)

olabomba said:

i have the same timing problem..
is there another solution ???
(I'd like not to use Thread.sleep...)


As I said, write your own timing, where you make sure the time you use for updating all added up together equals the amount of real time that has passed. I shouldn't be too hard to do that in a simple way. The problem is you won't have any smoothing (if you want to keep it simple) but since apperently you're running over a 100 frames too, that's not an issue.