or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdaudio-system.mdcore-system.mdcursor-management.mddisplay-graphics.mdevent-handling.mdindex.mdinput-systems.mdmath-operations.mdmidi-support.mdsprites-game-objects.mdtyping-support.md

sprites-game-objects.mddocs/

0

# Sprites and Game Objects

1

2

High-level game object management with sprite classes, collision detection, and group operations. Provides organized structure for managing game entities and their interactions.

3

4

## Capabilities

5

6

### Sprite Base Class

7

8

The Sprite class provides the foundation for all visible game objects.

9

10

```python { .api }

11

class Sprite:

12

def __init__(self):

13

"""

14

Initialize sprite object.

15

Must set image and rect attributes after creation.

16

"""

17

18

image: Surface # Sprite's visual representation

19

rect: Rect # Sprite's position and collision bounds

20

21

def update(self, *args, **kwargs) -> None:

22

"""

23

Update sprite state.

24

Override this method in subclasses for custom behavior.

25

26

Parameters:

27

*args: Variable arguments passed from group update

28

**kwargs: Keyword arguments passed from group update

29

"""

30

31

def add(self, *groups: Group) -> None:

32

"""

33

Add sprite to groups.

34

35

Parameters:

36

*groups: Groups to add sprite to

37

"""

38

39

def remove(self, *groups: Group) -> None:

40

"""

41

Remove sprite from groups.

42

43

Parameters:

44

*groups: Groups to remove sprite from

45

"""

46

47

def kill(self) -> None:

48

"""Remove sprite from all groups."""

49

50

def alive(self) -> bool:

51

"""

52

Check if sprite belongs to any groups.

53

54

Returns:

55

bool: True if sprite is in at least one group

56

"""

57

58

def groups(self) -> list[Group]:

59

"""

60

Get list of groups containing this sprite.

61

62

Returns:

63

list[Group]: Groups this sprite belongs to

64

"""

65

```

66

67

### Dirty Sprite

68

69

Enhanced sprite with dirty rectangle tracking for optimized rendering.

70

71

```python { .api }

72

class DirtySprite(Sprite):

73

def __init__(self):

74

"""Initialize dirty sprite with additional attributes."""

75

76

dirty: int = 1 # Dirty flag (0=clean, 1=dirty, 2=always dirty)

77

source_rect: Rect | None = None # Source rectangle for partial image blitting

78

visible: int = 1 # Visibility flag (0=invisible, 1=visible)

79

blendmode: int = 0 # Blend mode for rendering

80

```

81

82

### Group Management

83

84

Groups provide containers for managing collections of sprites.

85

86

```python { .api }

87

class Group:

88

def __init__(self, *sprites: Sprite):

89

"""

90

Initialize group with optional sprites.

91

92

Parameters:

93

*sprites: Initial sprites to add to group

94

"""

95

96

def sprites(self) -> list[Sprite]:

97

"""

98

Get list of sprites in group.

99

100

Returns:

101

list[Sprite]: Sprites in this group

102

"""

103

104

def copy(self) -> Group:

105

"""

106

Create copy of group.

107

108

Returns:

109

Group: New group with same sprites

110

"""

111

112

def add(self, *sprites: Sprite) -> None:

113

"""

114

Add sprites to group.

115

116

Parameters:

117

*sprites: Sprites to add

118

"""

119

120

def remove(self, *sprites: Sprite) -> None:

121

"""

122

Remove sprites from group.

123

124

Parameters:

125

*sprites: Sprites to remove

126

"""

127

128

def has(self, *sprites: Sprite) -> bool:

129

"""

130

Check if group contains sprites.

131

132

Parameters:

133

*sprites: Sprites to check for

134

135

Returns:

136

bool: True if all sprites are in group

137

"""

138

139

def update(self, *args, **kwargs) -> None:

140

"""

141

Call update() on all sprites in group.

142

143

Parameters:

144

*args: Arguments to pass to sprite update methods

145

**kwargs: Keyword arguments to pass to sprite update methods

146

"""

147

148

def draw(self, surface: Surface) -> list[Rect]:

149

"""

150

Draw all sprites to surface.

151

152

Parameters:

153

surface: Surface to draw sprites on

154

155

Returns:

156

list[Rect]: List of rectangles that were drawn

157

"""

158

159

def clear(self, surface: Surface, background: Surface | Color) -> list[Rect]:

160

"""

161

Clear sprites from surface using background.

162

163

Parameters:

164

surface: Surface to clear sprites from

165

background: Background to use for clearing

166

167

Returns:

168

list[Rect]: List of rectangles that were cleared

169

"""

170

171

def empty(self) -> None:

172

"""Remove all sprites from group."""

173

174

def __len__(self) -> int:

175

"""Get number of sprites in group."""

176

177

def __iter__(self):

178

"""Iterate over sprites in group."""

179

180

def __contains__(self, sprite: Sprite) -> bool:

181

"""Check if sprite is in group."""

182

```

