0
# Sprite and Animation System
1
2
High-level sprite management, animation, and rendering systems for 2D games and applications. The PySDL2 extension module provides object-oriented sprite classes and efficient batch rendering systems.
3
4
## Capabilities
5
6
### Sprite Classes
7
8
Core sprite classes for representing game objects and visual elements.
9
10
```python { .api }
11
class Sprite:
12
"""
13
Base sprite class for 2D game objects.
14
15
Attributes:
16
- x: horizontal position
17
- y: vertical position
18
- size: (width, height) tuple
19
"""
20
21
def __init__(self, x: int = 0, y: int = 0):
22
"""
23
Create sprite at position.
24
25
Parameters:
26
- x: initial X coordinate
27
- y: initial Y coordinate
28
"""
29
30
@property
31
def position(self) -> tuple[int, int]:
32
"""Get sprite position as (x, y) tuple."""
33
34
@position.setter
35
def position(self, value: tuple[int, int]) -> None:
36
"""Set sprite position from (x, y) tuple."""
37
38
@property
39
def size(self) -> tuple[int, int]:
40
"""Get sprite size as (width, height) tuple."""
41
42
class SoftwareSprite(Sprite):
43
"""
44
Software-rendered sprite using SDL surfaces.
45
46
Uses CPU-based blitting for rendering. Best for applications with
47
few sprites or when hardware acceleration is unavailable.
48
"""
49
50
def __init__(self, surface: SDL_Surface, free: bool = False, x: int = 0, y: int = 0):
51
"""
52
Create software sprite from surface.
53
54
Parameters:
55
- surface: SDL_Surface containing sprite image
56
- free: whether to free surface when sprite is destroyed
57
- x, y: initial position
58
"""
59
60
class TextureSprite(Sprite):
61
"""
62
Hardware-accelerated sprite using SDL textures.
63
64
Uses GPU-based rendering for better performance. Requires
65
hardware-accelerated renderer.
66
"""
67
68
def __init__(self, texture: SDL_Texture, x: int = 0, y: int = 0):
69
"""
70
Create texture sprite from texture.
71
72
Parameters:
73
- texture: SDL_Texture containing sprite image
74
- x, y: initial position
75
"""
76
```
77
78
### Sprite Factory
79
80
Factory class for creating sprites from various sources.
81
82
```python { .api }
83
class SpriteFactory:
84
"""Factory for creating sprites from images and other sources."""
85
86
def __init__(self, sprite_type: int = TEXTURE, renderer: Renderer = None,
87
fontmanager: FontManager = None):
88
"""
89
Create sprite factory.
90
91
Parameters:
92
- sprite_type: TEXTURE for TextureSprite, SOFTWARE for SoftwareSprite
93
- renderer: Renderer for texture sprites (required for TEXTURE type)
94
- fontmanager: FontManager for text sprites
95
"""
96
97
def from_image(self, fname: str) -> Sprite:
98
"""
99
Create sprite from image file.
100
101
Parameters:
102
- fname: path to image file
103
104
Returns:
105
Sprite object loaded from image
106
"""
107
108
def from_surface(self, surface: SDL_Surface, free: bool = False) -> Sprite:
109
"""
110
Create sprite from SDL surface.
111
112
Parameters:
113
- surface: SDL_Surface to create sprite from
114
- free: whether to free surface when sprite is destroyed
115
116
Returns:
117
Sprite object created from surface
118
"""
119
120
def from_color(self, color: tuple[int, int, int, int], size: tuple[int, int]) -> Sprite:
121
"""
122
Create solid color sprite.
123
124
Parameters:
125
- color: RGBA color tuple (0-255 each component)
126
- size: (width, height) sprite dimensions
127
128
Returns:
129
Sprite filled with solid color
130
"""
131
132
def from_text(self, text: str, font: FontTTF = None, size: int = 12,
133
color: tuple[int, int, int] = (255, 255, 255)) -> Sprite:
134
"""
135
Create text sprite.
136
137
Parameters:
138
- text: text string to render
139
- font: font to use (None for default)
140
- size: font size in points
141
- color: RGB text color
142
143
Returns:
144
Sprite containing rendered text
145
"""
146
147
def create_texture_sprite(self, renderer: Renderer, size: tuple[int, int]) -> TextureSprite:
148
"""
149
Create empty texture sprite for custom drawing.
150
151
Parameters:
152
- renderer: renderer to create texture for
153
- size: (width, height) texture dimensions
154
155
Returns:
156
Empty TextureSprite that can be drawn to
157
"""
158
```
159
160
### Sprite Factory Constants
161
162
```python { .api }
163
TEXTURE: int = 1 # Create TextureSprite objects
164
SOFTWARE: int = 2 # Create SoftwareSprite objects
165
```
166
167
### Sprite Rendering Systems
168
169
Efficient batch rendering systems for sprites.
170
171
```python { .api }
172
class SpriteRenderSystem:
173
"""Base class for sprite rendering systems."""
174
175
def render(self, sprites: list[Sprite]) -> None:
176
"""
177
Render list of sprites.
178
179
Parameters:
180
- sprites: list of Sprite objects to render
181
"""
182
183
class SoftwareSpriteRenderSystem(SpriteRenderSystem):
184
"""Software-based sprite rendering system using surface blitting."""
185
186
def __init__(self, window: Window):
187
"""
188
Create software sprite renderer.
189
190
Parameters:
191
- window: Window to render sprites to
192
"""
193
194
def render(self, sprites: list[Sprite]) -> None:
195
"""Render sprites using CPU blitting."""
196
197
class TextureSpriteRenderSystem(SpriteRenderSystem):
198
"""Hardware-accelerated sprite rendering system using textures."""
199
200
def __init__(self, renderer: Renderer):
201
"""
202
Create texture sprite renderer.
203
204
Parameters:
205
- renderer: Renderer to use for sprite rendering
206
"""
207
208
def render(self, sprites: list[Sprite]) -> None:
209
"""Render sprites using GPU acceleration."""
210
211
def render_sprite(self, sprite: TextureSprite, x: int = None, y: int = None,
212
area: SDL_Rect = None) -> None:
213
"""
214
Render single sprite.
215
216
Parameters:
217
- sprite: TextureSprite to render
218
- x, y: override position (None to use sprite position)
219
- area: source area to render (None for entire sprite)
220
"""
221
```
222
223
### Entity-Component System
224
225
Framework for managing game entities with component-based architecture.
226
227
```python { .api }
228
class Entity:
229
"""
230
Game entity in entity-component system.
231
232
Entities are containers for components that define behavior
233
and properties.
234
"""
235
236
def __init__(self, world: World = None, *args, **kwargs):
237
"""
238
Create entity.
239
240
Parameters:
241
- world: World to add entity to
242
- args, kwargs: additional arguments
243
"""
244
245
class World:
246
"""
247
Container for entities and systems.
248
249
Manages entity lifecycle and system processing.
250
"""
251
252
def __init__(self):
253
"""Create empty world."""
254
255
def add_system(self, system: System) -> None:
256
"""
257
Add system to world.
258
259
Parameters:
260
- system: System to add
261
"""
262
263
def remove_system(self, system: System) -> None:
264
"""
265
Remove system from world.
266
267
Parameters:
268
- system: System to remove
269
"""
270
271
def get_entities(self, component: type) -> list[Entity]:
272
"""
273
Get entities that have specific component.
274
275
Parameters:
276
- component: component type to search for
277
278
Returns:
279
List of entities with the component
280
"""
281
282
def process(self) -> None:
283
"""Process all systems in the world."""
284
285
class System:
286
"""
287
Base class for entity-component systems.
288
289
Systems process entities that have specific components.
290
"""
291
292
def __init__(self):
293
"""Create system."""
294
295
def process(self, world: World, components: dict) -> None:
296
"""
297
Process entities with required components.
298
299
Parameters:
300
- world: World containing entities
301
- components: dictionary of component types to process
302
"""
303
304
class Applicator:
305
"""
306
Utility for applying functions to entities with specific components.
307
308
Provides functional programming interface for entity processing.
309
"""
310
311
def __init__(self, world: World):
312
"""
313
Create applicator for world.
314
315
Parameters:
316
- world: World to operate on
317
"""
318
319
def apply(self, func: callable, *component_types) -> None:
320
"""
321
Apply function to entities with specified components.
322
323
Parameters:
324
- func: function to apply to matching entities
325
- component_types: component types entities must have
326
"""
327
```
328
329
### Particle System
330
331
Basic particle system for visual effects.
332
333
```python { .api }
334
class ParticleEngine:
335
"""
336
Basic particle system for effects.
337
338
Note: This is from the particles module which may have encoding issues.
339
Check source if this doesn't work as expected.
340
"""
341
342
def __init__(self):
343
"""Create particle engine."""
344
345
def process(self, world: World, componentsets: dict) -> None:
346
"""Process particle components."""
347
```
348
349
## Usage Examples
350
351
### Basic Sprite Creation and Rendering
352
353
```python
354
import sdl2.ext
355
356
# Initialize SDL2 extensions
357
sdl2.ext.init()
358
359
# Create window and renderer
360
window = sdl2.ext.Window("Sprite Example", size=(800, 600))
361
window.show()
362
renderer = sdl2.ext.Renderer(window)
363
364
# Create sprite factory for texture sprites
365
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=renderer)
366
367
# Create sprite from image file
368
player_sprite = factory.from_image("player.png")
369
player_sprite.position = (100, 100)
370
371
# Create colored sprite
372
enemy_sprite = factory.from_color((255, 0, 0, 255), (32, 32)) # Red 32x32 square
373
enemy_sprite.position = (200, 150)
374
375
# Create rendering system
376
render_system = sdl2.ext.TextureSpriteRenderSystem(renderer)
377
378
# Main game loop
379
running = True
380
while running:
381
# Handle events
382
events = sdl2.ext.get_events()
383
for event in events:
384
if event.type == sdl2.SDL_QUIT:
385
running = False
386
elif event.type == sdl2.SDL_KEYDOWN:
387
# Move player sprite with arrow keys
388
if event.key.keysym.sym == sdl2.SDLK_LEFT:
389
player_sprite.x -= 5
390
elif event.key.keysym.sym == sdl2.SDLK_RIGHT:
391
player_sprite.x += 5
392
elif event.key.keysym.sym == sdl2.SDLK_UP:
393
player_sprite.y -= 5
394
elif event.key.keysym.sym == sdl2.SDLK_DOWN:
395
player_sprite.y += 5
396
397
# Clear screen
398
renderer.clear((0, 50, 100))
399
400
# Render sprites
401
render_system.render([player_sprite, enemy_sprite])
402
403
# Present frame
404
renderer.present()
405
406
sdl2.ext.quit()
407
```
408
409
### Software Sprite Rendering
410
411
```python
412
import sdl2.ext
413
414
# Initialize with software rendering
415
sdl2.ext.init()
416
window = sdl2.ext.Window("Software Sprites", size=(640, 480))
417
window.show()
418
419
# Create software sprite factory
420
factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE)
421
422
# Create sprites from images
423
sprites = []
424
for i in range(5):
425
sprite = factory.from_image(f"sprite_{i}.bmp")
426
sprite.position = (i * 100, 100)
427
sprites.append(sprite)
428
429
# Create software rendering system
430
render_system = sdl2.ext.SoftwareSpriteRenderSystem(window)
431
432
# Main loop
433
running = True
434
while running:
435
events = sdl2.ext.get_events()
436
for event in events:
437
if event.type == sdl2.SDL_QUIT:
438
running = False
439
440
# Move sprites
441
for sprite in sprites:
442
sprite.y = 100 + 50 * math.sin((sprite.x + frame_count) * 0.05)
443
444
# Render all sprites
445
render_system.render(sprites)
446
447
frame_count += 1
448
449
sdl2.ext.quit()
450
```
451
452
### Entity-Component System Example
453
454
```python
455
import sdl2.ext
456
457
# Define components
458
class Position:
459
def __init__(self, x=0, y=0):
460
self.x = x
461
self.y = y
462
463
class Velocity:
464
def __init__(self, vx=0, vy=0):
465
self.vx = vx
466
self.vy = vy
467
468
class Renderable:
469
def __init__(self, sprite):
470
self.sprite = sprite
471
472
# Define movement system
473
class MovementSystem(sdl2.ext.System):
474
def __init__(self):
475
super().__init__()
476
self.componenttypes = (Position, Velocity)
477
478
def process(self, world, componentsets):
479
for entity in componentsets:
480
position = entity[Position]
481
velocity = entity[Velocity]
482
483
position.x += velocity.vx
484
position.y += velocity.vy
485
486
# Wrap around screen edges
487
if position.x < 0:
488
position.x = 800
489
elif position.x > 800:
490
position.x = 0
491
492
# Initialize SDL2
493
sdl2.ext.init()
494
window = sdl2.ext.Window("ECS Example", size=(800, 600))
495
window.show()
496
renderer = sdl2.ext.Renderer(window)
497
498
# Create world and systems
499
world = sdl2.ext.World()
500
movement_system = MovementSystem()
501
render_system = sdl2.ext.TextureSpriteRenderSystem(renderer)
502
503
world.add_system(movement_system)
504
505
# Create sprite factory
506
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=renderer)
507
508
# Create entities
509
for i in range(10):
510
entity = sdl2.ext.Entity(world)
511
512
# Add components
513
entity.components[Position] = Position(i * 80, 200)
514
entity.components[Velocity] = Velocity(random.randint(-3, 3), random.randint(-2, 2))
515
516
sprite = factory.from_color((255, 255, 0, 255), (16, 16))
517
entity.components[Renderable] = Renderable(sprite)
518
519
# Main loop
520
running = True
521
while running:
522
events = sdl2.ext.get_events()
523
for event in events:
524
if event.type == sdl2.SDL_QUIT:
525
running = False
526
527
# Process systems
528
world.process()
529
530
# Render
531
renderer.clear((0, 0, 0))
532
533
# Get all entities with Position and Renderable components
534
renderable_entities = world.get_entities((Position, Renderable))
535
sprites_to_render = []
536
537
for entity in renderable_entities:
538
position = entity.components[Position]
539
renderable = entity.components[Renderable]
540
541
renderable.sprite.position = (position.x, position.y)
542
sprites_to_render.append(renderable.sprite)
543
544
render_system.render(sprites_to_render)
545
renderer.present()
546
547
sdl2.ext.quit()
548
```
549
550
### Sprite Animation Example
551
552
```python
553
import sdl2.ext
554
import time
555
556
# Initialize SDL2
557
sdl2.ext.init()
558
window = sdl2.ext.Window("Sprite Animation", size=(800, 600))
559
window.show()
560
renderer = sdl2.ext.Renderer(window)
561
562
# Create sprite factory
563
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=renderer)
564
565
# Load animation frames
566
animation_frames = []
567
for i in range(8): # Assuming 8 frame animation
568
frame = factory.from_image(f"animation_frame_{i}.png")
569
animation_frames.append(frame)
570
571
# Animation variables
572
current_frame = 0
573
frame_duration = 0.1 # 100ms per frame
574
last_frame_time = time.time()
575
576
# Create rendering system
577
render_system = sdl2.ext.TextureSpriteRenderSystem(renderer)
578
579
# Main loop
580
running = True
581
while running:
582
current_time = time.time()
583
584
events = sdl2.ext.get_events()
585
for event in events:
586
if event.type == sdl2.SDL_QUIT:
587
running = False
588
589
# Update animation
590
if current_time - last_frame_time >= frame_duration:
591
current_frame = (current_frame + 1) % len(animation_frames)
592
last_frame_time = current_time
593
594
# Position current frame
595
current_sprite = animation_frames[current_frame]
596
current_sprite.position = (350, 250)
597
598
# Render
599
renderer.clear((0, 0, 0))
600
render_system.render([current_sprite])
601
renderer.present()
602
603
sdl2.ext.quit()
604
```