# The Python Game Book

code games. learn Python.

### Site Tools

en:pygame:step005

# Step 005 - Frame-based Movement  simple version pretty version

### Code Discussion

#### Frame-based Movement

In this code example, the ball surface seems to move around. Keep in mind that the ball surface is not moving at all, it is just blitted to another position each frame (each cycle of the main loop). This movement is a frame-based movement and will depend on the actual frame-rate that your computer manages to display. Thus, the same pygame program will run slower on old or busy computers than on fast machines.

Manipulate the size of the pygame window (screen) or the workload of your computer (by moving big applications around) and you should notice that the ball needs a different amount of time to cross the screen.

#### Vectors

The new variables dx and dy are variables to control the speed of the ball surface (in pixels per second). You could call (dx, dy) a speed vector. Because integer values would mean a minimum visible movement of one pixel per frame, dx and dy are decimal values (float). In the mainloop, the current movement is added to the current position (resulting in a float value). Because pygame is (yet) unable to blit at the position of a half-pixel, the position is rounded to integer values just for blitting.

```#calculate new center of ball
ballx += dx
#bally += dy # y movement deactivated
```

```# paint the ball
screen.blit(ball, (round(ballx,0), round(bally,0)))
```

#### Line too long

The pygame.display.set_caption line is too long to fit comfortably in this wiki. Here is the code line in its natural length:

```    pygame.display.set_caption("FPS: %.2f X: %.2f Y: %.2f dx: %.2f dy:%.2f" % (clock.get_fps(), ballx, bally, dx, dy))
```
In Python, you can split a code line into several lines if:

• you split between opening <key>(</key> and closing <key>)</key> brackets
• you have two strings enclosed in <key>“</key>-signs where the second string directly follows the first string. Python will interpred those two strings as one large string. See the source code example below.
• a line ends with space and backslash <key> \</key> see this example:

```result = 5 + 7 + \
+ 10
print result # result is 5+7+10=22
```

#### Bouncing off Walls

If the ball (surface) hits the edge of the screen, the speed vector component dx or dy are inverted by multiplying with -1 to make sure that the ball surface position ballx or bally stay inside of the screen.

```    # bounce ball if out of screen
if ballx < 0:
ballx = 0
dx *= -1
```

#### Drawing on the Screen

You can draw on any pygame surface, even directly on the screen. While all changed surfaces need to be blitted again on the screen to make their changes visible, the screen surface is updated with the `pygame.display.flip()` command each frame (main loop cycle). Because the whole background is blitted on top of the screen (practically cleaning the screen) each frame, you are free to experiment with moving pictures.

Note that the `random` module has to be imported at the start of the program.

```import random # after importing pygame
```

The next code-lines draw an glittering, pulsating circle. add this code inside the mainloop:

```    # ----- pulsating circle -----------
colour = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
dr *= -1
pygame.draw.circle(screen, colour , (100,100), radius, 2) # draw pulsating circle
# -------- end of pulsating circle -------
```

## Source Code on Github

To run this example you need:

https://github.com/horstjens/ThePythonGameBook/archives/master
simple version
005_frame_based_movement_pretty.py pygame pretty version

### Simple Version

If using Python 3, be sure to replace the division operators in the following line:

`pygame.draw.circle(background, (0,0,200), (screenrect.width/2, screenrect.height/2), screenrect.width/3)`

with the ”//“ operator to truncate the quotients and return integers, rather than floating point numbers. And remember that print is now a function.

```#!/usr/bin/env python
"""
005_bouncing_ball_frame_based.py
bouncing ball and pulsating circle
url: http://thepythongamebook.com/en:part2:pygame:step005
author: horst.jens@spielend-programmieren.at

works with python3.4 and python2.7

bouncing ball. each frame the complete screen is filled with the background,
making this example simple to code but possible slow on larger resolutions.
Each frame, a random-coloured circle is drawn with randomized radius directly on the screen.
Try to manipulate the display.set_mode values to change the resolution."""
#the next line is only needed for python2.x and not necessary for python3.x
from __future__ import print_function, division
import pygame
import random
pygame.init()
screen=pygame.display.set_mode((640,480),) # try out larger values and see what happens !
screenrect = screen.get_rect()
# ------ constants ------------
clock = pygame.time.Clock()
mainloop = True
FPS = 30 # desired framerate in frames per second.
playtime = 0
radius = 50 # for pulsating circle
dr = 1  # change of radius in pixel per frame
# ------- background ---------
background = pygame.Surface(screen.get_size())
background.fill((255,155,155))     #fill the background white (red,green,blue)
background = background.convert()
screen.blit(background, (0,0))     #draw background on screen (overwriting all)
# -------- bouncing ball surface ---------
ballsurface = pygame.Surface((50,50))     #create a new surface (black by default)
ballsurface.set_colorkey((0,0,0))         #make black the transparent color (red,green,blue)
pygame.draw.circle(ballsurface, (100,175,81), (25,25),25) # paint blue circle
ballsurface = ballsurface.convert_alpha()       # if you use tranparent colors you need convert_alpha()
ballrect = ballsurface.get_rect() # the rectangle of the ball surface, for collision detection
ballx, bally = 550, 240           # start position of the ball (x,y)
dx = 10                 # x speed vector of the ball in pixel per frame
dy = 0                 # y speed vector of the ball in pixel per frame
# ----------- bouncing ball (drawing) ------
x1 = 50
y1 = 200
dx1 = 7
dy1 = 0
# --------- static big blue ball -----------
pygame.draw.circle(background, (0,0,200), (screenrect.width//2, screenrect.height//2), screenrect.width//3)
# --------- mainloop ----------
while mainloop:
# do all this each frame
milliseconds = clock.tick(FPS) # do not go faster than this framerate
playtime += milliseconds / 1000.0
for event in pygame.event.get():
if event.type == pygame.QUIT:
mainloop = False # pygame window closed by user
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
mainloop = False # user pressed ESC
pygame.display.set_caption("FPS: %.2f X: %.2f Y: %.2f dx: %.2f dy:"
" %.2f" % (clock.get_fps(), ballx, bally, dx, dy))
# ----- clean screen ----------
screen.blit(background, (0,0))     #draw background on screen (overwriting all)
# ------- bouncing ball (drawing) ---------
x1 += dx1
if x1 + radius1 >= screenrect.width:
dx1 *= -1
elif x1 - radius1 <= 0:
dx1 *= -1
# -------- bouncing ball surface ----------
ballx += dx
bally += dy
if ballx < 0: # bounce ball if out of screen
ballx = 0
dx *= -1
elif ballx + ballrect.width > screenrect.width:
ballx = screenrect.width - ballrect.width
dx *= -1
screen.blit(ballsurface, (round(ballx,0), round(bally,0)))
# ----- pulsating circle -----------
colour = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
dr *= -1
pygame.draw.circle(screen, colour , (100,100), radius, 2) # draw pulsating circle
# --------- flip screen ------------------
pygame.display.flip()          # flip the screen FPS times a second
print("This 'game' was played for {:.2f} seconds.".format(playtime))

```