183

184

### Specialized Groups

185

186

Enhanced group classes with additional functionality.

187

188

```python { .api }

189

class RenderUpdates(Group):

190

"""Group that tracks dirty rectangles for efficient rendering."""

191

192

def draw(self, surface: Surface) -> list[Rect]:

193

"""

194

Draw sprites and return changed rectangles.

195

196

Parameters:

197

surface: Surface to draw on

198

199

Returns:

200

list[Rect]: Rectangles that changed

201

"""

202

203

class OrderedUpdates(RenderUpdates):

204

"""RenderUpdates that maintains sprite order."""

205

206

def add(self, *sprites: Sprite) -> None:

207

"""Add sprites maintaining order."""

208

209

def remove(self, *sprites: Sprite) -> None:

210

"""Remove sprites maintaining order."""

211

212

class LayeredUpdates(Group):

213

"""Group with layered rendering support."""

214

215

def add(self, *sprites: Sprite, **kwargs) -> None:

216

"""

217

Add sprites to specific layer.

218

219

Parameters:

220

*sprites: Sprites to add

221

**kwargs: Can include 'layer' parameter

222

"""

223

224

def change_layer(self, sprite: Sprite, new_layer: int) -> None:

225

"""

226

Move sprite to different layer.

227

228

Parameters:

229

sprite: Sprite to move

230

new_layer: Target layer

231

"""

232

233

def get_layer_of_sprite(self, sprite: Sprite) -> int:

234

"""

235

Get layer of sprite.

236

237

Parameters:

238

sprite: Sprite to check

239

240

Returns:

241

int: Layer number

242

"""

243

244

def get_top_layer(self) -> int:

245

"""Get highest layer number."""

246

247

def get_bottom_layer(self) -> int:

248

"""Get lowest layer number."""

249

250

def move_to_front(self, sprite: Sprite) -> None:

251

"""Move sprite to front (highest layer)."""

252

253

def move_to_back(self, sprite: Sprite) -> None:

254

"""Move sprite to back (lowest layer)."""

255

256

def get_top_sprite(self) -> Sprite | None:

257

"""Get topmost sprite."""

258

259

def get_sprites_at(self, pos: tuple[int, int]) -> list[Sprite]:

260

"""

261

Get sprites at position.

262

263

Parameters:

264

pos: (x, y) position to check

265

266

Returns:

267

list[Sprite]: Sprites at position, ordered by layer

268

"""

269

270

def layers(self) -> list[int]:

271

"""Get list of all layers."""

272

273

def draw(self, surface: Surface) -> list[Rect]:

274

"""Draw sprites in layer order."""

275

276

class LayeredDirty(LayeredUpdates):

277

"""LayeredUpdates with dirty rectangle optimization."""

278

279

def draw(self, surface: Surface, bgd: Surface | None = None) -> list[Rect]:

280

"""

281

Draw only dirty sprites.

282

283

Parameters:

284

surface: Surface to draw on

285

bgd: Background surface for clearing

286

287

Returns:

288

list[Rect]: Changed rectangles

289

"""

290

291

def clear(self, surface: Surface, bgd: Surface) -> None:

292

"""Clear dirty sprites using background."""

293

294

def repaint_rect(self, screen_rect: Rect) -> None:

295

"""Mark screen area as needing repaint."""

296

297

def set_clip(self, screen_rect: Rect | None = None) -> None:

298

"""Set clipping rectangle for drawing."""

299

300

class GroupSingle(Group):

301

"""Group that contains only one sprite."""

302

303

sprite: Sprite | None # The single sprite in the group

304

305

def __init__(self, sprite: Sprite | None = None):

306

"""

307

Initialize single sprite group.

308

309

Parameters:

310

sprite: Initial sprite (optional)

311

"""

312

```

