3
�berBall python source by Geoff Howland. 04-28-02
5
All rights to this code and source and name are placed in the public domain.
7
This is meant to be an example of how to use Python for creating games, and being my first project
8
in Python could have some things that could have been organized better, but I think it handles most
9
of the situations pretty elegantly. The code was originally based on the 'chimp.py' example that
10
with PyGame examples as it was the most basic example to do full drawing/events. Most, but not all,
11
of that code has been rewriten though so it probably wont be much to compare.
13
If you're interested in independent game development you can take a look at my website Ludum Dare:
16
Hope this helps some people!
19
ghowland@lupinegames.com
22
On May 21, 2002, this was slightly cleaned up to be included with the pygame
23
examples. Changes mainly include better use of the pygame.sprite module. All
24
graphic resources are now rendered during runtime instead of loaded from disk.
30
from pygame.locals import *
31
from random import randint
33
if not pygame.font: raise SystemExit, "Requires pygame.font module"
34
if not pygame.mixer: print 'Warning, sound disabled'
37
#functions to create our resources
42
if not pygame.mixer or not pygame.mixer.get_init():
44
fullname = os.path.join('data', name)
46
sound = pygame.mixer.Sound(fullname)
47
except pygame.error, message:
48
print 'Cannot load sound:', fullname
49
raise SystemExit, message
52
def render_block(size, color, width=1):
53
hicolor = map(lambda x: x+40, color)
54
locolor = map(lambda x: x-20, color)
55
surf = pygame.Surface(size)
57
smallr = r.inflate(-width-1, -width-1)
59
pygame.draw.lines(surf, locolor, 0, (smallr.topright, smallr.bottomright, smallr.bottomleft), width)
60
pygame.draw.lines(surf, hicolor, 0, (r.bottomleft, r.topleft, r.topright), width)
63
def render_ball(radius, color):
64
hicolor = map(lambda x: x+50, color)
67
surf = pygame.Surface((size, size))
68
pygame.draw.circle(surf, color, (radius, radius), radius)
70
surf.set_at((half, half+1), hicolor)
71
surf.set_at((half+1, half), hicolor)
73
return surf, surf.get_rect()
76
def render_powerup(color):
78
surf = pygame.Surface((30, 30))
80
return surf, surf.get_rect()
82
font = pygame.font.Font(None, 15)
84
surf = font.render(":-)", 0, color)
85
surf2 = pygame.transform.rotate(surf, -90)
87
pygame.draw.rect(surf2, color, r, 1)
92
"Class for the game's play state variables and control functions."
97
self.effectCurrent = 0
98
self.effectDuration = 0
101
self.menuMode = 2 # Game
104
self.effectCurrent = 0
105
self.effectDuration = 0
109
def ScoreAdd(self, scoreAdd):
110
self.score = self.score + scoreAdd
112
#classes for our game objects
113
class Paddle(pygame.sprite.Sprite):
114
"""moves a clenched paddle on the screen, following the mouse"""
116
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
117
self.image, self.rect = render_block((50, 15), (200, 180, 120), 2)
120
"move the paddle based on the mouse position"
121
pos = pygame.mouse.get_pos() # This should really be passed the mouse position
122
self.rect.midtop = pos
123
self.rect.bottom = 440 # Lock Paddle at the bottom of the screen
126
#classes for our game objects
127
class Brick(pygame.sprite.Sprite):
128
"""moves a clenched paddle on the screen, following the mouse"""
129
colors = (200, 50, 50), (50, 200, 50), (50, 50, 200), (200, 200, 50)
130
def __init__(self, type, x, y):
131
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
133
self.image, self.rect = render_block((30, 10), Brick.colors[type])
135
self.rect.left = (x * self.rect.width) + 5
136
self.rect.top = y * self.rect.height
140
#classes for our game objects
141
class PowerUp(pygame.sprite.Sprite):
142
"""moves a clenched paddle on the screen, following the mouse"""
143
colors = (150, 50, 50), (50, 150, 50)
144
def __init__(self, type, speed, x, y):
145
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
148
self.image, self.rect = render_powerup(PowerUp.colors[type])
154
self.rect.top = self.rect.top + self.speed
156
def collide (self, test):
157
if self.rect.colliderect (test.rect):
162
class Ball(pygame.sprite.Sprite):
163
"""moves a monkey critter across the screen. it can spin the
164
monkey when it is punched."""
166
def __init__(self, size,x=300,y=200):
167
pygame.sprite.Sprite.__init__(self) #call Sprite intializer
169
self.image, self.rect = render_ball(Ball.radii[size], (50, 200, 200))
170
screen = pygame.display.get_surface()
171
self.area = screen.get_rect()
172
self.area.left = self.area.left - 5
174
self.rect.centerx = x
175
self.rect.centery = y
176
self.moveX = 4 + (2 * size)
177
self.moveY = 4 + (2 * size)
178
self.lastRect = self.rect
180
def changeType(self):
183
x = self.rect.centerx
184
y = self.rect.centery
185
self.image, self.rect = render_ball(Ball.radii[size], (50, 200, 200))
186
self.rect.centerx = x
187
self.rect.centery = y
189
# Set the speed based on the size, and keep its direction
191
self.moveX = self.moveX * 2
192
self.moveY = self.moveY * 2
194
self.moveX = self.moveX / 2
195
self.moveY = self.moveY / 2
198
self.lastRect = self.rect
199
newpos = self.rect.move((self.moveX, self.moveY))
200
if not self.area.contains(newpos):
201
if self.rect.centerx < 0 or self.rect.centerx > self.area.right:
202
self.moveX = -self.moveX
203
if self.rect.centery < 0 or self.rect.centery > self.area.bottom:
204
self.moveY = -self.moveY
205
newpos = self.rect.move((self.moveX, self.moveY))
209
def collidePaddle (self, test):
211
## Collision from right
212
if self.lastRect.right < test.rect.left and self.lastRect.bottom > test.rect.top:
213
self.rect.right = test.rect.left
214
self.moveX = -self.moveX
217
## Collision from left
218
if self.lastRect.left > test.rect.right and self.lastRect.bottom > test.rect.top:
219
self.rect.left = test.rect.right
220
self.moveX = -self.moveX
223
## Collision from above
224
if not horReverse and self.moveY > 0:
225
self.rect.bottom = test.rect.top
226
# If we haven't done a horizontal reverse then do a gradient angle change
228
if self.rect.centerx < test.rect.centerx:
229
self.moveX = ((self.rect.centerx - test.rect.centerx) / (test.rect.width / 2.0)) * 4.0
233
self.moveX = ((self.rect.centerx - test.rect.centerx) / (test.rect.width / 2.0)) * 4.0
237
self.moveY = -self.moveY
239
def collide (self, test):
241
# Collision from right
242
if self.lastRect.right < test.rect.left and self.lastRect.bottom > test.rect.top:
243
self.rect.right = test.rect.left-1
244
self.moveX = -self.moveX
247
# Collision from left
248
if self.lastRect.left > test.rect.right and self.lastRect.bottom > test.rect.top:
249
self.rect.left = test.rect.right+1
250
self.moveX = -self.moveX
253
# Collision from above
254
if not horReverse and self.moveY > 0:
255
self.rect.bottom = test.rect.top
256
self.moveY = -self.moveY
258
# Collision from below
259
if not horReverse and self.moveY < 0:
260
self.rect.top = test.rect.bottom
261
self.moveY = -self.moveY
265
"""This handles all the game mode activities, playing, scoring, dying, drawing, etc."""
266
def __init__(self, gameControlObj, screenRect):
267
self.gameControl = gameControlObj # Mode can change to another mode by itself
269
# Create rect bonudaries for the screen
270
self.screenBoundary = screenRect
274
self.fontScore = pygame.font.Font(None, 26)
275
self.fontLives = pygame.font.Font(None, 26)
278
self.bong_sound = load_sound('bong.wav')
283
# Create and clear the gameState
284
self.gameState = PlayGameState()
286
# Create paddle for player
287
self.paddle = Paddle()
288
self.allsprites = pygame.sprite.RenderUpdates(self.paddle)
291
self.allballs = pygame.sprite.Group((Ball(1), Ball(0)))
292
self.allsprites.add(self.allballs)
295
self.allbricks = pygame.sprite.Group()
299
brick = Brick(type, x, y)
300
brick.add((self.allbricks, self.allsprites))
303
self.allpowerups = pygame.sprite.Group()
305
def eventHandle(self):
306
if self.gameControl.gameEvent.keystate[K_ESCAPE]:
307
self.gameControl.setMode (0)
311
# Update all the sprite objects we've added to this comprehensive list
312
self.allsprites.update()
314
# If we're under the paddle
315
bottom = self.screenBoundary.bottom
316
for powerUp in self.allpowerups.sprites():
317
if powerUp.rect.bottom > bottom:
319
for ball in self.allballs.sprites():
320
if ball.rect.bottom > bottom:
323
# Check balls against blocks
324
collisiondict = pygame.sprite.groupcollide(self.allballs, self.allbricks, 0, 1)
325
for ball,bricks in collisiondict.items():
328
self.bong_sound.play()
329
self.gameState.ScoreAdd (10)
330
# Randomly create a power up
331
if not randint(0, self.powerchance):
332
self.powerchance = len(self.allballs) * 2
333
center = brick.rect.center
334
powerUp = PowerUp(randint(0,1), randint(3,7), center[0], center[1])
335
powerUp.add((self.allpowerups, self.allsprites))
337
self.powerchance = self.powerchance - 1
339
# Check balls against paddle
340
for ball in pygame.sprite.spritecollide(self.paddle, self.allballs, 0):
341
ball.collidePaddle(self.paddle)
342
self.bong_sound.play()
343
self.gameState.ScoreAdd(10)
345
# Check powerups against paddle
346
for powerUp in pygame.sprite.spritecollide(self.paddle, self.allpowerups, 1):
347
ball.collidePaddle(self.paddle)
348
self.bong_sound.play()
349
if powerUp.type == 1: # Double the balls
350
for ball in self.allballs.sprites():
351
newBall = Ball (ball.size, ball.rect.centerx, ball.rect.centery)
352
newBall.moveX = -ball.moveX
353
newBall.moveY = -ball.moveY
354
newBall.add((self.allballs, self.allsprites))
355
else: # Convert the balls
356
for ball in self.allballs.sprites():
359
# If all the balls are gone
360
if not self.allballs:
361
self.gameControl.setMode(0) # Back to the main menu
363
def draw(self, background, screen):
365
screen.blit(background, (0, 0))
366
self.allsprites.draw(screen)
369
textScore = self.fontScore.render(str(self.gameState.score), 1, (255, 255, 255))
370
textposScore = textScore.get_rect()
371
textposScore.bottom = background.get_rect().bottom - 5
372
textposScore.left = background.get_rect().left + 10
373
screen.blit(textScore, textposScore)
376
textLives = self.fontLives.render(str(len(self.allballs)), 1, (255, 255, 255))
377
textposLives = textLives.get_rect()
378
textposLives.bottom = background.get_rect().bottom - 5
379
textposLives.right = background.get_rect().right - 10
380
screen.blit(textLives, textposLives)
383
"""This handles all the main menu activities of quitting, or starting a game, checking high score"""
384
def __init__(self, gameControlObj, screenRect):
385
self.gameControl = gameControlObj # Mode can change to another mode by itself
386
self.screenBoundary = screenRect
391
self.fontMenu = pygame.font.Font(None, 30)
396
def eventHandle(self):
398
if self.gameControl.gameEvent.keystate[K_ESCAPE]:
399
self.gameControl.setMode (-1) # -1 is exit the game
401
# Move selection up and down
402
if self.gameControl.gameEvent.newkeys[K_DOWN] and self.menuSelect < self.menuMax-1:
403
self.menuSelect = self.menuSelect + 1
405
if self.gameControl.gameEvent.newkeys[K_UP] and self.menuSelect > 0:
406
self.menuSelect = self.menuSelect - 1
408
# Process current selection
409
if self.gameControl.gameEvent.newkeys[K_RETURN]:
410
if self.menuSelect == 0:
411
self.gameControl.setMode (2)
412
if self.menuSelect == 1:
413
self.gameControl.setMode (1)
414
if self.menuSelect == 2:
415
self.gameControl.setMode (-1) # -1 is exit the game
420
def draw(self, background, screen):
422
screen.blit(background, (0, 0))
424
# Draw options - New Game
425
color = (255, 255, 255)
426
if self.menuSelect == 0:
428
textMenu = self.fontMenu.render("New Game", 1, color)
429
textPosMenu = textMenu.get_rect()
430
textPosMenu.bottom = background.get_rect().centery - textPosMenu.height
431
textPosMenu.left = background.get_rect().centerx - textPosMenu.width/2
432
screen.blit(textMenu, textPosMenu)
433
lastBottom = textPosMenu.bottom
435
# Draw options - High Score
436
color = (255, 255, 255)
437
if self.menuSelect == 1:
439
textMenu = self.fontMenu.render("High Scores", 1, color)
440
textPosMenu = textMenu.get_rect()
441
textPosMenu.top = lastBottom+10
442
textPosMenu.left = background.get_rect().centerx - textPosMenu.width/2
443
screen.blit(textMenu, textPosMenu)
444
lastBottom = textPosMenu.bottom
446
# Draw options - Quit
447
color = (255, 255, 255)
448
if self.menuSelect == 2:
450
textMenu = self.fontMenu.render("Quit", 1, color)
451
textPosMenu = textMenu.get_rect()
452
textPosMenu.top = lastBottom+10
453
textPosMenu.left = background.get_rect().centerx - textPosMenu.width/2
454
screen.blit(textMenu, textPosMenu)
456
class HighScoreMenuMode:
457
"""This handles all the main menu activities of quitting, or starting a game, checking high score"""
458
def __init__(self, gameControlObj, screenRect):
459
self.gameControl = gameControlObj # Mode can change to another mode by itself
460
self.screenBoundary = screenRect
463
self.fontMenu = pygame.font.Font(None, 30)
468
def eventHandle(self):
470
if self.gameControl.gameEvent.keystate[K_RETURN]:
471
self.gameControl.setMode (0)
472
if self.gameControl.gameEvent.keystate[K_SPACE]:
473
self.gameControl.setMode (0)
478
def draw(self, background, screen):
480
screen.blit(background, (0, 0))
482
# This could be more dynamic and function, but I dont really feel like it right now.
484
# Draw options - New Game
485
color = (255, 255, 255)
486
textMenu = self.fontMenu.render("High Scores", 1, color)
487
textPosMenu = textMenu.get_rect()
488
textPosMenu.bottom = background.get_rect().centery - textPosMenu.height
489
textPosMenu.left = background.get_rect().centerx - textPosMenu.width/2
490
screen.blit(textMenu, textPosMenu)
491
lastBottom = textPosMenu.bottom
493
# Draw options - High Score
494
color = (255, 255, 255)
495
textMenu = self.fontMenu.render("----", 1, color)
496
textPosMenu = textMenu.get_rect()
497
textPosMenu.top = lastBottom+10
498
textPosMenu.left = background.get_rect().centerx - textPosMenu.width/2
499
screen.blit(textMenu, textPosMenu)
500
lastBottom = textPosMenu.bottom
502
# Draw options - Quit
503
color = (255, 255, 255)
504
textMenu = self.fontMenu.render("----", 1, color)
505
textPosMenu = textMenu.get_rect()
506
textPosMenu.top = lastBottom+10
507
textPosMenu.left = background.get_rect().centerx - textPosMenu.width/2
508
screen.blit(textMenu, textPosMenu)
511
"""Event system wrapped so that it is based on time things were pressed.
512
Otherwise repeats occur that we dont desire."""
515
# Run update to init all the variables
520
pygame.event.pump() #keep messages alive
524
oldstate = self.keystate
525
self.keystate = pygame.key.get_pressed()
526
self.newkeys = map(lambda o,c: c and not o, oldstate, self.keystate)
528
self.keystate = pygame.key.get_pressed()
529
self.newkeys = self.keystate
532
self.mousePos = pygame.mouse.get_pos()
533
self.mouseButtons = pygame.mouse.get_pressed()
536
self.ticks = pygame.time.get_ticks()
539
"""This saves the state that the game is in: what mode we're in, etc.
540
This is different than GameState because it deals with the play state."""
542
self.gameEvent = GameEvent()
547
def addMode(self, newMode):
548
"""Insert the new mode into the modes list"""
549
self.modes.insert (len(self.modes), newMode)
551
def setMode(self, newMode):
552
"""Set the new mode, and reset it"""
553
self.modeCur = newMode
555
# If we didn't set the mode to exit
556
if self.modeCur != -1:
557
self.modes[self.modeCur].reset()
560
"""Update the current mode and events"""
561
self.gameEvent.update()
563
if self.modeCur != -1:
564
self.modes[self.modeCur].eventHandle()
566
if self.modeCur != -1:
567
self.modes[self.modeCur].update()
569
def draw(self, background, screen):
570
if self.modeCur != -1:
571
self.modes[self.modeCur].draw(background, screen)
575
"""this function is called when the program starts.
576
it initializes everything it needs, then runs in
577
a loop until the function returns."""
579
#Initialize Everything
581
screen = pygame.display.set_mode((640, 480))
582
pygame.display.set_caption('�berBall')
583
pygame.mouse.set_visible(0)
584
pygame.event.set_grab(1)
586
#Create The Backgound
587
background = pygame.Surface(screen.get_size())
588
background = background.convert()
589
background.fill((0, 0, 0))
591
#Put Text On The Background, Centered
593
font = pygame.font.Font(None, 36)
594
text = font.render("�berBall", 1, (255, 255, 255))
595
textpos = text.get_rect()
596
textpos.centerx = background.get_rect().centerx
597
background.blit(text, textpos)
598
lastBot = textpos.bottom
600
font = pygame.font.Font(None, 20)
601
text = font.render("by Geoff Howland", 1, (255, 255, 255))
602
textpos = text.get_rect()
603
textpos.centerx = background.get_rect().centerx
604
textpos.top = lastBot + 10
605
background.blit(text, textpos)
608
# Create clock to lock framerate
609
clock = pygame.time.Clock()
611
# Create the game control object and add the game modes
612
gameControl = GameControl()
613
gameControl.addMode( MainMenuMode (gameControl, screen.get_rect()) )
614
gameControl.addMode( HighScoreMenuMode (gameControl, screen.get_rect()) )
615
gameControl.addMode( GameMode (gameControl, screen.get_rect()) )
624
gameControl.draw(background, screen)
627
if gameControl.modeCur == -1:
631
pygame.display.flip()
634
#this calls the 'main' function when this script is executed
635
if __name__ == '__main__': main()