Game Development Community

Best way to continually move a StaticShape?

by Phil · in Torque Game Engine · 01/06/2012 (5:50 am) · 7 replies

Hi all,

For my game, my aim is to have a 3D indicator to show how far I can fire my weapon (limited by a spherical radius). It's only shown to the controlling player (and therefore should only get rendered on the client).

I implemented this by calling setPosition() on a StaticShape in my engine's Player::interpolateTick() method. The problem is that this rendering seems sluggish. I tried doing the same thing via script and got the same result.

I looked through a few threads, and I think I've came to the conclusion that I'm limited to the game's packet send/receive rate (it seems like it, since I upped the rate to find it's a bit more smoother but not perfect by any means). One thread suggested that I use a different ShapeBase-derived object which I tried, but this made no difference.

I then found the "client-side TSStatic" resource which seems like it might help. But a TSStatic seems to only update the world box and not the actual rendered mesh when the position gets changed, so it's useless in that regard (it also doesn't have features I'd like that a StaticShape has, eg. dts animation).

Would this (possibly) mean I need something like a "client-side StaticShape"? I found this thread but I don't know if it's worth following the steps in each post. Would it prevent this unneccessary 'lag' in object updates that can occur between the local client/server?

I'm really unsure. Any suggestions? Should I take a completely different approach altogether? I'm really new to working with the source code, but I've had a fair bit of experience with the scripting language.

Cheers for any advice :)

- phil

#1
01/06/2012 (11:38 pm)
You might have some luck with the Path Shape resource. It might require some tinkering to get it working with TGE, looks like the resource was written in 2003.
#2
01/07/2012 (3:27 am)
It worked without any changes, but I don't think it's that helpful :(
However it would probably be useful in a networked scenario where you want the other clients to have some sort of smoothness...?

I tried replacing my setPosition() call with pushBack() and put a really high speed on it, but I just get the same delay. And after a few seconds I think the list of queued positions (I think the source calls them Knots) gets overloaded causing a fatal error.

Thanks for the suggestion though. I'm feeling more convinced that I do need a client-only object to do this now, so I might give what I linked a try.
#3
01/07/2012 (6:17 am)
StaticShape can be moved, but collision might not update correctly.

Try this:

function SceneObject::rotFromTransform(%obj, %transform)
{
   if(%transform $= "")
      %transform = %obj.getTransForm();
   // the last four words of an object's transform are the object's rotation
   %rotation = getWord(%transform, 3) SPC getWord(%transform, 4) SPC getWord(%transform, 5) SPC getWord(%transform, 6);
   return %rotation;
}

function SceneObject::StartMoveObject(%obj, %endpos, %time, %smoothness, %delay)
{
   if(isObject(%obj))
   {
      %startpos = %obj.getTransform();
      %diff = VectorSub(%endpos, %startpos);
      %numsteps = (%time/1000) * %smoothness;
      %interval = 1000 / %smoothness;
      %stepvec = VectorScale(%diff, (1/%numsteps));
      %numstepsleft = %numsteps;
      %currpos = %startpos;
      %obj.MoveObject(%startpos, %endpos, %numsteps, %numstepsleft, %stepvec, %currpos, %interval, %delay);
   }
}

function SceneObject::MoveObject(%obj, %startpos, %endpos, %numsteps, %numstepsleft, %stepvec, %currpos, %interval, %delay)
{
   %rot = rotFromTransform(%obj.getTransform());
   %currpos = VectorAdd(%currpos, %stepvec);

   %obj.setTransForm(%currpos SPC %rot);
   %numstepsleft--;
   if(%numstepsleft < 1)
      return;
   else
      %obj.schedule(%interval, "MoveObject", %startpos, %endpos, %numsteps, %numstepsleft, %stepvec, %currpos, %interval, %delay);
}
#4
01/07/2012 (7:24 am)
I have two suggestions.

1. If all you need to do is render a sphere around your character, you should just do that, instead of creating a new object. Modifying Player::renderObject to render a sphere dictated by some scripted property shouldn't be too difficult. For extra eye-candy you could lift the rendering code from the replicator classes, that sweep a transparent cylinder around the object.

2. If you want to have finer control over the thing's appearance, and you're really set on rendering a shape instance, you'd be best to use the existing mountObject system. As you've discovered, it's not enough to just keep sending position updates; this method will always be fighting the net code. If you have a look through the code related to mounting, you'll see that if a shape is mounted, it affects code in places like setTransform and (IIRC) getTransform. When you call getTransform to render a mounted object, it will return the transform of its mount node. It's the only way for an object to smoothly track its mount.

Only rendering for one client would be the trickiest part of this. I'm not familiar with how clients are identified in the engine, and the tricky part about using object IDs is that they're different on each client and the server.

If the client-side TSStatic resource already does what you need, then all I'd suggest is that you give it some methods to allow it to be mounted to another shape. All you need to do, really, is overload getTransform (or possibly getRenderTransform) and have it return its mount's location.
#5
01/07/2012 (7:09 pm)
Daniel: Maybe I should've been more clear about my aim (I can see what you mean by mounting a sphere, I like that idea too actually). Basically my idea is that a small node appears at the point you're firing at. If you know Team Fortress 2, think of the red dot the sniper makes on the wall; this is pretty much what I'd like to do.

Quote:If the client-side TSStatic resource already does what you need, then all I'd suggest is that you give it some methods to allow it to be mounted to another shape. All you need to do, really, is overload getTransform (or possibly getRenderTransform) and have it return its mount's location.
I hadn't actually tested the client-side TSStatic when I mentioned it, because I assumed that calling setTransform() on it would only update its world box, but I did try it, and it did actually render immediately on each tick like what I wanted! (I had to comment out the collision hull initialization stuff though in order for it to initialize - this object doesn't need collision anyway)