313

314

### Collision Detection

315

316

Functions for detecting collisions between sprites and groups.

317

318

```python { .api }

319

def spritecollide(sprite: Sprite, group: Group, dokill: bool, collided: callable | None = None) -> list[Sprite]:

320

"""

321

Find sprites in group that collide with sprite.

322

323

Parameters:

324

sprite: Sprite to test collision against

325

group: Group of sprites to check

326

dokill: If True, remove colliding sprites from group

327

collided: Custom collision function (default uses rect collision)

328

329

Returns:

330

list[Sprite]: Sprites that collided

331

"""

332

333

def groupcollide(group1: Group, group2: Group, dokill1: bool, dokill2: bool, collided: callable | None = None) -> dict[Sprite, list[Sprite]]:

334

"""

335

Find collisions between two groups.

336

337

Parameters:

338

group1: First group

339

group2: Second group

340

dokill1: Remove colliding sprites from group1

341

dokill2: Remove colliding sprites from group2

342

collided: Custom collision function

343

344

Returns:

345

dict[Sprite, list[Sprite]]: Mapping of group1 sprites to colliding group2 sprites

346

"""

347

348

def spritecollideany(sprite: Sprite, group: Group, collided: callable | None = None) -> Sprite | None:

349

"""

350

Check if sprite collides with any sprite in group.

351

352

Parameters:

353

sprite: Sprite to test

354

group: Group to check against

355

collided: Custom collision function

356

357

Returns:

358

Sprite | None: First colliding sprite or None

359

"""

360

```

361

362

### Collision Callbacks

363

364

Built-in collision detection functions.

365

366

```python { .api }

367

def collide_rect(left: Sprite, right: Sprite) -> bool:

368

"""

369

Rectangle collision detection.

370

371

Parameters:

372

left: First sprite

373

right: Second sprite

374

375

Returns:

376

bool: True if rectangles overlap

377

"""

378

379

def collide_rect_ratio(ratio: float) -> callable:

380

"""

381

Create rectangle collision function with size ratio.

382

383

Parameters:

384

ratio: Size ratio (1.0 = normal, 0.5 = half size, 2.0 = double size)

385

386

Returns:

387

callable: Collision function

388

"""

389

390

def collide_circle(left: Sprite, right: Sprite) -> bool:

391

"""

392

Circle collision detection using sprite radius.

393

394

Parameters:

395

left: First sprite (must have radius attribute)

396

right: Second sprite (must have radius attribute)

397

398

Returns:

399

bool: True if circles overlap

400

"""

401

402

def collide_circle_ratio(ratio: float) -> callable:

403

"""

404

Create circle collision function with radius ratio.

405

406

Parameters:

407

ratio: Radius ratio

408

409

Returns:

410

callable: Collision function

411

"""

412

413

def collide_mask(left: Sprite, right: Sprite) -> tuple[int, int] | None:

414

"""

415

Pixel-perfect collision using sprite masks.

416

417

Parameters:

418

left: First sprite (must have mask attribute)

419

right: Second sprite (must have mask attribute)

420

421

Returns:

422

tuple[int, int] | None: Collision point or None

423

"""

424

```

425

426

## Usage Examples

427

428

### Basic Sprite Usage

429

430

