0
# 2D Sprites and Shapes
1
2
Efficient 2D sprite rendering and geometric shape primitives.
3
4
## When to Use This Module
5
6
- Rendering 2D game sprites
7
- Drawing UI elements
8
- Creating particles and effects
9
- Rendering geometric shapes (circles, rectangles, lines)
10
- **Always use with Batch for performance (10-100x faster)**
11
12
## Quick Decision Guide
13
14
```
15
What to render?
16
├─ Image/texture → pyglet.sprite.Sprite
17
├─ Basic shape → pyglet.shapes.*
18
├─ Multiple objects → Always use Batch!
19
└─ Layering needed → Use Groups (order parameter)
20
```
21
22
## Sprites - Quick Reference
23
24
```python
25
# Create sprite
26
image = pyglet.image.load('player.png')
27
sprite = pyglet.sprite.Sprite(image, x=100, y=100)
28
29
# With batching (REQUIRED for multiple sprites)
30
batch = pyglet.graphics.Batch()
31
sprite = pyglet.sprite.Sprite(image, x=100, y=100, batch=batch)
32
33
# Transform
34
sprite.x, sprite.y = 200, 300 # Position
35
sprite.rotation = 45 # Degrees (clockwise)
36
sprite.scale = 2.0 # Uniform scale
37
sprite.opacity = 128 # 0-255
38
sprite.color = (255, 0, 0) # RGB tint
39
40
# Draw
41
batch.draw() # Draws all batched sprites
42
```
43
44
## Shapes - Quick Reference
45
46
```python
47
from pyglet import shapes
48
49
batch = pyglet.graphics.Batch()
50
51
# Common shapes
52
circle = shapes.Circle(x=100, y=100, radius=50, color=(255,0,0), batch=batch)
53
rect = shapes.Rectangle(x=200, y=50, width=100, height=100, color=(0,255,0), batch=batch)
54
line = shapes.Line(x=0, y=0, x2=100, y2=100, width=3, color=(0,0,255), batch=batch)
55
56
# Draw all
57
batch.draw()
58
```
59
60
## Sprite Class
61
62
```python
63
class pyglet.sprite.Sprite:
64
__init__(img, x=0, y=0, z=0, batch=None, group=None,
65
blend_src=GL_SRC_ALPHA, blend_dest=GL_ONE_MINUS_SRC_ALPHA)
66
67
# Transform
68
x, y, z: float
69
position: tuple # (x, y, z)
70
rotation: float # Degrees clockwise
71
scale: float # Uniform scale
72
scale_x, scale_y: float # Non-uniform
73
width, height: int # Adjusts scale when set
74
75
# Appearance
76
opacity: int # 0-255
77
color: tuple # RGB (0-255) multiplier
78
visible: bool
79
image: AbstractImage
80
81
# Rendering
82
batch: Batch
83
group: Group
84
85
# Animation (if image is Animation)
86
paused: bool
87
frame_index: int
88
89
# Methods
90
def draw() # DON'T USE IN PRODUCTION (use batch instead)
91
def delete() # Remove from memory
92
93
# Events (for animations)
94
@sprite.event
95
def on_animation_end(): ...
96
```
97
98
## Batching & Groups (CRITICAL FOR PERFORMANCE)
99
100
### The Batch Pattern (Required for Multiple Objects)
101
102
```python
103
# WRONG: Very slow for multiple sprites
104
sprite1 = pyglet.sprite.Sprite(img, x=0, y=0)
105
sprite2 = pyglet.sprite.Sprite(img, x=100, y=100)
106
107
@window.event
108
def on_draw():
109
window.clear()
110
sprite1.draw() # State change
111
sprite2.draw() # State change
112
# 100 sprites = 100 state changes = SLOW
113
114
# CORRECT: Fast batch rendering
115
batch = pyglet.graphics.Batch()
116
sprite1 = pyglet.sprite.Sprite(img, x=0, y=0, batch=batch)
117
sprite2 = pyglet.sprite.Sprite(img, x=100, y=100, batch=batch)
118
119
@window.event
120
def on_draw():
121
window.clear()
122
batch.draw() # One call = 10-100x faster!
123
```
124
125
### Layering with Groups
126
127
```python
128
batch = pyglet.graphics.Batch()
129
130
# Define layers (lower order drawn first)
131
background = pyglet.graphics.Group(order=0)
132
midground = pyglet.graphics.Group(order=1)
133
foreground = pyglet.graphics.Group(order=2)
134
ui = pyglet.graphics.Group(order=3)
135
136
# Assign sprites to layers
137
bg_sprite = pyglet.sprite.Sprite(bg_img, batch=batch, group=background)
138
player = pyglet.sprite.Sprite(player_img, batch=batch, group=midground)
139
effects = pyglet.sprite.Sprite(effect_img, batch=batch, group=foreground)
140
button = pyglet.sprite.Sprite(btn_img, batch=batch, group=ui)
141
142
# All drawn in correct order automatically
143
batch.draw()
144
```
145
146
## Shape Classes
147
148
All shapes support: `x, y, position, rotation, color, opacity, visible, batch, group`
149
150
### Basic Shapes
151
152
```python
153
from pyglet import shapes
154
155
# Rectangle
156
Rectangle(x, y, width, height, color=(255,255,255,255), batch=None, group=None)
157
158
# Circle
159
Circle(x, y, radius, segments=None, color=(255,255,255,255), batch=None, group=None)
160
161
# Line
162
Line(x, y, x2, y2, thickness=1.0, color=(255,255,255,255), batch=None, group=None)
163
164
# Triangle
165
Triangle(x, y, x2, y2, x3, y3, color=(255,255,255,255), batch=None, group=None)
166
```
167
168
### Styled Shapes
169
170
```python
171
# Bordered rectangle
172
BorderedRectangle(x, y, width, height, border=1,
173
color=(255,255,255), border_color=(100,100,100))
174
175
# Rounded corners
176
RoundedRectangle(x, y, width, height, radius=5, color=(255,255,255,255))
177
178
# 3D-style box
179
Box(x, y, width, height, thickness=10, color=(255,255,255,255))
180
```
181
182
### Advanced Shapes
183
184
```python
185
# Star
186
Star(x, y, outer_radius, inner_radius, num_spikes=5, rotation=0)
187
188
# Polygon (any number of vertices)
189
Polygon(*coordinates, color=(255,255,255,255))
190
# coordinates: [(x1,y1), (x2,y2), (x3,y3), ...]
191
192
# Ellipse
193
Ellipse(x, y, a, b, segments=None, color=(255,255,255,255))
194
# a = semi-major axis, b = semi-minor axis
195
196
# Arc (curved line)
197
Arc(x, y, radius, angle=360.0, start_angle=0, thickness=1.0)
198
199
# Sector (pie slice)
200
Sector(x, y, radius, angle=90, start_angle=0)
201
202
# Bezier curve
203
BezierCurve(*points, t=1.0, segments=100, thickness=1.0)
204
# points: must be 3n+1 points: (x1,y1), (x2,y2), ...
205
```
206
207
## Essential Patterns
208
209
### Sprite Pool (Performance Critical)
210
211
```python
212
class SpritePool:
213
"""Reuse sprites instead of creating/destroying"""
214
def __init__(self, image, batch, size=100):
215
self.active = []
216
self.inactive = [
217
pyglet.sprite.Sprite(image, batch=batch, visible=False)
218
for _ in range(size)
219
]
220
221
def spawn(self, x, y):
222
if not self.inactive:
223
return None
224
sprite = self.inactive.pop()
225
sprite.position = (x, y, 0)
226
sprite.visible = True
227
self.active.append(sprite)
228
return sprite
229
230
def recycle(self, sprite):
231
sprite.visible = False
232
self.active.remove(sprite)
233
self.inactive.append(sprite)
234
```
235
236
### Animation
237
238
```python
239
# Load animated GIF
240
anim = pyglet.image.load_animation('explosion.gif')
241
sprite = pyglet.sprite.Sprite(anim, x=100, y=100, batch=batch)
242
243
# Or create from frames
244
frames = [pyglet.image.load(f'frame{i}.png') for i in range(10)]
245
anim = pyglet.image.Animation.from_image_sequence(
246
frames, duration=0.1, loop=True
247
)
248
sprite = pyglet.sprite.Sprite(anim, batch=batch)
249
250
# Control
251
sprite.paused = True # Pause
252
sprite.frame_index = 0 # Reset
253
254
@sprite.event
255
def on_animation_end():
256
sprite.delete() # Remove when done
257
```
258
259
### Collision Detection
260
261
```python
262
# Point in shape
263
if (mouse_x, mouse_y) in circle:
264
print("Clicked!")
265
266
# AABB (Axis-Aligned Bounding Box)
267
def sprites_collide(s1, s2):
268
return (abs(s1.x - s2.x) < (s1.width + s2.width) / 2 and
269
abs(s1.y - s2.y) < (s1.height + s2.height) / 2)
270
271
# Circular collision
272
def circle_collision(s1, s2, r1, r2):
273
dx = s1.x - s2.x
274
dy = s1.y - s2.y
275
dist_sq = dx*dx + dy*dy
276
return dist_sq < (r1 + r2) ** 2
277
```
278
279
### Dynamic Shape Updates
280
281
```python
282
shape = shapes.Circle(200, 200, 50, batch=batch)
283
284
def update(dt):
285
import math
286
time = pyglet.clock.get_default().time()
287
# Shapes auto-update when properties change
288
shape.x = 400 + 100 * math.cos(time)
289
shape.y = 300 + 100 * math.sin(time)
290
shape.radius = 50 + 20 * math.sin(time * 2)
291
```
292
293
## Group Class
294
295
```python
296
class pyglet.graphics.Group:
297
__init__(order=0, parent=None)
298
299
order: int # Draw order (lower first)
300
parent: Group | None
301
visible: bool # Toggle entire group
302
303
def set_state() # Apply OpenGL state
304
def unset_state() # Restore state
305
306
# Specialized groups
307
TextureGroup(texture, order=0, parent=None)
308
ShaderGroup(program, order=0, parent=None)
309
```
310
311
## Batch Class
312
313
```python
314
class pyglet.graphics.Batch:
315
__init__()
316
317
def draw() # Render all batched items
318
def invalidate() # Mark as needing update
319
```
320
321
## Performance Rules
322
323
### DO:
324
1. ✓ Always use Batch for multiple objects
325
2. ✓ Use Groups to organize by texture/shader
326
3. ✓ Set visible=False for offscreen objects
327
4. ✓ Preallocate sprites (use pools)
328
5. ✓ Use texture atlases for sprite sheets
329
6. ✓ Update only when needed
330
331
### DON'T:
332
1. ✗ Call sprite.draw() individually (very slow!)
333
2. ✗ Create/delete sprites every frame
334
3. ✗ Forget to batch
335
4. ✗ Modify batch during batch.draw()
336
5. ✗ Create new Batch per frame
337
338
## Common Mistakes
339
340
| Mistake | Problem | Solution |
341
|---------|---------|----------|
342
| Not using Batch | 10-100x slower | Always use batch for multiple objects |
343
| Creating sprites per frame | Memory/CPU waste | Preallocate, use pools |
344
| Forgetting z-order | Wrong layer order | Use Groups with order parameter |
345
| Individual .draw() calls | Very slow | Use batch.draw() instead |
346
| Wrong anchor point | Rotation off-center | Anchor is separate from rotation center |
347
| Color as floats | Wrong values | Use 0-255 integers, not 0.0-1.0 |
348
349
## Critical Reminders
350
351
```python
352
# CORRECT: Batched rendering
353
batch = pyglet.graphics.Batch()
354
sprites = [pyglet.sprite.Sprite(img, x=i*50, batch=batch) for i in range(100)]
355
batch.draw() # One call
356
357
# WRONG: Individual draws (100x slower!)
358
sprites = [pyglet.sprite.Sprite(img, x=i*50) for i in range(100)]
359
for sprite in sprites:
360
sprite.draw() # 100 state changes!
361
362
# Origin is BOTTOM-LEFT
363
sprite.x = 0 # Left edge
364
sprite.y = 0 # Bottom edge (not top!)
365
366
# Colors are 0-255, not floats
367
sprite.color = (255, 0, 0) # Red (CORRECT)
368
# sprite.color = (1.0, 0.0, 0.0) # WRONG!
369
370
# Rotation is around (x, y), not anchor
371
sprite.x, sprite.y = 400, 300 # Center of rotation
372
sprite.rotation = 45 # Rotates around (400, 300)
373
```
374
375
## Typical Game Structure
376
377
```python
378
import pyglet
379
380
window = pyglet.window.Window(800, 600)
381
batch = pyglet.graphics.Batch()
382
383
# Layers
384
bg_group = pyglet.graphics.Group(order=0)
385
game_group = pyglet.graphics.Group(order=1)
386
ui_group = pyglet.graphics.Group(order=2)
387
388
# Load resources
389
player_img = pyglet.resource.image('player.png')
390
enemy_img = pyglet.resource.image('enemy.png')
391
392
# Create sprites
393
player = pyglet.sprite.Sprite(player_img, x=400, y=300,
394
batch=batch, group=game_group)
395
enemies = [
396
pyglet.sprite.Sprite(enemy_img, x=100+i*150, y=400,
397
batch=batch, group=game_group)
398
for i in range(5)
399
]
400
401
# Update
402
def update(dt):
403
# Sprite properties auto-update the batch
404
player.x += player.velocity_x * dt
405
for enemy in enemies:
406
enemy.x -= 50 * dt
407
408
pyglet.clock.schedule_interval(update, 1/60)
409
410
# Render
411
@window.event
412
def on_draw():
413
window.clear()
414
batch.draw() # Draws all in correct order
415
416
pyglet.app.run()
417
```
418
419
---
420
421
**Next Steps:** See [images-textures.md](./images-textures.md) for texture atlases, [graphics-rendering.md](./graphics-rendering.md) for custom shaders.
422