0
# Application and Event Loop
1
2
Application lifecycle management, event loop control, and timing/scheduling system.
3
4
## When to Use This Module
5
6
- Running the main game/application loop
7
- Scheduling periodic updates (game logic, AI, spawning)
8
- Implementing frame-rate independent movement
9
- Creating timers and delayed actions
10
11
## Quick Reference
12
13
```python
14
# Start event loop
15
pyglet.app.run() # Blocks until exit
16
pyglet.app.exit() # Stop the loop
17
18
# Schedule functions
19
pyglet.clock.schedule(update_func) # Every frame
20
pyglet.clock.schedule_interval(update_func, 1/60) # Fixed rate
21
pyglet.clock.schedule_once(delayed_func, 3.0) # One-time delay
22
pyglet.clock.unschedule(update_func) # Remove scheduled
23
24
# Manual timing
25
dt = pyglet.clock.tick() # Returns delta time
26
fps = pyglet.clock.get_fps() # Current FPS estimate
27
```
28
29
## Application Control
30
31
```python
32
def pyglet.app.run(interval=1/60):
33
"""Start event loop. Blocks until pyglet.app.exit() or all windows closed."""
34
35
def pyglet.app.exit():
36
"""Exit the event loop."""
37
38
# Global objects
39
pyglet.app.event_loop: EventLoop
40
pyglet.app.windows: weakref.WeakSet # All open windows
41
```
42
43
## Scheduling Functions
44
45
### Function Signatures
46
47
All scheduled functions **must** accept `dt` parameter:
48
```python
49
def update(dt): # dt = delta time in seconds
50
player.x += velocity * dt
51
```
52
53
### Schedule Types
54
55
```python
56
# Every frame (variable timing)
57
pyglet.clock.schedule(func)
58
# Use for: Animation, visual effects, input polling
59
60
# Fixed interval (consistent timing)
61
pyglet.clock.schedule_interval(func, interval_seconds)
62
# Use for: Physics, game logic, spawning
63
64
# Approximate interval (drift allowed)
65
pyglet.clock.schedule_interval_soft(func, interval_seconds)
66
# Use for: Autosave, garbage collection, non-critical tasks
67
68
# One-time delay
69
pyglet.clock.schedule_once(func, delay_seconds)
70
# Use for: Delayed actions, cooldowns, timers
71
72
# Remove scheduled function
73
pyglet.clock.unschedule(func)
74
```
75
76
### With Arguments
77
78
```python
79
def spawn_enemy(dt, enemy_type, x, y):
80
enemies.append(Enemy(enemy_type, x, y))
81
82
pyglet.clock.schedule_once(spawn_enemy, 2.0,
83
enemy_type='zombie', x=100, y=200)
84
```
85
86
## Essential Patterns
87
88
### Standard Game Loop
89
90
```python
91
import pyglet
92
93
window = pyglet.window.Window()
94
batch = pyglet.graphics.Batch()
95
96
# Setup
97
player = Player(batch)
98
enemies = []
99
100
# Update function
101
def update(dt):
102
"""Game logic - called 60 times per second"""
103
player.update(dt)
104
for enemy in enemies:
105
enemy.update(dt)
106
check_collisions()
107
108
# Render function
109
@window.event
110
def on_draw():
111
"""Rendering - called when needed"""
112
window.clear()
113
batch.draw()
114
115
# Schedule
116
pyglet.clock.schedule_interval(update, 1/60)
117
118
# Run
119
pyglet.app.run()
120
```
121
122
### Frame-Rate Independent Movement
123
124
```python
125
class Player:
126
def __init__(self):
127
self.x = 0
128
self.velocity_x = 100 # pixels per second (not per frame!)
129
130
def update(self, dt):
131
# CORRECT: velocity * dt
132
self.x += self.velocity_x * dt
133
134
# WRONG: velocity without dt (frame-rate dependent!)
135
# self.x += self.velocity_x
136
```
137
138
### Multiple Update Rates
139
140
```python
141
# Fast physics update
142
def update_physics(dt):
143
physics.step(dt)
144
145
# Slower AI update
146
def update_ai(dt):
147
for enemy in enemies:
148
enemy.think()
149
150
# Very slow autosave
151
def autosave(dt):
152
save_game()
153
154
pyglet.clock.schedule_interval(update_physics, 1/120) # 120 Hz
155
pyglet.clock.schedule_interval(update_ai, 1/10) # 10 Hz
156
pyglet.clock.schedule_interval(autosave, 60.0) # Every minute
157
```
158
159
### Timer Class
160
161
```python
162
class Timer:
163
"""Reusable countdown timer"""
164
def __init__(self, duration, callback):
165
self.duration = duration
166
self.callback = callback
167
self.elapsed = 0
168
self.active = False
169
170
def start(self):
171
self.elapsed = 0
172
self.active = True
173
pyglet.clock.schedule(self.update)
174
175
def update(self, dt):
176
if not self.active:
177
return
178
self.elapsed += dt
179
if self.elapsed >= self.duration:
180
self.stop()
181
self.callback()
182
183
def stop(self):
184
self.active = False
185
pyglet.clock.unschedule(self.update)
186
187
# Usage
188
timer = Timer(3.0, lambda: print("Time's up!"))
189
timer.start()
190
```
191
192
### Pause/Resume System
193
194
```python
195
class GameScheduler:
196
def __init__(self):
197
self.funcs = [] # [(func, interval), ...]
198
self.paused = False
199
200
def schedule(self, func, interval=None):
201
self.funcs.append((func, interval))
202
if not self.paused:
203
self._schedule_func(func, interval)
204
205
def _schedule_func(self, func, interval):
206
if interval is None:
207
pyglet.clock.schedule(func)
208
else:
209
pyglet.clock.schedule_interval(func, interval)
210
211
def pause(self):
212
if not self.paused:
213
for func, _ in self.funcs:
214
pyglet.clock.unschedule(func)
215
self.paused = True
216
217
def resume(self):
218
if self.paused:
219
for func, interval in self.funcs:
220
self._schedule_func(func, interval)
221
self.paused = False
222
```
223
224
## Clock Class
225
226
```python
227
class pyglet.clock.Clock:
228
time: float # Current time
229
230
@staticmethod
231
def sleep(microseconds: float)
232
233
def tick(poll=False) -> float # Update and return dt
234
def get_frequency() -> float # Clock update frequency
235
```
236
237
## FPS Limiting
238
239
```python
240
# Method 1: Use vsync (recommended)
241
window = pyglet.window.Window(vsync=True) # Syncs to monitor
242
243
# Method 2: Custom interval
244
pyglet.app.run(interval=1/60) # Cap at 60 FPS
245
```
246
247
## Critical Reminders
248
249
### 1. Always Accept dt Parameter
250
```python
251
# CORRECT
252
def update(dt):
253
player.x += velocity * dt
254
255
# WRONG - will crash
256
def update():
257
player.x += velocity
258
```
259
260
### 2. Always Multiply Movement by dt
261
```python
262
# CORRECT: Frame-rate independent
263
x += velocity * dt
264
265
# WRONG: Frame-rate dependent
266
x += velocity
267
```
268
269
### 3. Always Unschedule When Done
270
```python
271
# CORRECT
272
def cleanup():
273
pyglet.clock.unschedule(update_func)
274
275
# WRONG: Memory leak
276
# (never unscheduling)
277
```
278
279
### 4. Don't Schedule in on_draw
280
```python
281
# WRONG: Scheduled multiple times
282
@window.event
283
def on_draw():
284
pyglet.clock.schedule_once(foo, 1.0) # BAD!
285
window.clear()
286
batch.draw()
287
288
# CORRECT: Schedule outside
289
pyglet.clock.schedule_once(foo, 1.0)
290
```
291
292
### 5. Use Correct Schedule Type
293
```python
294
# Physics/game logic: Use schedule_interval (consistent timing)
295
pyglet.clock.schedule_interval(update_physics, 1/60)
296
297
# Animation/visual: Use schedule (smooth, every frame)
298
pyglet.clock.schedule(update_animation)
299
300
# Non-critical periodic: Use schedule_interval_soft (CPU efficient)
301
pyglet.clock.schedule_interval_soft(cleanup, 10.0)
302
```
303
304
## Debug/Monitoring
305
306
```python
307
# Get current FPS
308
fps = pyglet.clock.get_fps()
309
print(f"FPS: {fps:.1f}")
310
311
# Get clock frequency
312
freq = pyglet.clock.get_frequency()
313
314
# Access clock directly
315
clock = pyglet.clock.get_default()
316
current_time = clock.time()
317
```
318
319
## Performance Tips
320
321
1. **Use schedule_interval for physics** - More consistent than schedule()
322
2. **Limit scheduled functions** - Each has overhead
323
3. **Use schedule_interval_soft for rare tasks** - Reduces CPU usage
324
4. **Unschedule unused functions** - Prevents unnecessary calls
325
5. **Cache clock object** - If checking time frequently
326
327
## Common Mistakes
328
329
| Mistake | Problem | Solution |
330
|---------|---------|----------|
331
| Forget dt parameter | Crash | Always: `def update(dt):` |
332
| Don't multiply by dt | Frame-rate dependent | Always: `x += velocity * dt` |
333
| Never unschedule | Memory leak | Call `unschedule()` when done |
334
| Schedule in on_draw | Multiple schedules | Schedule outside on_draw |
335
| Wrong schedule type | Inconsistent behavior | schedule_interval for physics |
336
337
## Custom Event Loop (Advanced)
338
339
```python
340
window = pyglet.window.Window()
341
running = True
342
343
while running:
344
window.dispatch_events() # Process events
345
dt = pyglet.clock.tick() # Update clock
346
347
# Call scheduled functions
348
pyglet.clock.get_default().call_scheduled_functions(dt)
349
350
# Custom logic here
351
# ...
352
353
# Render
354
window.dispatch_event('on_draw')
355
window.flip()
356
```
357
358
---
359
360
**Next Steps:** See [windowing.md](./windowing.md) for window events, [sprites-shapes.md](./sprites-shapes.md) for rendering.
361