or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

3d-models.mdapp-clock.mdaudio-video.mdgraphics-rendering.mdgui.mdimages-textures.mdindex.mdinput-devices.mdmath.mdopengl.mdresource-management.mdsprites-shapes.mdtext-rendering.mdwindowing.md
IMPROVEMENTS.md

sprites-shapes.mddocs/

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