Re: [pygame] Framerate Normalizer

2008-05-14 Thread Jake b
I think I understand what you are saying, if I go with the
fixed-time-steps method: I remove all 'delta's from my math, and I
save alot of headaches. I instead call the physics update() more or
less depending on the framerate?

I need some clarification on the pseudo code:

I put a couple of questions in the code:

# (1) Is this the right way to implement the const-time-step? / what's wrong?

# (2) Does this location of .time_left matter? ( ie: right after
.draw() vs. right before first call of .update() vs right after
Clock.tick() ? )

# (3) On howto make up time:

Thanks for any fixes:

# pseudo.py
class Game():
"""pseudo-code, my attempt at constant-time-steps updates"""
# ...
def needToDoAnotherUpdate(self):
"""My guess on what I need to calculate"""
# .time_left = time since last draw()
# .time_adjusted = time_left - number_of_updates * 
PHYSICS_TIME_STEP
# PHYSICS_TIME_STEP = time in MS of constant time between calls 
to .update()
if self.time_adjusted > self.PHYSICS_TIME_STEP:
self.time_adjusted -= self.PHYSICS_TIME_STEP
return True
else:
return False

def update(self):
while not self.bDone:
# events
self.handle_events()

self.time_adjusted = self.time_left

# (1) Is this the right way to implement? / what's 
wrong?
while self.needToDoAnotherUpdate():
# calculate all physics
self.update()

# else draw:
# draws everything
self.draw()

# (2) Does this location of .time_left matter?
#not sure If I should be getting time now, or right 
before
# the first call to .update(), OR after 
clock.tick() ?
self.time_left = pygame.time.get_ticks()

# Brian F said: make up for the time lost by extra 
simulation
updates by doing fewer draws.
# (3) Not sure how else: So I did this: cap FPS
self.clock.tick(30) # pygame.time.Clock.tick(fps)

-- thanks,
jake

On Sat, May 10, 2008 at 1:30 PM, Brian Fisher <[EMAIL PROTECTED]> wrote:
> My high-level advice is that the approach you are trying to use to achieving
> frame-rate independence is a math heavy and complex path to getting right,
> and the issue you are seeing here is just the first of many like it (and
> some worse). There is a much simpler approach, where all your simulation
> update code doesn't care at all about frame rate or time elapsed, but
> instead you call your simulation update routine the right amount of times
> for how much time has elapsed, and make up for the time lost by extra
> simulation updates by doing fewer draws.


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Ian Mallett
I made sure they were floats.
The new program is Asteroids 7.1 on pygame.org
I think the solution was mostly successful.
Ian


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Greg Ewing

Ian Mallett wrote:

((0.0075/(IdealFPS**2))*((IdealFPS/TargetFPS)**2))*(TargetFPS**2)
...doesn't work.


Be careful with data types and division. If IdealFPS and
TargetFPS are integers and you're not using future division,
then (IdealFPS/TargetFPS) will be doing integer division.

--
Greg


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Patrick Mullen
Yeah, fixed-time physics is the way to go.  How you get that fixed time is
up to you, but it's a much better way than trying to put time into all of
your code.  It is more stable, and easier to program as well.  Since you are
thinking about lower end computers, you may have to rebalance everything for
a lower fixed-time update.  (i.e, right now it runs good at 190 fps, but
that's probably not a good interval for a slower computer that can only run
60 fps).  I think most professional games run at around 30 hz or so.  I know
Doom3 did anyway, and that's the last I read about any game's update
function.  Less than 30 though is probably not so good.  On a really slow
computer, things will slow down, but you wouldn't want to skip too many
frames in that case anyway.  If I can only run at 10fps, I would prefer the
game to update a little slower (slow motion) to having objects be warping
around.


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Ian Mallett
I think the best option is to call the update etc. functions the correct
number of times.  The thrust still seems a little off--slightly slower at
slower framerates.


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Brian Fisher
My high-level advice is that the approach you are trying to use to achieving
frame-rate independence is a math heavy and complex path to getting right,
and the issue you are seeing here is just the first of many like it (and
some worse). There is a much simpler approach, where all your simulation
update code doesn't care at all about frame rate or time elapsed, but
instead you call your simulation update routine the right amount of times
for how much time has elapsed, and make up for the time lost by extra
simulation updates by doing fewer draws.

--

anyways, to answer your question - the reason the behavior of the simulation
doesn't scale with different framerates is because basically you are
simulating non-linear equations with discrete numerical integration, and the
method you are using for it (the Euler method) introduces error with a
strong bias in one direction proportional to the size of your timestep
(basically slower framerates or larger integration timesteps tend to
underestimate the non-linear effects, i.e. slower framerate means the thrust
has less effect in a way that accumulates over time).

in other words, you are trying to calculate motion that could explicitly be
described like this:
   vel(time) = initial_vel + acceleration*time
   pos(time) = initial_pos + initial_vel*time + .5*acceleration*(time*time)
implicitly by repeated applications of:
   vel = vel  + acceleration*time_delta
   pos = pos + vel*time_delta
and basically n repeated applications of the lower math gives a different
result to applying the upper math with time = n*time_delta. In particular
the non-linear pos has a different result (while the linear vel would be the
same in both cases)