I think I'll need to find out how to interpolate the movement though since the change in position still isn't absolutely smooth (I think I'm limited by the amount I can move the camera within a single tick), so Steve's code should be useful for that.

Also I still would like to have the stuff a StaticShape lets me do in my original post, so I think I'll keep going with trying to get the client-side StaticShape to work. (so far I've gotten the server object to return an ID, but I don't think the client version of the object has an initialized datablock... I'm still playing around with it)

Thanks both for your input, I really appreciate it.
#6
01/08/2012 (3:50 am)
Oh, I see what you're doing. Sorry, I was barking up the wrong tree.

Quote:I think I'm limited by the amount I can move the camera within a single tick
If you're setting the position of your marker every tick, that's something like 32 times per second. If your frame rate is above that, you'll probably be seeing the jitter.

Though, I just realised you mentioned you're calling setPosition in interpolateTick(). This method is called every rendered frame, so it should work.
#7
01/08/2012 (5:39 am)
Quote:Though, I just realised you mentioned you're calling setPosition in interpolateTick(). This method is called every rendered frame, so it should work.
Sorry, I actually had setPosition() being called in Player::updateMove().

(what I did was THEN test it in interpolateTick() but it seemed like was just producing the same effect as updateMove(), so that's why I said the call was in interpolateTick() just so things sound slightly less confusing)

I've moved the call back into interpolateTick() now, but it still feels like I'm getting this 32 ms (I think) delay. Do you think there's some sort of limitation going on with ClientSideTSStatic's setPosition()? (well the next uppermost definition is in TSStatic)

I tried spamming console messages and changing $timeScale's value to ridiculously low values and it seemed like a message was always being printed at the same rate (ie. every frame) so it seems like interpolateTick() is fine.
Also just to hopefully ensure nothing extra weird is going on, setPosition() gets called if isClientObject() is true.
I then also tried something kinda hacky which probably isn't worth explaining :P

So now I think the only idea left is that there is a limitation with how a TSStatic updates its position. What do you think?

PS - I know this has been long-winded but I just want to give you an idea of everything I've tried.