1
## pygame - Python Game Library
2
## Copyright (C) 2000-2001 Pete Shinners
4
## This library is free software; you can redistribute it and/or
5
## modify it under the terms of the GNU Library General Public
6
## License as published by the Free Software Foundation; either
7
## version 2 of the License, or (at your option) any later version.
9
## This library is distributed in the hope that it will be useful,
10
## but WITHOUT ANY WARRANTY; without even the implied warranty of
11
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
## Library General Public License for more details.
14
## You should have received a copy of the GNU Library General Public
15
## License along with this library; if not, write to the Free
16
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
This module contains a base class for sprite objects. Also
23
several different group classes you can use to store and
24
identify the sprites. Some of the groups can be used to
25
draw the sprites they contain. Lastly there are a handful
26
of collision detection functions to help you quickly find
27
intersecting sprites in a group.
29
The way the groups are designed, it is very efficient at
30
adding and removing sprites from groups. This makes the
31
groups a perfect use for cataloging or tagging different
32
sprites. instead of keeping an identifier or type as a
33
member of a sprite class, just store the sprite in a
34
different set of groups. this ends up being a much better
35
way to loop through, find, and effect different sprites.
36
It is also a very quick to test if a sprite is contained
39
You can manage the relationship between groups and sprites
40
from both the groups and the actual sprite classes. Both
41
have add() and remove() functions that let you add sprites
42
to groups and groups to sprites. Both have initializing
43
functions that can accept a list of containers or sprites.
45
The methods to add and remove sprites from groups are
46
smart enough to not delete sprites that aren't already part
47
of a group, and not add sprites to a group if it already
48
exists. You may also pass a sequence of sprites or groups
49
to these functions and each one will be used.
51
The design of the sprites and groups is very flexible.
52
There's no need to inherit from the provided classes, you
53
can use any object you want for the sprites, as long as it
54
contains "add_internal" and "remove_internal" methods,
55
which are called by the groups when they remove and add
56
sprites. The same is true for containers. A container
57
can be any python object that has "add_internal" and
58
"remove_internal" methods that the sprites call when
59
they want add and remove themselves from containers. The
60
containers must also have a member named "_spritegroup",
61
which can be set to any dummy value.
65
## a group that holds only the 'n' most recent elements.
66
## sort of like the GroupSingle class, but holding more
69
## drawing groups that can 'automatically' store the area
70
## underneath, so the can "clear" without needing a background
71
## function. obviously a little slower than normal, but nice
72
## to use in many situations. (also remember it must "clear"
73
## in the reverse order that it draws :])
75
## the drawing groups should also be able to take a background
76
## function, instead of just a background surface. the function
77
## would take a surface and a rectangle on that surface to erase.
79
## perhaps more types of collision functions? the current two
80
## should handle just about every need, but perhaps more optimized
81
## specific ones that aren't quite so general but fit into common
87
"""the base class for your visible game objects.
88
The sprite class is meant to be used as a base class
89
for the objects in your game. It just provides functions
90
to maintain itself in different groups. A sprite is
91
considered 'alive' as long as it is a member of one
92
or more groups. The kill() method simply removes this
93
sprite from all groups."""
95
def __init__(self, group=()):
97
initialize a sprite object
99
You can initialize a sprite by passing it a
100
group or sequence of groups to be contained in."""
104
def add(self, group):
106
add a sprite to container
108
Add the sprite to a group or sequence of groups."""
109
has = self.__g.has_key
110
if hasattr(group, '_spritegroup'):
112
group.add_internal(self)
118
group.add_internal(self)
121
def remove(self, group):
123
remove a sprite from container
125
Remove the sprite from a group or sequence of groups."""
126
has = self.__g.has_key
127
if hasattr(group, '_spritegroup'):
129
group.remove_internal(self)
134
g.remove_internal(self)
137
def add_internal(self, group):
140
def remove_internal(self, group):
143
def update(self, *args, **kwargs):
144
#note, this just ignores all args
149
end life of sprite, remove from all groups
151
Removes the sprite from all the groups that contain
152
it. The sprite is still fine after calling this kill()
153
so you could use it to remove a sprite from all groups,
154
and then add it to some other groups."""
155
for c in self.__g.keys():
156
c.remove_internal(self)
161
list used sprite containers
163
Returns a list of all the groups that contain this
165
return self.__g.keys()
169
ask the life of a sprite
171
Returns true if this sprite is a member of any groups."""
175
return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g))
180
"""the Group class is a container for sprites
181
This is the base sprite group class. It does everything
182
needed to behave as a normal group. You can easily inherit
183
a new group class from this if you want to add more features."""
184
_spritegroup = 1 #dummy val to identify sprite groups
186
def __init__(self, sprite=()):
187
"""__init__(sprite=())
190
You can initialize a group by passing it a
191
sprite or sequence of sprites to be contained."""
198
copy a group with all the same sprites
200
Returns a copy of the group that is the same class
201
type, and has the same contained sprites."""
202
return self.__class__(self.spritedict.keys())
205
"""sprites() -> iterator
206
return an object to loop over each sprite
208
Returns an object that can be looped over with
209
a 'for' loop. (For now it is always a list, but
210
newer version of python could return different
211
objects, like iterators.)"""
212
return self.spritedict.keys()
214
def add(self, sprite):
218
Add a sprite or sequence of sprites to a group."""
219
has = self.spritedict.has_key
220
if hasattr(sprite, '_spritegroup'):
221
for sprite in sprite.sprites():
223
self.spritedict[sprite] = 0
224
sprite.add_internal(self)
226
try: len(sprite) #see if its a sequence
227
except (TypeError, AttributeError):
229
self.spritedict[sprite] = 0
230
sprite.add_internal(self)
232
for sprite in sprite:
234
self.spritedict[sprite] = 0
235
sprite.add_internal(self)
237
def remove(self, sprite):
239
remove sprite from group
241
Remove a sprite or sequence of sprites from a group."""
242
has = self.spritedict.has_key
243
if hasattr(sprite, '_spritegroup'):
244
for sprite in sprite.sprites():
246
self.remove_internal(sprite)
247
sprite.remove_internal(self)
249
try: len(sprite) #see if its a sequence
250
except (TypeError, AttributeError):
252
self.remove_internal(sprite)
253
sprite.remove_internal(self)
255
for sprite in sprite:
257
self.remove_internal(sprite)
258
sprite.remove_internal(self)
260
def add_internal(self, sprite):
261
self.spritedict[sprite] = 0
263
def remove_internal(self, sprite):
264
del self.spritedict[sprite]
266
def has(self, sprite):
267
"""has(sprite) -> bool
268
ask if group has sprite
270
Returns true if the given sprite or sprites are
271
contained in the group"""
272
has = self.spritedict.has_key
273
if hasattr(sprite, '_spritegroup'):
274
for sprite in sprite.sprites():
278
try: len(sprite) #see if its a sequence
279
except (TypeError, AttributeError):
282
for sprite in sprite:
287
if hasattr(sprite, '_spritegroup'):
288
return sprite in has(sprite)
290
for sprite in sprites:
299
Removes all the sprites from the group."""
300
for s in self.spritedict.keys():
301
self.remove_internal(s)
302
s.remove_internal(self)
303
self.spritedict.clear()
305
def update(self, *args, **kwargs):
307
call update for all member sprites
309
calls the update method for all sprites in
310
the group. any arguments are passed to the update
313
for s in self.spritedict.keys():
314
a(s.update, args, kwargs)
316
def __nonzero__(self):
317
"""__nonzero__() -> bool
318
ask if group is empty
320
Returns true if the group has any sprites. This
321
lets you check if the group has any sprites by
322
using it in a logical if statement."""
323
return len(self.spritedict)
327
number of sprites in group
329
Returns the number of sprites contained in the group."""
330
return len(self.spritedict)
333
return "<%s(%d sprites)>" % (self.__class__.__name__, len(self))
335
##note that this GroupSingle class is not derived from any other
336
##group class, it can make as a good example if you ever want to
337
##create your own new group type.
340
"""a group container that holds a single most recent item
341
This class works just like a regular group, but it only
342
keeps a single sprite in the group. Whatever sprite has
343
been added to the group last, will be the only sprite in
345
_spritegroup = 1 #dummy val to identify groups
347
def __init__(self, sprite=()):
352
if self.sprite is not 0:
353
return GroupSingle(self.sprite)
357
if self.sprite is not 0:
361
def add(self, sprite):
362
if hasattr(sprite, '_spritegroup'):
363
for sprite in sprite.sprites(): pass
366
if not len(sprite): return #see if its a sequence
368
except (TypeError, AttributeError): pass
369
if sprite is not self.sprite:
370
self.add_internal(sprite)
371
sprite.add_internal(self)
373
def remove(self, sprite):
374
if hasattr(sprite, '_spritegroup'):
375
for sprite in sprite.sprites():
376
if self.sprite is sprite:
378
sprite.remove_internal(self)
382
if not len(sprite): return #see if its a sequence
383
except (TypeError, AttributeError):
384
if self.sprite is sprite:
386
sprite.remove_internal(self)
388
for sprite in sprite:
389
if self.sprite is sprite:
391
sprite.remove_internal(self)
394
def add_internal(self, sprite):
395
if self.sprite is not 0:
396
self.sprite.remove_internal(self)
399
def remove_internal(self, sprite):
402
def has(self, sprite):
403
return self.sprite is sprite
406
if self.sprite is not 0:
407
self.sprite.remove_internal(self)
410
def update(self, *args, **kwargs):
412
apply(self.sprite.update(args, kwargs))
414
def __nonzero__(self):
415
return self.sprite is not 0
418
return self.sprite is not 0
421
return "<%s(%d sprites)>" % (self.__class__.__name__, len(self))
424
##these render groups are derived from the normal Group
425
##class, they just add methods. the Updates and Clear versions
426
##of drawing are more complex groups. They keep track of sprites
427
##that have been drawn but are removed, and also keep track of
428
##drawing info from each sprite, by storing it in the dictionary
429
##values used to store the sprite. very sneaky, but a great
430
##example for you if you ever need your own group class that
431
##maintains its own data along with each sprite it holds.
433
class RenderPlain(Group):
434
"""a sprite group that can draw all its sprites
435
The RenderPlain group is just like a normal group,
436
it just adds a "draw" method. Any sprites used with
437
this group to draw must contain two member elements
438
named "image" and "rect". These are a pygame Surface
439
and Rect object that are passed to blit."""
441
def draw(self, surface):
443
draw all sprites onto a surface
445
Draws all the sprites onto the given surface."""
446
spritedict = self.spritedict
447
surface_blit = surface.blit
448
for s in spritedict.keys():
449
surface_blit(s.image, s.rect)
453
class RenderClear(Group):
454
"""a group container that can draw and clear its sprites
455
The RenderClear group is just like a normal group,
456
but it can draw and clear the sprites. Any sprites
457
used in this group must contain member elements
458
named "image" and "rect". These are a pygame Surface
459
and Rect, which are passed to a blit call."""
460
def __init__(self, sprite=()):
461
Group.__init__(self, sprite)
462
self.lostsprites = []
464
def remove_internal(self, sprite):
465
r = self.spritedict[sprite]
467
self.lostsprites.append(r)
468
del self.spritedict[sprite]
470
def draw(self, surface):
472
draw all sprites onto a surface
474
Draws all the sprites onto the given surface."""
475
spritedict = self.spritedict
476
surface_blit = surface.blit
477
for s in spritedict.keys():
478
spritedict[s] = surface_blit(s.image, s.rect)
480
def clear(self, surface, bgd):
481
"""clear(surface, bgd)
482
erase the previous position of all sprites
484
Clears the area of all drawn sprites. the bgd
485
argument should be Surface which is the same
486
dimensions as the surface."""
487
surface_blit = surface.blit
488
for r in self.lostsprites:
489
surface_blit(bgd, r, r)
490
self.lostsprites = []
491
for r in self.spritedict.values():
493
surface_blit(bgd, r, r)
496
class RenderUpdates(RenderClear):
497
"""a sprite group that can draw and clear with update rectangles
498
The RenderUpdates is derived from the RenderClear group
499
and keeps track of all the areas drawn and cleared. It
500
also smartly handles overlapping areas between where a
501
sprite was drawn and cleared when generating the update
504
def draw(self, surface):
506
draw all sprites onto the surface
508
Draws all the sprites onto the given surface. It
509
returns a list of rectangles, which should be passed
510
to pygame.display.update()"""
511
spritedict = self.spritedict
512
surface_blit = surface.blit
513
dirty = self.lostsprites
514
self.lostsprites = []
515
dirty_append = dirty.append
516
for s, r in spritedict.items():
517
newrect = surface_blit(s.image, s.rect)
519
dirty_append(newrect)
521
dirty_append(newrect.union(r))
522
spritedict[s] = newrect
526
def spritecollide(sprite, group, dokill):
527
"""pygame.sprite.spritecollide(sprite, group, dokill) -> list
528
collision detection between sprite and group
530
given a sprite and a group of sprites, this will
531
return a list of all the sprites that intersect
533
all sprites must have a "rect" method, which is a
534
rectangle of the sprite area. if the dokill argument
535
is true, the sprites that do collide will be
536
automatically removed from all groups."""
537
spritecollide = sprite.rect.colliderect
539
for s in group.sprites():
540
if spritecollide(s.rect):
546
def groupcollide(groupa, groupb, dokilla, dokillb):
547
"""pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb) -> dict
548
collision detection between group and group
550
given two groups, this will find the intersections
551
between all sprites in each group. it returns a
552
dictionary of all sprites in the first group that
553
collide. the value for each item in the dictionary
554
is a list of the sprites in the second group it
555
collides with. the two dokill arguments control if
556
the sprites from either group will be automatically
557
removed from all groups."""
560
for s in groupa.sprites():
561
c = SC(s, groupb, dokillb)
568
def spritecollideany(sprite, group):
569
"""pygame.sprite.spritecollideany(sprite, group) -> sprite
570
finds any sprites that collide
572
given a sprite and a group of sprites, this will
573
return return any single sprite that collides with
574
with the given sprite. If there are no collisions
577
if you don't need all the features of the
578
spritecollide function, this function will be a
581
all sprites must have a "rect" method, which is a
582
rectangle of the sprite area. if the dokill argument
583
is true, the sprites that do collide will be
584
automatically removed from all groups."""
585
spritecollide = sprite.rect.colliderect
586
for s in group.sprites():
587
if spritecollide(s.rect):