In my game I have a situation where I need to generate a list of items and then move my camera to a new position via an interval. It seems the generation takes few dozen frames, when panda is completely frozen and that’s OK. The problem is when the generation process finishes, it appears the interval has already been ran and camera is in the new position, as if Panda had “lost focus” a short time even after everything was processed. So the next frame my camera is rendered already in the new position, the player doesn’t see any smooth animation, which is bad. This problem wasn’t present in the original game we are porting.
I thought I could run my interval in a sequence, after a Wait() function to prevent this, but it turned out to be a bad idea as sometimes when not much was needed to be generated, the player now had to wait a bit after the generation process for the camera animation to start.
I made a simple script to demonstrate the problem:
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
env = loader.loadModel('environment')
# something heavy on the CPU
for i in range(9999999):
# run the interval after
posival = LerpPosInterval(base.cam, 0.4, (0,base.cam.getY()-12,0), base.cam.getPos())
Now comment the for loop and run the code again, now you should be able to see the animation. If your PC is faster than mine, you can add an extra digit to see what I mean.
I found a similar issue posted before: panda3d.org/forums/viewtopic … 2036#52036
I tried ynjh_jo’s suggestion of adding these lines before and after the “loop”
I’m not completely sure what they do, but they didn’t seem to help.
The problem is that everything that happens within one frame is deemed to happen at the same time. This is the “frame time” of the clock object–it is the time as of the start of the frame, and everything you do within that frame is deemed to have happened at the “frame time”.
This is usually a good thing, because it makes the simulation internally consistent. Frames are atomic. If you start five animations in a row with five different calls to actor.start(), you want them all to have “started” at the exact same time, not within a few milliseconds of each other. If you start an interval, you also want it to have started at the same time as every other atomic operation in that frame.
The problem is when you have a single really long frame. In this case, anything you do at the end of this long frame is considered to have actually happened at the beginning of the frame, and when the next frame rolls around (after some considerable time has elapsed from the previous frame), Panda has to skip over all of the intervening time to catch up, and you miss seeing the first part of your interval.
There are several easy solutions. One is to munge the clock while you’re computing your slow frame so that it doesn’t actually allow time to advance during this period. I guess this is the idea behind setting the clock mode around the loop. I’m not sure whether that’s sufficient, though–the right thing to do is probably just to do something like:
after the loop. This simply resets the “frame time” to whatever the current real time is towards the end of your long frame. This will break the atomic-frame rule for that one frame, but in this case that’s what you want to happen.
Another approach, that doesn’t involve explicitly munging the clock, would be simply to wait to start the interval until the next frame, for instance with a doMethodLater().
Thank you, that did the trick
I’ve came across this issue many times before but never thought of asking here. I think others have too. Do you think it would be a good idea to document this, for instance in the manual?
EDIT: BTW, is it possible to disable a particular warning message (the one you get when calling setFrameTime)?
taskMgr.doMethodLater(0, func, ‘taskName’) will run func(task) the next frame. So, you can use this in conjunction with a Python lambda function (for instance) to start the interval next frame instead of this frame:
taskMgr.doMethodLater(0, lambda task, posival = posival: posival.start(), 'startIterval')
If you’re not comfortable with Python’s lambda syntax, you can do the same thing with a method:
taskMgr.doMethodLater(0, self.startInterval, 'startInterval', extraArgs = [posival])
def startInterval(self, posival):
This is all just basic Python coding, though, and in general we don’t try to teach Python in the Panda3D manual (it’s too big a topic). But I don’t see any reason why there shouldn’t be a helpful example like the above. Which page do you think it belongs on?
You can disable any of Panda’s notify messages by setting the appropriate notify category to the next higher severity level in the Config.prc file. In this case:
The severity levels are, in order: spam, debug, info (the word “info” is not printed), warning, error, fatal.
I didn’t mean this, I meant using globalClock.setFrameTime(globalClock.getRealTime()) or doMethodLater to solve a problem like this, as it seems like a common problem to me.
I’m not sure, I wanted to ask you. If I’m not mistaken, there is a page about globalClock in the manual.
OK, but adding a little tip to that page won’t be very helpful for the people who are searching the manual to solve this particular problem. How will they know they need to look at a page about globalClock?
Then it could go in a “faq”/“common problems” page or in it’s own page somewhere.
Sounds like a fine idea. Would you like to make it so?
The FAQ page? panda3d.org/manual/index.php/FAQ
Or in it’s own? I’m not sure where that one should go though.
I share your uncertainty about where a new page belongs. The FAQ is a fine place for now.