Game Development Community

Heartbeat (fixed hertz reoccuring tick)

by Fenrir Wolf · in Torque Game Builder · 07/21/2005 (6:04 pm) · 27 replies

Lately, I've found the need for a heartbeat function in T2D. While I could simulate it with a reoccuring schedule, I am never sure how accurate the schedules are on a fast, reoccuring basis.

There's fxSceneGraph2D::onSceneUpdate, but this is only called once per frame. Which can cause wildly differing results depending on the ingame framerate. Currently, my logic lives in onSceneUpdate and when the game's frame rate drops, everything is thrown out of whack.

I've been poking around in DemoGame::processTimeEvent and have been tempted to add some kind of heartbeat or per-tick script function call, but I was curious what everybody's thoughts are on this? I need my heartbeat event to be called at an exact rate, regardless of ingame framerate. (I am syncronizing things based on this event.)
Page«First 1 2 Next»
#21
08/26/2005 (1:40 pm)
Quote:It works good for single-fire events, but there's an issue with using it for repeating events. For some reason, a playAnimation() call will not work inside of a onAnimationEnd() callback.

If only there was an onAnimationCycle callback as well... But Melv has said that the animation system has much more to offer down the road.

I wonder if you could use two animations, so that one never calls its own play from within the onAnimationEnd, but they cycle back and forth. But we're way into hackville here ;)

Excellent work on the onUpdateScene mods. I wonder if that was an oversight. It does seem like this callback should occur even when the sim is in catch-up mode. Your mod makes it much more reliable and predictable.

I've made many uses so far of the animation-based scene timer, but haven't yet needed a cycling timer, just one-shot. So I didn't run into the problem you mention. I do have a number of long-term, time-based systems, but I'm using the onUpdateScene callback to support these. What I've done is similar to what you're doing, but my goals are different so I was able to reach a solution entirely in script, using the onUpdateScene callback as originally designed. The main difference is that my time-based systems key off of scene time rather than a "heartbeat". OnUpdateScene passes the scene time parameter, so my systems are always in synch. I've never actually had a problem with the catch-up behavior, but that's because the effect of a lag is different when a system is keyed on time rather than cycles. My system might skip a large section of the simulation, and might look bad on screen, but it will still be right where it needs to be at any given instant.

Here's an example of what I'm talking about. I have a generic system for tossing an object (my game is top-down, where yours is profile, so there are going to be major differences in what we need) where you can pass the object, set a few parameters, and let it go. I use OnUpdateScene to call the function that supports all of these kinds of systems, OnCompute, passing the object and the time. OnCompute then switches out to specific callbacks for each system type. In this case, I hit my handleThrowObject callback. See the next post for my handleThrowObject code...
#22
08/26/2005 (1:40 pm)
function handleThrowObject( %obj, %time )
{
  %delT = (%time-%obj.compute.start);

  if ( %delT < %obj.compute.duration )
  {
    %px = %obj.compute.startX+%obj.compute.velocityX*%delT;
    %py = %obj.compute.startY+%obj.compute.velocityY*%delT;

    %obj.setPosition( %px SPC %py );

    // Set my size
    %factor = (1+mSin( 3.142/%obj.compute.duration*%delT ));
    %newSizeX = %obj.compute.startSizeX*%factor;
    %newSizeY = %obj.compute.startSizeY*%factor;

    %obj.setSize( %newSizeX SPC %newSizeY );
  } else
  { // I'm done
    %obj.setPosition( %obj.compute.endX SPC %obj.compute.endY );
    %obj.setSize( %obj.compute.startSizeX SPC %obj.compute.startSizeY );

    delComputeObject( %obj );

    if ( !(%obj.compute.endThrow $= "" ) )
      call( %obj.compute.endThrow, %obj );
  }
}

When I initiate this throwing behavior for an object, that's where the compute class gets created and attached to the object, and then the object gets shoved into a compute list which can contain several kinds of OnCompute-based systems. This compute class also holds all relevant properties for this system.

So what I'm doing here... First I check for elapsed time by using the passed scene time and the start time that was saved at initialization of the system for this particular object. %delT is my time variable that gets applied to any time-based formulas that drive my systems.

The next thing I do is check to see if the lifetime of this event has passed. If so, I do some cleanup and call any endThrow event that was provided at initialization. If not, I update the state of my object based on the elapsed time. For this particular system, I want to simply move my object along a straight line at a specified velocity, and also give the illusion of a throw by changing the size of the object over the duration of the throw. It starts out at the normal size, but grows as you approach the middle of the throw, then starts to shrink, giving the illusion of a thrown object viewed from above.

Pretty simple, but this kind of approach can be used to create some pretty nice systems. I have systems that control object movement (both constant velocity and accelerating/decelerating), size, alpha, draw order, and that also trigger events at key points along the simulation.

This sort of approach can certainly be applied to systems that require a "tick". You could drive it with a sinusoid, for example. The tick could occur at every transition from negative to positive, or whatever. I'm already doing something like this for a system that simulates an object in orbit around another object. In order to appear 3D, as the orbiting object passes "under" the other object, I shift that object lower in the draw order, and as it passes "over, I shift it higher in the draw order. This draw order transition happens at key points along the elliptical motion, where the object is supposedly moving toward or away from the viewer. The effect is pretty nice, especially with a particle effect trailing off behind the orbiting object.
#23
08/29/2005 (5:08 pm)
@David, I just wanted to let you know that I'm not having any problems using the animation-based timer for periodic events. But I think the reason I don't have the problem you're having is because I actually create a new timer rather than attempt to reuse the existing one. So when the timer event fires, I process it, then create another timer. It seems to work well.
#24
09/29/2005 (9:21 am)
@David
Could you go into more depth on FPS syncronization? The scripting is easy enough but I am not sure of what values to start with. Do you set the limits and targets all within a specific range or equal?

Thank you

- Jesse
#25
12/30/2005 (11:20 pm)
@David, I'm not sure if this is still of interest to you, but I finally broke down and fired up a compiler to poke around in the engine this week and I added in a few of the things I've been wanting, including an fxSceneGraph2D::schedule() function for scheduling scene time based events. Even though the animation-based system was functional, I'm starting to optimize my game now, and the amount of overhead I was generating with such a large number of time events forced me to replace it with a proper timer system. I finally have complete confidence in my timer integration. They trigger when they're supposed to, even when the game is pushing the limits of the system it's running on.
#26
12/31/2005 (12:43 am)
This sounds great. Can you please post your engine modifications to a spot on TDN or as a resource? I would find the fxSceneGraph2D::schedule() function extremely handy.
#27
01/01/2006 (3:53 pm)
@Jason:

I'm going to nail down the details, then let Melv have a look at it. If it passes his inspection, I'll be happy to present it.

- JP
Page«First 1 2 Next»