### Pretty Version

(by yipyip)

Note that this version move more objects than the simple version. View/Edit/Download the file directly in Github:

```#!/usr/bin/env python

"""
Name   : blit_pulse2.py
URL    : http://thepythongamebook.com/en:part2:pygame:step003
Author : yipyip

works with pyhton3.4 and python2.7
"""

####

import pygame as pyg
import random as rand

####

def random_rgb():

return rand.randint(0, 255), rand.randint(0,255), rand.randint(0, 255)

####

class PygView(object):

def __init__(self, width=800, height=600, fps=50):
"""Initializing background surface for static drawing
and screen surface for dynamic drawing
"""
pyg.init()
pyg.display.set_caption("Press ESC to quit")

self.width = width
self.height = height

self.screen = pyg.display.set_mode((self.width, self.height), pyg.DOUBLEBUF)
self.background = pyg.Surface(self.screen.get_size()).convert()
# white blackground
self.background.fill((255, 255, 255))

self.act_surface = self.screen
self.act_rgb = 255, 0, 0

def draw_static(self):

self.act_surface = self.background

def draw_dynamic(self):

self.act_surface = self.screen

def set_color(self, rgb):

self.act_rgb = rgb

def circle(self, x, y, radius, width):
"""Allocate surface for blitting and draw circle
"""
surface.set_colorkey((0, 0, 0))
self.act_surface.blit(surface.convert_alpha(), (x, y))

def run(self, draw_dynamic):
"""The mainloop
"""
running = True
while running:
for event in pyg.event.get():
if event.type == pyg.QUIT:
running = False
elif event.type == pyg.KEYDOWN:
if event.key == pyg.K_ESCAPE:
running = False

draw_dynamic()
pyg.display.flip()
self.screen.blit(self.background, (0, 0))

pyg.quit()

####

class Ball(object):
"""A circle object with no hardcoded dependency on pygame
(and other libs too, obviously...)
"""
def __init__(self, x, y, radius, speed_x=1, speed_pulse=0, color=(0,0,255), width=0):

self.x = x
self.y = y
self.speed_x = speed_x
self.speed_pulse = speed_pulse
self.color = color
self.width = width
self.shrinking = True

@property
def max_x(self):

return self.x + self.radius * 2

def rel_move(self, dx, dy):

self.x += dx
self.y += dy

def pulse(self):
"""Shrink or expand ball
"""
if not self.speed_pulse:
return

# balls are shrinking first
if self.shrinking:
else:
self.shrinking = False
else:
else:
self.shrinking = True

def draw(self, view):
""" Draw on a device with an appropriate interface
"""
if self.speed_pulse:
color = random_rgb()
else:
color = self.color
view.set_color(color)

####

def action(balls, width, view):
""" Return a function for the pygame mainloop
"""
# balls move to the right first
right_moving = [True] * len(balls)

def animate_balls():
""" Draw moving balls
"""
for i, ball in enumerate(balls):
if right_moving[i]:
if ball.max_x < width:
ball.rel_move(ball.speed_x, 0)
else:
right_moving[i] = False
else:
if ball.x > 0:
ball.rel_move(-ball.speed_x, 0)
else:
right_moving[i] = True

ball.pulse()
ball.draw(view)

return animate_balls

####

def main(width):
"""Simple example with stationary and moving balls
"""
view = PygView(width)

view.draw_static()
# args:  x, y, radius, speed_x, speed_pulse, color, border_width
ball01 = Ball(50, 60, 50, 0, 0, (255, 255, 0))
ball01.draw(view)
ball02 = Ball(250, 150, 190, 0, 0, (66, 1, 166))
ball02.draw(view)

view.draw_dynamic()
ball1 = Ball(15, 130, 100, 1, 0, (255, 0, 0))
ball2 = Ball(25, 200, 80, 2, 0, (0, 255, 155))
ball3 = Ball(20, 220, 110, 1, 1, (100, 55, 155))
ball4 = Ball(20, 400, 70, 3, 0, (250, 100, 255))
ball5 = Ball(90, 390, 70, 0, 1, (250, 100, 255), 1)

loopfunc = action((ball1, ball2, ball4, ball5), width, view)
view.run(loopfunc)

####

if __name__ == '__main__':

main(900)

```

# Comment this Ptage 