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

app-clock.mddocs/

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