If you want to read some of the huge body of stuff people have written and
researched on the topic, you want to read up on "Euler Integration", "Euler
Method" or "numerical integration", but this Wikipedia article should be a
good start:
http://en.wikipedia.org/wiki/Euler_method

While you could try to use much better integration methods (like Runge-Kutta
or something like that) which would let you support more widely varying
timesteps with much less noticeable error, that kind of stuff is much harder
to grok and use - especially when you start putting in more things to affect
motion, like grappples or boosters, etc. etc.

Another possible solution that will work for things with very simple motion
(like say this one object only moves based on one single acceleration - like
either gravity or user control in space) is to write explicit equations for
motion based on time to do the integration
What I mean is you could use the:
  pos(time) = initial_pos + initial_vel*time + .5*acceleration*(time*time)
equation, as long as it's sufficient to describe how your object behaves,
and that approach will be very stable for differently sized integration
steps.


On Sat, May 10, 2008 at 8:49 AM, Ian Mallett <[EMAIL PROTECTED]> wrote:

> Hello,
> I have a game, written in pygame, but it now needs to support more
> framerates.
> The movements of objects need to be at the same speed, regardless of the
> framerate.
> Here is an old line:
>
> self.thrust = 0.0075
>
> Here is the new line.  The game was developed on my computer at 198 fps
> (IdealFPS), but now needs to be scaled to a lower one for use on alternate
> computers (TargetFPS).  The theory here is to multiply this rate by IdealFPS
> to give the distance moved in one second, then divide this difference by the
> number of frames in second there will really be. (TargetFPS).
>
> self.thrust = (0.0075*IdealFPS)/TargetFPS
>
> Unfortunately, this doesn't work--the movement doesn't scale properly.  Any
> ideas why?
> Thanks,
> Ian
>


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Ian Mallett
Jake, that is actually pretty much my current solution. :)
I just noticed that linear things, like turning speed do seem to scale
properly.  Acceleration things, as indeed thrust is, don't.
((0.0075/(IdealFPS**2))*((IdealFPS/TargetFPS)**2))*(TargetFPS**2)
...doesn't work.  (Remember, IdealFPS = 198 and TargetFPS = 50)
Ian


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Jake b
You can define movement speed per second. That way every computer
moves X pixels/sec ( and faster computers still get the benefit of
more FPS )

Ie: velocity = 10 pixels/sec , or rotation = 90degrees/sec

Then calculate how much to move based on the users FPS:

# fps.py
class FPS():
def __init__(self):
# FPS() calc's delta ( AKA: fps_elapsed )
self.ticks_cur = pygame.time.get_ticks()
self.ticks_last = pygame.time.get_ticks()

def tick(self):
"""call once each game-loop to calculate .delta"""  
# calculate delta
self.ticks_cur = pygame.time.get_ticks()
self.delta = ( self.ticks_cur - self.ticks_last ) / 1000.0
self.ticks_last = self.ticks_cur

# unit.py
class Unit():
def update(self):
"""update physics / etc. for all units."""

# delta is what you multiple your speed per second by
delta = self.fps.delta

# in my example: .vel, .accel are euclid.Vector2()
self.vel += self.accel * delta
new_loc = self.loc + self.vel * delta


-- 
Jake


Re: [pygame] Framerate Normalizer

2008-05-10 Thread Michael George
I usually either use time directly in my calculations (rather than fps), 
because I find it more natural.  Does the velocity depend linearly on 
thrust, or is thrust an acceleration?  If the units on thrust are 
something like meters per tick squared, then I think you will need to 
compensate twice for the change in frame rate.


meters/newtick^2 = meters/oldtick^2 * (oldtick/newtick)^2

--Mike

Ian Mallett wrote:

Hello,
I have a game, written in pygame, but it now needs to support more 
framerates.
The movements of objects need to be at the same speed, regardless of 
the framerate.

Here is an old line:

self.thrust = 0.0075

Here is the new line.  The game was developed on my computer at 198 
fps (IdealFPS), but now needs to be scaled to a lower one for use on 
alternate computers (TargetFPS).  The theory here is to multiply this 
rate by IdealFPS to give the distance moved in one second, then divide 
this difference by the number of frames in second there will really 
be. (TargetFPS).


self.thrust = (0.0075*IdealFPS)/TargetFPS

Unfortunately, this doesn't work--the movement doesn't scale 
properly.  Any ideas why?

Thanks,
Ian




[pygame] Framerate Normalizer

2008-05-10 Thread Ian Mallett
Hello,
I have a game, written in pygame, but it now needs to support more
framerates.
The movements of objects need to be at the same speed, regardless of the
framerate.
Here is an old line:

self.thrust = 0.0075

Here is the new line.  The game was developed on my computer at 198 fps
(IdealFPS), but now needs to be scaled to a lower one for use on alternate
computers (TargetFPS).  The theory here is to multiply this rate by IdealFPS
to give the distance moved in one second, then divide this difference by the
number of frames in second there will really be. (TargetFPS).

self.thrust = (0.0075*IdealFPS)/TargetFPS

Unfortunately, this doesn't work--the movement doesn't scale properly.  Any
ideas why?
Thanks,
Ian