```python

431

import pygame

432

433

class Player(pygame.sprite.Sprite):

434

def __init__(self):

435

super().__init__()

436

self.image = pygame.Surface((50, 50))

437

self.image.fill((0, 128, 255)) # Blue

438

self.rect = self.image.get_rect()

439

self.rect.center = (400, 300)

440

self.speed = 5

441

442

def update(self):

443

# Handle input

444

keys = pygame.key.get_pressed()

445

if keys[pygame.K_LEFT]:

446

self.rect.x -= self.speed

447

if keys[pygame.K_RIGHT]:

448

self.rect.x += self.speed

449

if keys[pygame.K_UP]:

450

self.rect.y -= self.speed

451

if keys[pygame.K_DOWN]:

452

self.rect.y += self.speed

453

454

class Enemy(pygame.sprite.Sprite):

455

def __init__(self, x, y):

456

super().__init__()

457

self.image = pygame.Surface((30, 30))

458

self.image.fill((255, 0, 0)) # Red

459

self.rect = self.image.get_rect()

460

self.rect.center = (x, y)

461

self.speed = 2

462

463

def update(self):

464

# Simple movement

465

self.rect.x += self.speed

466

if self.rect.right > 800 or self.rect.left < 0:

467

self.speed = -self.speed

468

469

# Create sprites

470

player = Player()

471

enemy1 = Enemy(100, 100)

472

enemy2 = Enemy(200, 200)

473

474

# Create groups

475

all_sprites = pygame.sprite.Group()

476

enemies = pygame.sprite.Group()

477

478

all_sprites.add(player, enemy1, enemy2)

479

enemies.add(enemy1, enemy2)

480

481

# Game loop

482

pygame.init()

483

screen = pygame.display.set_mode((800, 600))

484

clock = pygame.time.Clock()

485

running = True

486

487

while running:

488

for event in pygame.event.get():

489

if event.type == pygame.QUIT:

490

running = False

491

492

# Update all sprites

493

all_sprites.update()

494

495

# Check collisions

496

hits = pygame.sprite.spritecollide(player, enemies, False)

497

if hits:

498

print("Player hit enemy!")

499

500

# Draw everything

501

screen.fill((0, 0, 0))

502

all_sprites.draw(screen)

503

pygame.display.flip()

504

clock.tick(60)

505

506

pygame.quit()

507

```

508

509

### Layered Sprites

510

511

```python

512

import pygame

513

514

class Background(pygame.sprite.Sprite):

515

def __init__(self):

516

super().__init__()

517

self.image = pygame.Surface((800, 600))

518

self.image.fill((0, 100, 0)) # Green background

519

self.rect = self.image.get_rect()

520

521

class Player(pygame.sprite.Sprite):

522

def __init__(self):

523

super().__init__()

524

self.image = pygame.Surface((50, 50))

525

self.image.fill((0, 0, 255)) # Blue player

526

self.rect = self.image.get_rect(center=(400, 300))

527

528

class Overlay(pygame.sprite.Sprite):

529

def __init__(self):

530

super().__init__()

531

self.image = pygame.Surface((200, 100))

532

self.image.fill((255, 255, 0)) # Yellow overlay

533

self.image.set_alpha(128) # Semi-transparent

534

self.rect = self.image.get_rect(topleft=(10, 10))

535

536

# Create layered group

537

layered_group = pygame.sprite.LayeredUpdates()

538

539

# Add sprites to different layers

540

background = Background()

541

player = Player()

542

overlay = Overlay()

543

544

layered_group.add(background, layer=0) # Background layer

545

layered_group.add(player, layer=1) # Player layer

546

layered_group.add(overlay, layer=2) # UI layer

547

548

# Game loop

549

pygame.init()

550

screen = pygame.display.set_mode((800, 600))

551

clock = pygame.time.Clock()

552

running = True

553

554

while running:

555

for event in pygame.event.get():

556

if event.type == pygame.QUIT:

557

running = False

558

559

# Update and draw in layer order

560

layered_group.update()

561

layered_group.draw(screen)

562

563

pygame.display.flip()

564

clock.tick(60)

565

566

pygame.quit()

567

```

568

569

### Advanced Collision Detection

570

571

