Do-later tasks and delta time

I’m reaching the point with my Panda3D game project at which I want to try to optimize various bits of the game. Right now I’m trying to improve speed and reduce overhead by converting some of my continuously repeating tasks to do-later, periodic tasks. I’m finding that the do-later tasks seem to conflict with the use of a delta time variable. Using dt is supposed to guarantee a constant rate of motion when processing speeds and FPS vary, but here the rates of motion vary even more wildly with delta time than without it.

Based on some tests, I assumed that the do-later tasks might already tick regularly, producing constant speeds in themselves and not being sensitive to FPS or processing speed variance. I assumed, then, that such tasks might not require the use of delta time. But my subsequent tests suggest otherwise. With or without delta time, the do-later tasks do not give me constant rates of motion. This is a bit of a puzzle, and I can’t find any information relating to such a problem in the Panda documentation, so I’ve come to the forum hoping someone might have some pointers.

What’s particularly puzzling here is that the do-later tasks seem to offer constant, unvaried results when I use them for anything other than driving motion. I have a task which flashes through a series of colors over and over, and this seems to happen at a constant rate, for instance. It just all sort of falls apart when I’m calculating motion rather than incrementing a variable or regularly flipping a Boolean value.

Does anyone have any thoughts or insight about this? I’m sure my enemy tasks can run more efficiently, if I can get constant rates of motion out of do-later tasks.

I think I’ve found the solution to this problem. Within the do-later task, task.delayTime should be used as the delta time value. The do-later task does run at a regular rate, and that rate itself needs to be used to regulate the speed of motion.

Using globalClock.getDt() for delta time fails within a do-later task, because it reports the time since the last frame was rendered and not the time since the task was last run. Calculating delta time manually using task.time and a storage variable fails in a do-later task, because task.time is reset to zero each time the task runs. Calculating dt manually using globalClock.getRealTime() also fails, for reasons that are less clear to me. :question:

Delta time needs to be handled differently for do-later tasks than for normal, continuous tasks. Strangely, this doesn’t seem to be documented anywhere.

Switching to using do-later tasks has generated real speed gains with my project. Previously I could run a testing version of the game with 20 bat-type enemies and 6 dragon-type enemies at 250-300 FPS. Now I can run 40 bat-type enemies and 20 dragon-types at 450-600 FPS. The CPU usage overhead for the game is also significantly reduced. :smiley:

Hmm. The solution I posted above seems correct, but only when the FPS exceeds an undetermined threshold. If I am using loadPrcFileData("",“sync-video #f”) and allowing the game to run fast, I see consistent rates of motion from the do-later tasks. This is true when I clamp the FPS using globalClock.setMode(ClockObject.MLimited) with globalClock.setFrameRate(100) and when I allow Panda to run as fast as it can.

However if I run the game at 60 FPS or lower, by deactivating loadPrcFileData("",“sync-video #f”) or using setFrameRate() at 60 or 30, all of the do-later tasks run slowly. Clearly, then, using task.delayTime() as dt is either an incomplete solution or it isn’t correct at all. It looks like it may be effective when dt would be small due to a high FPS, but once dt would be large enough to make a noticeable difference, the approach fails.

So somewhere in the mix proper delta time still needs to be accommodated, but how this needs to be approached is unclear. :neutral_face: :question:

I arrived at a final solution to this problem by using Python’s time module instead of Panda’s clock objects. Each do-later task needs to keep track of its own time, for the sake of maintaining the proper delta time. Each time the do-later task is called, it needs to store the time that the task was run, whether it uses that time to calculate dt or not. Then when dt does need to be used, it is calculated in the standard manner as

dt = current_time - stored_time

.