Jump to content
• Advertisement

# fixed timestep euler physics

This topic is 3795 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

## Recommended Posts

I'm working on a basic asteroids clone, learning how to implement a fixed timestep, so that I can use Euler without it exploding. ( My ref is: gaffer.org/fix-your-timestep) I have a few questions, and my code is below. [1] If I want to run my simulation at 100fps , is it correct to set dt to 10? Since I'm using pygame.time.get_ticks(), where the tut was using (I think? seconds). [ 1000ms / 100fps = 10ms ? ] [2] How do I calculate my speeds? ie: I want to set player's velocity to 200 pixels/second. Do I do something like:?
def get_adjusted_vel( speed_in_pixels ):
# speed_in_pixels was: 200
# adjusted_vel : is the vel I set to get 200px/sec movement ?
adjusted_vel = speed_in_pixels / dt # dt was 10 if q #1 was correct

[2.B]Does that also work for accel? ( ie: accel of 200px/sec ?) [3] on the gaffer.org/fix-your-timestep tutorial, it calls integrate() with 3 args; state, t, dt. Do I only need t if I were to use the RK4 integrator instead of Euler? [4] How low should I set dt? 100fps? ( Or it doesn't matter as long as .integrate() takes less time to complete than my timestep: dt? ) [5] Is there anything else wrong with my code? ( in the fixed timestep physics part )
#game.py
__author__ = "Jake b"
VERSION = "0.3"
WINDOW_TITLE = "fixed-timestep (euler): v%s [ %s ]" % (VERSION, __author__)

try:
import sys
import pygame
from pygame.locals import *
from pygame.sprite import RenderPlain
from actor import Actor, Ship, Asteroid
except ImportError, err:
print "Couldn't load module. %s" % (err)
sys.exit(2)

class FPSText():
"""helper to render FPS text"""
def __init__(self, game, color="white"):
self.screen = game.screen
self.clock = game.clock
self.font = pygame.font.Font(None, 18)
self.image = None
self.rect = pygame.Rect(0,0,0,0)
self.text = "FPS: "
self.color = pygame.color.Color(color)

def draw(self): # todo: only re-render every 1 second
self.text = "FPS: %d" % self.clock.get_fps()
self.image = self.font.render( self.text, False, self.color )
self.rect = self.image.get_rect()
dest_rect = pygame.Rect( 15, 15, 0, 0 )
self.screen.blit( self.image, dest_rect )

class GameMain():
"""game Main entry point. handles intialization of game and graphics.

methods:
__init__(width, height) : just init

main_loop() : the main loop
handle_events() : main events loop
draw() : main render
update() : physics framerate logic, calls .integrate()
integrate() : physics
members:
dt : physics fixed-timestep ( defined as milliseconds ? )
fps : FPSText() object
actor_sprites : RenderPlain() sprite group, holds all sprites
player : player Ship() object
asteroids : Asteroid()s objects
screen : screen display surface
"""
bDone = False

def __init__(self, width=800, height=600):
"""Initialize PyGame"""
pygame.init()
self.width, self.height = width, height
self.screen = pygame.display.set_mode(( self.width, self.height ))
pygame.display.set_caption( WINDOW_TITLE )

# sprite group, all Actors(), Ship()s, and Asteroid()s will join this:
self.actor_sprites = RenderPlain()

self.clock = pygame.time.Clock()
self.fps = FPSText( self )
self.player = Ship(self)
self.asteroids = []
for i in range(15):
self.asteroids.append( Asteroid(self) )

# physics stuff
self.dt = 10. # 10 milliseconds ( or 1/100th of sec )
self.t = 0. # not needed for Euler?
self.timeCur = pygame.time.get_ticks()
self.accumulator = 0.

def main_loop(self):
"""Game() main loop"""
while not self.bDone:
self.handle_events()
self.update()
self.draw()

self.clock.tick(30) # delay and caps display FPS to 30

def integrate(self, dt):
"""physics update, integrate."""
self.actor_sprites.update( dt )	 # update Actor()s

def update(self):
"""integrate / update physics. using fixed-timestep.
based off of: http://www.gaffer.org/game-physics/fix-your-timestep"""
# newTime = time()
# deltaTime = newTime - curTime
# curTime = newTime
self.timeNew = pygame.time.get_ticks()
self.timeDelta = self.timeNew - self.timeCur
self.timeCur = self.timeNew

# jake note: is this clamping of .timeDelta correct?
# clamp timeDelta so it doesn't reach insanely high numbers if window
# loses focus for extended period of time. capped at 250ms
if self.timeDelta > 250: self.timeDelta = 250

self.accumulator += self.timeDelta

while self.accumulator >= self.dt:
self.integrate( self.dt )
self.t += self.dt
self.accumulator -= self.dt

def handle_events(self):
"""do regular events."""
# handle events, and pass to renderer
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT: sys.exit()
elif event.type == KEYDOWN:
if (event.key == K_ESCAPE): self.bDone = True

def draw(self):
"""render screen"""
# clear
self.screen.fill( pygame.color.Color("black") )
self.fps.draw()
# sprites
self.actor_sprites.draw( self.screen )
# flip
pygame.display.flip()

# == entry point ==
if __name__ == "__main__":
# entry point
main_window = GameMain()
main_window.main_loop()


#actor.py
__author__ = "Jake b"
VERSION = "0.1"

try:
import random
from math import *
import pygame
from pygame.locals import *
from euclid import Vector2
except ImportError, err:
print "Couldn't load module. %s" % (err)
sys.exit(2)

class Actor(pygame.sprite.Sprite):
"""Base actor class. Handles physics, and rendering. for now, renders Rect()

methods:
__init__(loc, vel, accel) : just init
update(dt) : physics
handle_events(events) : main events loop
handle_input() : handle key input not handled in .handle_events()
draw() : [todo:] main render, derived from this
spawn() : add to sprite group
kill() rem from sprite group
_get_topleft_offset() : get offset for rect.topleft based on loc

members:
screen : actual screen root surface
loc, vel, accel, mass : self explainatory
max_accel, max_vel : speed caps
"""
def __init__( self, game, loc=Vector2(0.,0.), vel=Vector2(0.,0.),
accel=Vector2(0.,0.), mass=1., sprite_w=15, sprite_h=15, color="blue" ):
"""init loc, vel, accel."""
pygame.sprite.Sprite.__init__(self)
self.game = game
self.loc = loc
self.vel = vel
self.accel = accel
self.mass = mass
self.sprite_w, self.sprite_h = sprite_w, sprite_h
# note: caps not used yet, todo:
self.set_max_vel( 1000 )
self.set_max_accel( 200 )
self.image = pygame.Surface([sprite_w, sprite_h])
self.image.fill( pygame.color.Color(color) )
self.rect = self.image.get_rect()

self.rect.topleft = self._get_topleft_offset()
self.spawn() # add to sprite group

# get/set loc, vel, accel, mass, max_vel, max_accel
def get_loc(self): return self.loc.copy()
def set_loc(self, loc): self.loc = loc
def get_vel(self): return self.vel.copy()
def set_vel(self, vel): self.vel = vel
def get_accel(self): return self.accel.copy()
def set_accel(self, accel): self.accel = accel
def get_mass(self): return self.mass
def set_mass(self, mass): self.mass = mass
def get_max_vel(self): return self.max_vel
def set_max_vel(self, max_vel): self.max_vel = max_vel
def get_max_accel(self): return self.max_accel
def set_max_accel(self, max_accel): self.max_accel = max_accel

def handle_events(self, events):
"""derive to handle a copy of events from mainloop."""
pass

def handle_input(self):
"""derive to handle key input not handled by events"""
pass

def draw(self):
"""derived classes can override this. note: RenderSrcRect() does not call .draw()"""
pass

def add_accel(self, force):
self.accel += ( force / self.mass )

def update(self, dt):
"""update physics, dt = the fixed-timestep"""
self.loc += self.vel * dt
self.vel += self.accel * dt

# update loc; keeponscreen;
self.rect.topleft = self._get_topleft_offset()
self._keep_onscreen()

def spawn(self):
"""add Actor() to sprite group, move to proper location, etc..."""
self.game.actor_sprites.add( self ) # add to sprite group

def kill(self):
"""kill player/asteroid/whatever"""
pygame.sprite.Sprite.kill(self) # call parent; rem from sprite group

def _keep_onscreen(self):
"""keep self onscreen, wrapping on edges"""
w,h = self.game.screen.get_size()
if self.loc.x < 0: self.loc.x = w
if self.loc.x > w: self.loc.x = 0
if self.loc.y < 0: self.loc.y = h
if self.loc.y > h: self.loc.y = 0

def _get_topleft_offset(self):
"""get the topleft offset of sprite based on Actor.loc(), and
Actor.sprite_w, Actor.sprite_h"""
# loc is based on center of sprite, so subtract to get topleft
x = self.loc.x - ( self.sprite_w / 2 )
y = self.loc.y - ( self.sprite_h / 2 )
return (x, y)

class Ship(Actor):
"""Ship() specific"""
def __init__( self, game, loc=Vector2(0.,0.), vel=Vector2(0.,0.),
accel=Vector2(0.,0.), mass=1., sprite_w=30, sprite_h=30, color="red" ):
"""Overwrite size and other stuff."""
Actor.__init__(self, game, loc, vel, accel, mass, sprite_w, sprite_h,
color )
self.set_max_vel( 1000 )
self.set_max_accel( 200 )

self.spawn()

def spawn(self):
"""spawn random loc, random vel, and random direction"""
Actor.spawn(self)

w,h = self.game.screen.get_size()
self.loc = w/2, h/2
self.vel = Vector2(0., 0.)
self.accel = Vector2(0., 0.)

class Asteroid(Actor):
"""Asteroid specific"""
def __init__( self, game, loc=Vector2(0.,0.), vel=Vector2(0.,0.),
accel=Vector2(0.,0.), mass=1., sprite_w=50, sprite_h=50, color="blue" ):
"""Overwrite size and other stuff."""
Actor.__init__(self, game, loc, vel, accel, mass, sprite_w, sprite_h,
color )
self.set_max_vel( 10 ) # todo: I expected this to mean 10px / sec but not working
self.set_max_accel( 200 )

self.spawn()

def spawn(self):
"""spawn random loc, random vel, and random direction"""
Actor.spawn(self)
w,h = self.game.screen.get_size()

# rand loc
self.loc = random.randint(0, w), random.randint(0,h)
# rand dir
angle = radians( random.randint( 0, 360 ) )
v = Vector2( cos(angle), sin(angle) )
v.normalize()
# rand vel
mag = random.randint( self.max_vel/10, self.max_vel )
# finally; set rand dir, and rand vel.
v *= mag
self.vel = v

def kill(self):
Actor.kill(self)


thanks, -- monkey

#### Share this post

##### Share on other sites
Advertisement
Quote:
 Original post by ninmonkeys[1] If I want to run my simulation at 100fps , is it correct to set dt to 10? Since I'm using pygame.time.get_ticks(), where the tut was using (I think? seconds). [ 1000ms / 100fps = 10ms ? ][2] How do I calculate my speeds? ie: I want to set player's velocity to 200 pixels/second. Do I do something like:?*** Source Snippet Removed ***[2.B]Does that also work for accel? ( ie: accel of 200px/sec ?)

If you want to work in "units per second" inside your code, you should use a dt of 0.01 (seconds), not 10 (milliseconds).
pygame.time.get_ticks() is returning values in milliseconds, so divide the return value by 1000 so that all of your values are in seconds.

The only major problem with [2] is that the '/' needs to be a '*'.
Then your [2] code will work, but speed_in_pixels is actually speed_in_pixels_per_millisecond (because dt is in milliseconds, not in seconds).
If you instead measure dt in seconds, and you measure your speed in pixels per second, then your code for [2] is ok.

i.e. You just need to ensure that all your units match up:
pixels_to_move_this_update = speed_in_pixels_per_second * dt_in_seconds

#### Share this post

##### Share on other sites
Quote:
 Original post by HodgmanIf you want to work in "units per second" inside your code, you should use a dt of 0.01 (seconds), not 10 (milliseconds). pygame.time.get_ticks() is returning values in milliseconds, so divide the return value by 1000 so that all of your values are in seconds.
I set dt = 0.01 ( seconds) and self.timeNew = pygame.time.get_ticks() / 1000.

But then my .integrate() is never called. ( So I stuck with ms since I was already using that. )

Quote:
 Original post by HodgmanThe only major problem with [2] is that the '/' needs to be a '*'.Then your [2] code will work, but speed_in_pixels is actually speed_in_pixels_per_millisecond (because dt is in milliseconds, not in seconds)....i.e. You just need to ensure that all your units match up:pixels_to_move_this_update = speed_in_pixels_per_second * dt_in_seconds

The following code apears right to me, Is my get_adjusted_speed() and integrate() code correct?

I was getting confused because you said pixels_to_move_this_update but I think I'm supposed to return pixels_per_ms, unless my update() is wrong.
def get_adjusted_speed( self, speed_in_px_per_sec ):	"""input: speed per seconds.	output: speed per ms.		I thought this function had to use dt to define the new speed,	so I would get the same speed per sec if dt is ever changed. but, it	seems like I don't use dt here, else: update() is wrong ?"""		# speed[per ms] = speed[per sec]/1000.	speed_in_px_per_ms =  speed_in_px_per_sec / 1000.	return speed_in_px_per_ms	def update(self, dt):	"""update physics, dt = the fixed-timestep"""	# note: preview is not showing the pluseuals operator, but its there.	self.loc += self.vel * dt	self.vel += self.accel * dtdef integrate(self, dt):	"""physics update, integrate."""	# update Actor()s	self.actor_sprites.update( dt )

#### Share this post

##### Share on other sites

• Advertisement
• Advertisement

• ### Popular Contributors

1. 1
2. 2
3. 3
4. 4
Rutin
18
5. 5
• Advertisement

• 13
• 14
• 9
• 9
• 9
• ### Forum Statistics

• Total Topics
632927
• Total Posts
3009247
• ### Who's Online (See full list)

There are no registered users currently online

×

## Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!