1 """<title>tutorial on how to add Sprite collision hit handlers, custom painting</title>"""
4 from pygame.rect import Rect
5 from pygame.locals import *
9 # the following line is not needed if pgu is installed
10 import sys; sys.path.insert(0, "..")
12 from pgu import tilevid, timer
19 def player_new(g,t,value):
20 g.clayer[t.ty][t.tx] = 0
21 s = tilevid.Sprite(g.images['player'],t.rect)
24 s.groups = g.string2groups('player')
27 ##In player_new() I add the shoot handler.
29 s.shoot = player_shoot
33 if s.rect.right < g.view.left: g.quit = 1
37 keys = pygame.key.get_pressed()
40 if keys[K_DOWN]: dy+=1
41 if keys[K_LEFT]: dx-=1
42 if keys[K_RIGHT]: dx+=1
45 s.rect.clamp_ip(g.view)
47 ##In player_loop(), I check for the spacebar. The spacebar triggers a shot every 8 frames.
49 if keys[K_SPACE] and g.frame%8==0:
53 ##The player_shoot() handler, as well as the shot Sprite functions. The shot group
54 ##has its agroup set to 'enemy' so it can hit 'enemy' Sprites.
56 def player_shoot(g,s):
59 def shot_new(g,t,value):
60 s = tilevid.Sprite(g.images['shot'],(t.rect.right,t.rect.centery-2))
62 s.agroups = g.string2groups('enemy')
68 if s.rect.left > g.view.right:
73 if a in g.sprites: g.sprites.remove(a)
77 def enemy_new(g,t,value):
78 g.clayer[t.ty][t.tx] = 0
79 s = tilevid.Sprite(g.images['enemy'],t.rect)
82 ##In enemy_new(), I've added a lot more detail.
83 ##- A move function to handle the type of movement the enemy will do.
84 ##- A record of the origin and entering frame of the enemy (useful for the move functions.)
85 ##- Set up the groups and agroups and a hit handler for the enemy.
87 s.move = value['move']
88 s.origin = pygame.Rect(s.rect)
90 s.groups = g.string2groups('enemy')
91 s.agroups = g.string2groups('player')
95 ##When an enemy is hit, the game quits.
102 ##In enemy_loop() we call the move handler.
106 if s.rect.right < g.view.left:
109 ##The enemy movement handlers.
111 def enemy_move_line(g,s):
114 def enemy_move_sine(g,s):
116 s.rect.y = s.origin.y + 65*math.sin((g.frame-s.frame)/10.0)
118 def enemy_move_circle(g,s):
120 s.rect.y = s.origin.y + 50*math.sin((g.frame-s.frame)/10.0)
121 s.rect.x = s.origin.x + 50*math.cos((g.frame-s.frame)/10.0)
124 def tile_block(g,t,a):
126 if (c['top'] == 1 and a._rect.bottom <= t._rect.top and a.rect.bottom > t.rect.top):
127 a.rect.bottom = t.rect.top
128 if (c['left'] == 1 and a._rect.right <= t._rect.left and a.rect.right > t.rect.left):
129 a.rect.right = t.rect.left
130 if (c['right'] == 1 and a._rect.left >= t._rect.right and a.rect.left < t.rect.right):
131 a.rect.left = t.rect.right
132 if (c['bottom'] == 1 and a._rect.top >= t._rect.bottom and a.rect.top < t.rect.bottom):
133 a.rect.top = t.rect.bottom
135 def tile_coin(g,t,a):
139 def tile_fire(g,t,a):
144 ('player','player.tga',(4,4,24,24)),
145 ('enemy','enemy.tga',(4,4,24,24)),
146 ('shot','shot.tga',(1,2,6,4)),
149 ##The codes data has been updated to include information about the appropriate
150 ##movement handlers for enemies.
154 2:(enemy_new,{'move':enemy_move_line}),
155 3:(enemy_new,{'move':enemy_move_sine}),
156 4:(enemy_new,{'move':enemy_move_circle}),
161 0x01:('player',tile_block,{'top':1,'bottom':1,'left':1,'right':1}),
162 0x02:('player',tile_block,{'top':1,'bottom':1,'left':1,'right':1}),
163 0x20:('player',tile_coin,None),
164 0x30:('player',tile_fire,None),
168 g = tilevid.Tilevid()
170 ##In init(), set the g.view size so that all the handlers will work properly. (The player_loop one depends on view having the correct size.)
172 g.view.w,g.view.h = SW,SH
175 g.screen = pygame.display.set_mode((SW,SH),SWSURFACE)
178 g.tga_load_tiles('tiles.tga',(TW,TH),tdata)
179 ##In init() I no longer have tga_load_level load the background layer, as
180 ##we will generate our own multi-layered starfield.
182 g.tga_load_level('level.tga')
184 g.bounds = pygame.Rect(TW,TH,(len(g.tlayer[0])-2)*TW,(len(g.tlayer)-2)*TH)
186 g.run_codes(cdata,(0,0,25,17))
188 g.font = pygame.font.SysFont('helvetica',16)
194 ##In run(), adding a pause variable to the game.
201 ##In run(), initializing the stars.
205 for n in range(0,NS):
206 stars.append([random.randrange(0,SW),random.randrange(0,SH),random.randrange(2,8)])
210 for e in pygame.event.get():
211 if e.type is QUIT: g.quit = 1
212 if e.type is KEYDOWN and e.key == K_ESCAPE: g.quit = 1
213 ##In run(), in the event loop, checking for F10 for full screen, RETURN for pause.
215 if e.type is KEYDOWN and e.key == K_F10:
216 #g.screen = pygame.display.set_mode((SW,SH),FULLSCREEN|HWSURFACE|DOUBLEBUF)
217 pygame.display.toggle_fullscreen()
219 if e.type is KEYDOWN and e.key == K_RETURN:
223 ##In run(), handles pause, and also renders the star field before the
224 ##foreground is painted.
228 g.run_codes(cdata,(g.view.right/TW,0,1,17))
232 g.screen.fill((0,0,0))
234 for n in range(0,NS):
236 if ((g.frame*s)%8) < s:
240 g.screen.set_at((x,y),(255,255,255))
243 img = g.font.render('%05d'%g.player.score,1,(0,0,0))
244 g.screen.blit(img,(0+1,SH-img.get_height()+1))
245 img = g.font.render('%05d'%g.player.score,1,(255,255,255))
246 g.screen.blit(img,(0,SH-img.get_height()))
247 pygame.display.flip()