```python

572

import pygame

573

import math

574

575

class CircleSprite(pygame.sprite.Sprite):

576

def __init__(self, x, y, radius, color):

577

super().__init__()

578

self.radius = radius

579

# Create circular image

580

self.image = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)

581

pygame.draw.circle(self.image, color, (radius, radius), radius)

582

self.rect = self.image.get_rect(center=(x, y))

583

584

class Bullet(pygame.sprite.Sprite):

585

def __init__(self, x, y):

586

super().__init__()

587

self.image = pygame.Surface((10, 20))

588

self.image.fill((255, 255, 0)) # Yellow bullet

589

self.rect = self.image.get_rect(center=(x, y))

590

self.speed = 10

591

592

def update(self):

593

self.rect.y -= self.speed

594

if self.rect.bottom < 0:

595

self.kill()

596

597

# Create sprites

598

player = CircleSprite(400, 500, 25, (0, 0, 255))

599

enemies = pygame.sprite.Group()

600

bullets = pygame.sprite.Group()

601

all_sprites = pygame.sprite.Group()

602

603

# Add enemies

604

for i in range(5):

605

enemy = CircleSprite(100 + i * 150, 100, 30, (255, 0, 0))

606

enemies.add(enemy)

607

all_sprites.add(enemy)

608

609

all_sprites.add(player)

610

611

pygame.init()

612

screen = pygame.display.set_mode((800, 600))

613

clock = pygame.time.Clock()

614

running = True

615

616

while running:

617

for event in pygame.event.get():

618

if event.type == pygame.QUIT:

619

running = False

620

elif event.type == pygame.KEYDOWN:

621

if event.key == pygame.K_SPACE:

622

# Shoot bullet

623

bullet = Bullet(player.rect.centerx, player.rect.top)

624

bullets.add(bullet)

625

all_sprites.add(bullet)

626

627

# Update sprites

628

all_sprites.update()

629

630

# Collision detection with custom callback

631

hits = pygame.sprite.groupcollide(

632

bullets, enemies, True, True,

633

pygame.sprite.collide_circle

634

)

635

636

if hits:

637

print(f"Hit {len(hits)} enemies!")

638

639

# Draw everything

640

screen.fill((0, 0, 0))

641

all_sprites.draw(screen)

642

pygame.display.flip()

643

clock.tick(60)

644

645

pygame.quit()

646

```

647

648

### Optimized Rendering with Dirty Sprites

649

650

```python

651

import pygame

652

653

class MovingSprite(pygame.sprite.DirtySprite):

654

def __init__(self, x, y):

655

super().__init__()

656

self.image = pygame.Surface((50, 50))

657

self.image.fill((255, 0, 0))

658

self.rect = self.image.get_rect(center=(x, y))

659

self.speed_x = 3

660

self.speed_y = 2

661

self.dirty = 1 # Mark as dirty initially

662

663

def update(self):

664

old_rect = self.rect.copy()

665

666

# Move sprite

667

self.rect.x += self.speed_x

668

self.rect.y += self.speed_y

669

670

# Bounce off edges

671

if self.rect.left < 0 or self.rect.right > 800:

672

self.speed_x = -self.speed_x

673

if self.rect.top < 0 or self.rect.bottom > 600:

674

self.speed_y = -self.speed_y

675

676

# Mark as dirty if moved

677

if self.rect != old_rect:

678

self.dirty = 1

679

680

# Create dirty group for optimized rendering

681

dirty_group = pygame.sprite.LayeredDirty()

682

683

# Add moving sprites

684

for i in range(10):

685

sprite = MovingSprite(i * 70 + 50, i * 50 + 50)

686

dirty_group.add(sprite)

687

688

pygame.init()

689

screen = pygame.display.set_mode((800, 600))

690

background = pygame.Surface((800, 600))

691

background.fill((0, 0, 100)) # Dark blue background

692

693

clock = pygame.time.Clock()

694

running = True

695

696

while running:

697

for event in pygame.event.get():

698

if event.type == pygame.QUIT:

699

running = False

700

701

# Update sprites

702

dirty_group.update()

703

704

# Only redraw dirty areas

705

dirty_rects = dirty_group.draw(screen, background)

706

707

# Update only changed areas

708

pygame.display.update(dirty_rects)

709

clock.tick(60)

710

711

pygame.quit()

712

```