0
# Game Object Management
1
2
Sprite and collision system for managing game objects. Pygame's sprite system provides an organized way to handle game entities with automatic updating, rendering, and collision detection capabilities.
3
4
## Capabilities
5
6
### Sprite Base Class
7
8
Foundation class for all game objects with lifecycle management.
9
10
```python { .api }
11
class Sprite:
12
def __init__(self, *groups):
13
"""
14
Base class for visible game objects.
15
16
Args:
17
*groups: Sprite groups to add this sprite to
18
"""
19
20
def update(self, *args, **kwargs) -> None:
21
"""
22
Update sprite state (override in subclasses).
23
24
Args:
25
*args, **kwargs: Arguments passed from group.update()
26
"""
27
28
def kill(self) -> None:
29
"""Remove sprite from all groups."""
30
31
def alive(self) -> bool:
32
"""
33
Check if sprite is in any groups.
34
35
Returns:
36
bool: True if sprite belongs to at least one group
37
"""
38
39
def groups(self) -> list:
40
"""
41
Get list of groups containing this sprite.
42
43
Returns:
44
list: List of groups this sprite belongs to
45
"""
46
47
def add(self, *groups) -> None:
48
"""
49
Add sprite to groups.
50
51
Args:
52
*groups: Groups to add sprite to
53
"""
54
55
def remove(self, *groups) -> None:
56
"""
57
Remove sprite from groups.
58
59
Args:
60
*groups: Groups to remove sprite from
61
"""
62
63
# Required attributes for proper sprite behavior:
64
image: pygame.Surface # Visual representation
65
rect: pygame.Rect # Position and collision bounds
66
```
67
68
### Sprite Groups
69
70
Container classes for managing collections of sprites.
71
72
```python { .api }
73
class Group:
74
def __init__(self, *sprites):
75
"""
76
Container for sprites with batch operations.
77
78
Args:
79
*sprites: Initial sprites to add
80
"""
81
82
def sprites(self) -> list:
83
"""
84
Get list of all sprites in group.
85
86
Returns:
87
list: List of sprites in group
88
"""
89
90
def copy(self) -> 'Group':
91
"""
92
Create a copy of the group.
93
94
Returns:
95
Group: New group with same sprites
96
"""
97
98
def add(self, *sprites) -> None:
99
"""
100
Add sprites to group.
101
102
Args:
103
*sprites: Sprites to add
104
"""
105
106
def remove(self, *sprites) -> None:
107
"""
108
Remove sprites from group.
109
110
Args:
111
*sprites: Sprites to remove
112
"""
113
114
def has(self, *sprites) -> bool:
115
"""
116
Check if group contains sprites.
117
118
Args:
119
*sprites: Sprites to check
120
121
Returns:
122
bool: True if all sprites are in group
123
"""
124
125
def update(self, *args, **kwargs) -> None:
126
"""
127
Call update() on all sprites in group.
128
129
Args:
130
*args, **kwargs: Arguments passed to sprite.update()
131
"""
132
133
def draw(self, surface: pygame.Surface) -> list:
134
"""
135
Draw all sprites to a surface.
136
137
Args:
138
surface (pygame.Surface): Surface to draw on
139
140
Returns:
141
list: List of Rect areas that were drawn
142
"""
143
144
def clear(self, surface: pygame.Surface, background) -> list:
145
"""
146
Clear sprites by drawing background over them.
147
148
Args:
149
surface (pygame.Surface): Surface to clear on
150
background: Background to draw (Surface or color)
151
152
Returns:
153
list: List of Rect areas that were cleared
154
"""
155
156
def empty(self) -> None:
157
"""Remove all sprites from group."""
158
159
def __len__(self) -> int:
160
"""Get number of sprites in group."""
161
162
def __iter__(self):
163
"""Iterate over sprites in group."""
164
165
def __contains__(self, sprite) -> bool:
166
"""Check if sprite is in group."""
167
```
168
169
### Specialized Group Classes
170
171
Advanced group classes with additional features for game development.
172
173
```python { .api }
174
class RenderUpdates(Group):
175
def __init__(self, *sprites):
176
"""Group that tracks dirty rectangles for efficient screen updates."""
177
178
def draw(self, surface: pygame.Surface) -> list:
179
"""
180
Draw sprites and return list of updated areas.
181
182
Returns:
183
list[pygame.Rect]: Areas that were drawn (for display updates)
184
"""
185
186
class OrderedUpdates(RenderUpdates):
187
def __init__(self, *sprites):
188
"""Group that maintains sprite order for drawing."""
189
190
class LayeredUpdates(OrderedUpdates):
191
def __init__(self, *sprites, **kwargs):
192
"""
193
Group with sprite layers for depth sorting.
194
195
Keyword Args:
196
default_layer (int): Default layer for new sprites
197
"""
198
199
def add(self, *sprites, **kwargs) -> None:
200
"""
201
Add sprites to group with optional layer.
202
203
Keyword Args:
204
layer (int): Layer to add sprites to
205
"""
206
207
def change_layer(self, sprite, new_layer: int) -> None:
208
"""
209
Move sprite to different layer.
210
211
Args:
212
sprite: Sprite to move
213
new_layer (int): New layer number
214
"""
215
216
def get_layer_of_sprite(self, sprite) -> int:
217
"""
218
Get layer of a sprite.
219
220
Args:
221
sprite: Sprite to check
222
223
Returns:
224
int: Layer number
225
"""
226
227
def get_sprites_at(self, layer: int) -> list:
228
"""
229
Get all sprites at a specific layer.
230
231
Args:
232
layer (int): Layer to check
233
234
Returns:
235
list: Sprites at the layer
236
"""
237
238
def layers(self) -> list:
239
"""
240
Get list of all layers.
241
242
Returns:
243
list[int]: Sorted list of layer numbers
244
"""
245
246
class GroupSingle(Group):
247
def __init__(self, sprite = None):
248
"""
249
Group that holds only one sprite at a time.
250
251
Args:
252
sprite: Initial sprite (optional)
253
"""
254
255
@property
256
def sprite(self):
257
"""Get the single sprite in group (or None)."""
258
```
259
260
### Sprite Variants
261
262
Specialized sprite classes with additional functionality.
263
264
```python { .api }
265
class DirtySprite(Sprite):
266
def __init__(self, *groups):
267
"""Sprite that tracks changes for efficient rendering."""
268
269
dirty: int # 0=clean, 1=dirty, 2=always dirty
270
source_rect: pygame.Rect # Source area for blitting
271
visible: int # Visibility flag
272
273
class WeakSprite(Sprite):
274
def __init__(self, *groups):
275
"""Sprite with weak references to groups (for memory management)."""
276
277
class WeakDirtySprite(WeakSprite, DirtySprite):
278
def __init__(self, *groups):
279
"""Sprite combining weak references and dirty tracking."""
280
```
281
282
## Collision Detection
283
284
Functions for detecting collisions between sprites and groups.
285
286
```python { .api }
287
def spritecollide(sprite, group: Group, dokill: bool, collided = None) -> list:
288
"""
289
Find sprites in group that collide with given sprite.
290
291
Args:
292
sprite: Sprite to check collisions for
293
group (Group): Group to check against
294
dokill (bool): Remove colliding sprites from group
295
collided: Collision detection function (default: collide_rect)
296
297
Returns:
298
list: List of sprites that collide
299
"""
300
301
def groupcollide(group1: Group, group2: Group, dokill1: bool, dokill2: bool, collided = None) -> dict:
302
"""
303
Detect collisions between two sprite groups.
304
305
Args:
306
group1 (Group): First group
307
group2 (Group): Second group
308
dokill1 (bool): Remove colliding sprites from group1
309
dokill2 (bool): Remove colliding sprites from group2
310
collided: Collision detection function
311
312
Returns:
313
dict: Mapping of group1 sprites to list of colliding group2 sprites
314
"""
315
316
def spritecollideany(sprite, group: Group, collided = None):
317
"""
318
Find first sprite in group that collides with given sprite.
319
320
Args:
321
sprite: Sprite to check collisions for
322
group (Group): Group to check against
323
collided: Collision detection function
324
325
Returns:
326
Sprite or None: First colliding sprite found
327
"""
328
```
329
330
### Collision Detection Functions
331
332
Different collision detection algorithms for various game needs.
333
334
```python { .api }
335
def collide_rect(left, right) -> bool:
336
"""
337
Collision detection using sprite rectangles.
338
339
Args:
340
left, right: Sprites to check
341
342
Returns:
343
bool: True if rectangles overlap
344
"""
345
346
def collide_rect_ratio(ratio: float):
347
"""
348
Create collision function using scaled rectangles.
349
350
Args:
351
ratio (float): Scale factor for rectangles
352
353
Returns:
354
function: Collision detection function
355
"""
356
357
def collide_circle(left, right) -> bool:
358
"""
359
Collision detection using circular areas.
360
361
Args:
362
left, right: Sprites to check (must have .radius attribute)
363
364
Returns:
365
bool: True if circles overlap
366
"""
367
368
def collide_circle_ratio(ratio: float):
369
"""
370
Create collision function using scaled circles.
371
372
Args:
373
ratio (float): Scale factor for circles
374
375
Returns:
376
function: Collision detection function
377
"""
378
379
def collide_mask(left, right):
380
"""
381
Pixel-perfect collision detection using masks.
382
383
Args:
384
left, right: Sprites to check (must have .mask attribute)
385
386
Returns:
387
tuple or None: Collision point or None if no collision
388
"""
389
```
390
391
## Usage Examples
392
393
### Basic Sprite Usage
394
395
```python
396
import pygame
397
398
class Player(pygame.sprite.Sprite):
399
def __init__(self):
400
super().__init__()
401
402
# Required sprite attributes
403
self.image = pygame.Surface((32, 32))
404
self.image.fill((0, 128, 255)) # Blue player
405
self.rect = self.image.get_rect()
406
self.rect.center = (400, 300)
407
408
# Custom attributes
409
self.speed = 5
410
411
def update(self):
412
# Move based on keyboard input
413
keys = pygame.key.get_pressed()
414
if keys[pygame.K_LEFT]:
415
self.rect.x -= self.speed
416
if keys[pygame.K_RIGHT]:
417
self.rect.x += self.speed
418
if keys[pygame.K_UP]:
419
self.rect.y -= self.speed
420
if keys[pygame.K_DOWN]:
421
self.rect.y += self.speed
422
423
# Keep player on screen
424
self.rect.clamp_ip(pygame.Rect(0, 0, 800, 600))
425
426
class Enemy(pygame.sprite.Sprite):
427
def __init__(self, x, y):
428
super().__init__()
429
430
self.image = pygame.Surface((24, 24))
431
self.image.fill((255, 0, 0)) # Red enemy
432
self.rect = self.image.get_rect()
433
self.rect.center = (x, y)
434
435
self.speed = 2
436
self.direction = 1
437
438
def update(self):
439
# Simple back and forth movement
440
self.rect.x += self.speed * self.direction
441
if self.rect.left < 0 or self.rect.right > 800:
442
self.direction *= -1
443
444
# Initialize pygame
445
pygame.init()
446
screen = pygame.display.set_mode((800, 600))
447
clock = pygame.time.Clock()
448
449
# Create sprite groups
450
all_sprites = pygame.sprite.Group()
451
enemies = pygame.sprite.Group()
452
453
# Create player
454
player = Player()
455
all_sprites.add(player)
456
457
# Create enemies
458
for i in range(5):
459
enemy = Enemy(100 + i * 120, 100)
460
all_sprites.add(enemy)
461
enemies.add(enemy)
462
463
running = True
464
while running:
465
for event in pygame.event.get():
466
if event.type == pygame.QUIT:
467
running = False
468
469
# Update all sprites
470
all_sprites.update()
471
472
# Check collisions
473
hits = pygame.sprite.spritecollide(player, enemies, True)
474
for hit in hits:
475
print("Player hit enemy!")
476
477
# Draw everything
478
screen.fill((0, 0, 0))
479
all_sprites.draw(screen)
480
481
pygame.display.flip()
482
clock.tick(60)
483
484
pygame.quit()
485
```
486
487
### Advanced Collision Detection
488
489
```python
490
import pygame
491
import math
492
493
class Ball(pygame.sprite.Sprite):
494
def __init__(self, x, y):
495
super().__init__()
496
497
# Create circular sprite
498
self.radius = 15
499
self.image = pygame.Surface((self.radius * 2, self.radius * 2), pygame.SRCALPHA)
500
pygame.draw.circle(self.image, (255, 255, 0), (self.radius, self.radius), self.radius)
501
502
self.rect = self.image.get_rect()
503
self.rect.center = (x, y)
504
505
# Physics
506
self.vel_x = random.uniform(-5, 5)
507
self.vel_y = random.uniform(-5, 5)
508
509
def update(self):
510
self.rect.x += self.vel_x
511
self.rect.y += self.vel_y
512
513
# Bounce off walls
514
if self.rect.left < 0 or self.rect.right > 800:
515
self.vel_x *= -1
516
if self.rect.top < 0 or self.rect.bottom > 600:
517
self.vel_y *= -1
518
519
import random
520
521
pygame.init()
522
screen = pygame.display.set_mode((800, 600))
523
clock = pygame.time.Clock()
524
525
# Create balls
526
balls = pygame.sprite.Group()
527
for _ in range(10):
528
ball = Ball(random.randint(50, 750), random.randint(50, 550))
529
balls.add(ball)
530
531
running = True
532
while running:
533
for event in pygame.event.get():
534
if event.type == pygame.QUIT:
535
running = False
536
537
balls.update()
538
539
# Check ball-to-ball collisions using circle collision
540
for ball in balls:
541
collisions = pygame.sprite.spritecollide(ball, balls, False, pygame.sprite.collide_circle)
542
for other in collisions:
543
if ball != other:
544
# Simple collision response
545
dx = ball.rect.centerx - other.rect.centerx
546
dy = ball.rect.centery - other.rect.centery
547
distance = math.sqrt(dx*dx + dy*dy)
548
549
if distance > 0:
550
# Normalize and separate
551
dx /= distance
552
dy /= distance
553
ball.rect.centerx += dx * 2
554
ball.rect.centery += dy * 2
555
556
screen.fill((0, 0, 0))
557
balls.draw(screen)
558
pygame.display.flip()
559
clock.tick(60)
560
561
pygame.quit()
562
```
563
564
### Layered Sprite Management
565
566
```python
567
import pygame
568
569
class Background(pygame.sprite.Sprite):
570
def __init__(self):
571
super().__init__()
572
self.image = pygame.Surface((800, 600))
573
self.image.fill((50, 100, 50)) # Green background
574
self.rect = self.image.get_rect()
575
576
class Player(pygame.sprite.Sprite):
577
def __init__(self):
578
super().__init__()
579
self.image = pygame.Surface((32, 32))
580
self.image.fill((0, 0, 255)) # Blue player
581
self.rect = self.image.get_rect()
582
self.rect.center = (400, 300)
583
584
class Bullet(pygame.sprite.Sprite):
585
def __init__(self, x, y):
586
super().__init__()
587
self.image = pygame.Surface((4, 8))
588
self.image.fill((255, 255, 0)) # Yellow bullet
589
self.rect = self.image.get_rect()
590
self.rect.centerx = x
591
self.rect.bottom = y
592
593
self.speed = -10
594
595
def update(self):
596
self.rect.y += self.speed
597
if self.rect.bottom < 0:
598
self.kill()
599
600
pygame.init()
601
screen = pygame.display.set_mode((800, 600))
602
clock = pygame.time.Clock()
603
604
# Use layered group for depth sorting
605
all_sprites = pygame.sprite.LayeredUpdates()
606
607
# Add sprites to specific layers (lower numbers = behind, higher = in front)
608
background = Background()
609
all_sprites.add(background, layer=0)
610
611
player = Player()
612
all_sprites.add(player, layer=2)
613
614
bullets = pygame.sprite.Group()
615
616
running = True
617
while running:
618
for event in pygame.event.get():
619
if event.type == pygame.QUIT:
620
running = False
621
elif event.type == pygame.KEYDOWN:
622
if event.key == pygame.K_SPACE:
623
bullet = Bullet(player.rect.centerx, player.rect.top)
624
all_sprites.add(bullet, layer=1) # Behind player
625
bullets.add(bullet)
626
627
all_sprites.update()
628
629
screen.fill((0, 0, 0))
630
all_sprites.draw(screen) # Draws in layer order
631
632
pygame.display.flip()
633
clock.tick(60)
634
635
pygame.quit()
636
```
637
638
### Performance Optimization with Dirty Rectangles
639
640
```python
641
import pygame
642
643
class MovingSprite(pygame.sprite.DirtySprite):
644
def __init__(self, x, y, color):
645
super().__init__()
646
647
self.image = pygame.Surface((20, 20))
648
self.image.fill(color)
649
self.rect = self.image.get_rect()
650
self.rect.center = (x, y)
651
652
# DirtySprite attributes
653
self.dirty = 1 # Start dirty
654
655
self.vel_x = random.uniform(-3, 3)
656
self.vel_y = random.uniform(-3, 3)
657
658
def update(self):
659
old_rect = self.rect.copy()
660
661
self.rect.x += self.vel_x
662
self.rect.y += self.vel_y
663
664
# Bounce off walls
665
if self.rect.left < 0 or self.rect.right > 800:
666
self.vel_x *= -1
667
if self.rect.top < 0 or self.rect.bottom > 600:
668
self.vel_y *= -1
669
670
# Mark as dirty if moved
671
if self.rect != old_rect:
672
self.dirty = 1
673
674
import random
675
676
pygame.init()
677
screen = pygame.display.set_mode((800, 600))
678
clock = pygame.time.Clock()
679
680
# Use RenderUpdates for efficient screen updates
681
sprites = pygame.sprite.RenderUpdates()
682
683
# Create many moving sprites
684
for _ in range(50):
685
color = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
686
sprite = MovingSprite(random.randint(0, 800), random.randint(0, 600), color)
687
sprites.add(sprite)
688
689
background = pygame.Surface(screen.get_size())
690
background.fill((30, 30, 30))
691
692
running = True
693
while running:
694
for event in pygame.event.get():
695
if event.type == pygame.QUIT:
696
running = False
697
698
sprites.update()
699
700
# Only update changed areas
701
dirty_rects = sprites.draw(screen)
702
pygame.display.update(dirty_rects)
703
704
# Clear sprites for next frame (only dirty areas)
705
sprites.clear(screen, background)
706
707
clock.tick(60)
708
709
pygame.quit()
710
```