0
# Time and Animation
1
2
Timing control, frame rate management, and animation support for smooth gameplay. Pygame's time module provides essential tools for controlling game timing, measuring elapsed time, and creating consistent frame rates across different hardware.
3
4
## Capabilities
5
6
### Time Measurement
7
8
Get timing information and measure elapsed time.
9
10
```python { .api }
11
def get_ticks() -> int:
12
"""
13
Get milliseconds since pygame.init() was called.
14
15
Returns:
16
int: Milliseconds elapsed since pygame initialization
17
"""
18
19
def wait(milliseconds: int) -> int:
20
"""
21
Pause program execution for specified time.
22
23
Args:
24
milliseconds (int): Time to wait in milliseconds
25
26
Returns:
27
int: Actual time waited (may be slightly different)
28
"""
29
30
def delay(milliseconds: int) -> int:
31
"""
32
Pause program with CPU time release (more CPU friendly than wait).
33
34
Args:
35
milliseconds (int): Time to delay in milliseconds
36
37
Returns:
38
int: Actual time delayed
39
"""
40
```
41
42
### Frame Rate Control
43
44
Manage game frame rate for consistent gameplay across different hardware.
45
46
```python { .api }
47
class Clock:
48
def __init__(self):
49
"""Create clock object for frame rate management."""
50
51
def tick(self, framerate: int = 0) -> int:
52
"""
53
Control frame rate and get frame time.
54
55
Args:
56
framerate (int): Target frames per second (0 for unlimited)
57
58
Returns:
59
int: Milliseconds since last call
60
"""
61
62
def tick_busy_loop(self, framerate: int = 0) -> int:
63
"""
64
Accurate frame rate control using busy loop (more CPU intensive).
65
66
Args:
67
framerate (int): Target frames per second
68
69
Returns:
70
int: Milliseconds since last call
71
"""
72
73
def get_time(self) -> int:
74
"""
75
Get time since last tick() call.
76
77
Returns:
78
int: Milliseconds since last tick
79
"""
80
81
def get_rawtime(self) -> int:
82
"""
83
Get actual time since last tick() (ignoring delays).
84
85
Returns:
86
int: Raw milliseconds since last tick
87
"""
88
89
def get_fps(self) -> float:
90
"""
91
Get current frame rate.
92
93
Returns:
94
float: Frames per second averaged over last 10 frames
95
"""
96
```
97
98
### Timer Events
99
100
Schedule events to be posted at regular intervals.
101
102
```python { .api }
103
def set_timer(eventid: int, milliseconds: int) -> None:
104
"""
105
Repeatedly post an event at regular intervals.
106
107
Args:
108
eventid (int): Event type to post
109
milliseconds (int): Interval in milliseconds (0 to stop)
110
"""
111
112
def set_timer(eventid: int, milliseconds: int, loops: int) -> None:
113
"""
114
Post event with limited number of repetitions.
115
116
Args:
117
eventid (int): Event type to post
118
milliseconds (int): Interval in milliseconds
119
loops (int): Number of times to post event (0 for infinite)
120
"""
121
```
122
123
## Animation Utilities
124
125
Helper functions and classes for creating smooth animations.
126
127
### Linear Interpolation
128
129
```python { .api }
130
def lerp(start: float, end: float, t: float) -> float:
131
"""
132
Linear interpolation between two values.
133
134
Args:
135
start (float): Starting value
136
end (float): Ending value
137
t (float): Interpolation factor (0.0 to 1.0)
138
139
Returns:
140
float: Interpolated value
141
"""
142
143
def smooth_step(t: float) -> float:
144
"""
145
Smooth interpolation curve (ease in and out).
146
147
Args:
148
t (float): Input value (0.0 to 1.0)
149
150
Returns:
151
float: Smoothed value using 3t² - 2t³
152
"""
153
154
def ease_in_quad(t: float) -> float:
155
"""
156
Quadratic ease-in animation curve.
157
158
Args:
159
t (float): Time parameter (0.0 to 1.0)
160
161
Returns:
162
float: Eased value
163
"""
164
165
def ease_out_quad(t: float) -> float:
166
"""
167
Quadratic ease-out animation curve.
168
169
Args:
170
t (float): Time parameter (0.0 to 1.0)
171
172
Returns:
173
float: Eased value
174
"""
175
176
def ease_in_out_quad(t: float) -> float:
177
"""
178
Quadratic ease-in-out animation curve.
179
180
Args:
181
t (float): Time parameter (0.0 to 1.0)
182
183
Returns:
184
float: Eased value
185
"""
186
```
187
188
### Animation Classes
189
190
Object-oriented animation system for managing multiple animations.
191
192
```python { .api }
193
class Animation:
194
def __init__(self, start_value: float, end_value: float, duration: int, easing_func = None):
195
"""
196
Create animation between two values.
197
198
Args:
199
start_value (float): Starting value
200
end_value (float): Target value
201
duration (int): Animation duration in milliseconds
202
easing_func: Easing function (default: linear)
203
"""
204
205
def update(self, dt: int) -> None:
206
"""
207
Update animation progress.
208
209
Args:
210
dt (int): Delta time in milliseconds
211
"""
212
213
def get_value(self) -> float:
214
"""
215
Get current animated value.
216
217
Returns:
218
float: Current value based on animation progress
219
"""
220
221
def is_finished(self) -> bool:
222
"""
223
Check if animation is complete.
224
225
Returns:
226
bool: True if animation has finished
227
"""
228
229
def reset(self) -> None:
230
"""Reset animation to beginning."""
231
232
def reverse(self) -> None:
233
"""Reverse animation direction."""
234
235
class AnimationManager:
236
def __init__(self):
237
"""Manage multiple animations."""
238
239
def add_animation(self, name: str, animation: Animation) -> None:
240
"""
241
Add animation to manager.
242
243
Args:
244
name (str): Animation identifier
245
animation (Animation): Animation object
246
"""
247
248
def update_all(self, dt: int) -> None:
249
"""
250
Update all managed animations.
251
252
Args:
253
dt (int): Delta time in milliseconds
254
"""
255
256
def get_value(self, name: str) -> float:
257
"""
258
Get value of named animation.
259
260
Args:
261
name (str): Animation name
262
263
Returns:
264
float: Current animation value
265
"""
266
267
def remove_finished(self) -> None:
268
"""Remove all completed animations."""
269
```
270
271
## Usage Examples
272
273
### Basic Frame Rate Control
274
275
```python
276
import pygame
277
278
pygame.init()
279
screen = pygame.display.set_mode((800, 600))
280
281
# Create clock for frame rate control
282
clock = pygame.time.Clock()
283
FPS = 60
284
285
# Game state
286
player_x = 400
287
player_speed = 200 # pixels per second
288
289
running = True
290
while running:
291
# Get delta time in seconds
292
dt = clock.tick(FPS) / 1000.0
293
294
for event in pygame.event.get():
295
if event.type == pygame.QUIT:
296
running = False
297
298
# Frame-rate independent movement
299
keys = pygame.key.get_pressed()
300
if keys[pygame.K_LEFT]:
301
player_x -= player_speed * dt
302
if keys[pygame.K_RIGHT]:
303
player_x += player_speed * dt
304
305
# Keep player on screen
306
player_x = max(0, min(800, player_x))
307
308
# Draw
309
screen.fill((0, 0, 0))
310
pygame.draw.circle(screen, (255, 255, 255), (int(player_x), 300), 20)
311
312
# Display FPS
313
fps_text = f"FPS: {clock.get_fps():.1f}"
314
print(fps_text) # In real game, render to screen
315
316
pygame.display.flip()
317
318
pygame.quit()
319
```
320
321
### Timer Events
322
323
```python
324
import pygame
325
import random
326
327
pygame.init()
328
screen = pygame.display.set_mode((800, 600))
329
330
# Create custom events
331
SPAWN_ENEMY = pygame.event.custom_type()
332
FLASH_SCREEN = pygame.event.custom_type()
333
GAME_TIMER = pygame.event.custom_type()
334
335
# Set up timers
336
pygame.time.set_timer(SPAWN_ENEMY, 2000) # Spawn enemy every 2 seconds
337
pygame.time.set_timer(FLASH_SCREEN, 500) # Flash every 0.5 seconds
338
pygame.time.set_timer(GAME_TIMER, 1000) # Game timer every second
339
340
# Game state
341
enemies = []
342
flash_color = (0, 0, 0)
343
game_time = 0
344
clock = pygame.time.Clock()
345
346
running = True
347
while running:
348
for event in pygame.event.get():
349
if event.type == pygame.QUIT:
350
running = False
351
352
elif event.type == SPAWN_ENEMY:
353
# Spawn enemy at random position
354
enemy_pos = (random.randint(0, 800), random.randint(0, 600))
355
enemies.append(enemy_pos)
356
print(f"Enemy spawned at {enemy_pos}")
357
358
elif event.type == FLASH_SCREEN:
359
# Toggle flash color
360
flash_color = (50, 0, 0) if flash_color == (0, 0, 0) else (0, 0, 0)
361
362
elif event.type == GAME_TIMER:
363
# Update game timer
364
game_time += 1
365
print(f"Game time: {game_time} seconds")
366
367
# Stop spawning after 10 seconds
368
if game_time >= 10:
369
pygame.time.set_timer(SPAWN_ENEMY, 0) # Stop timer
370
371
# Draw
372
screen.fill(flash_color)
373
374
# Draw enemies
375
for enemy_pos in enemies:
376
pygame.draw.circle(screen, (255, 0, 0), enemy_pos, 15)
377
378
# Draw timer
379
font = pygame.font.Font(None, 36)
380
timer_text = font.render(f"Time: {game_time}", True, (255, 255, 255))
381
screen.blit(timer_text, (50, 50))
382
383
pygame.display.flip()
384
clock.tick(60)
385
386
pygame.quit()
387
```
388
389
### Smooth Animation System
390
391
```python
392
import pygame
393
import math
394
395
# Easing functions
396
def ease_in_quad(t):
397
return t * t
398
399
def ease_out_quad(t):
400
return 1 - (1 - t) * (1 - t)
401
402
def ease_in_out_quad(t):
403
if t < 0.5:
404
return 2 * t * t
405
else:
406
return 1 - 2 * (1 - t) * (1 - t)
407
408
def ease_in_bounce(t):
409
return 1 - ease_out_bounce(1 - t)
410
411
def ease_out_bounce(t):
412
if t < 1 / 2.75:
413
return 7.5625 * t * t
414
elif t < 2 / 2.75:
415
t -= 1.5 / 2.75
416
return 7.5625 * t * t + 0.75
417
elif t < 2.5 / 2.75:
418
t -= 2.25 / 2.75
419
return 7.5625 * t * t + 0.9375
420
else:
421
t -= 2.625 / 2.75
422
return 7.5625 * t * t + 0.984375
423
424
class Animation:
425
def __init__(self, start_value, end_value, duration, easing_func=None):
426
self.start_value = start_value
427
self.end_value = end_value
428
self.duration = duration
429
self.easing_func = easing_func or (lambda t: t) # Linear by default
430
self.elapsed = 0
431
self.finished = False
432
433
def update(self, dt):
434
if not self.finished:
435
self.elapsed += dt
436
if self.elapsed >= self.duration:
437
self.elapsed = self.duration
438
self.finished = True
439
440
def get_value(self):
441
if self.duration == 0:
442
return self.end_value
443
444
t = self.elapsed / self.duration
445
eased_t = self.easing_func(t)
446
447
return self.start_value + (self.end_value - self.start_value) * eased_t
448
449
def is_finished(self):
450
return self.finished
451
452
def reset(self):
453
self.elapsed = 0
454
self.finished = False
455
456
pygame.init()
457
screen = pygame.display.set_mode((800, 600))
458
clock = pygame.time.Clock()
459
460
# Create animations
461
animations = {
462
'x': Animation(100, 700, 2000, ease_out_bounce), # X movement with bounce
463
'y': Animation(100, 500, 1500, ease_in_out_quad), # Y movement smooth
464
'size': Animation(10, 50, 1000, ease_in_quad), # Size growth
465
'rotation': Animation(0, 360, 3000), # Rotation
466
}
467
468
# Animation state
469
running = True
470
animation_started = False
471
start_time = 0
472
473
while running:
474
dt = clock.tick(60)
475
476
for event in pygame.event.get():
477
if event.type == pygame.QUIT:
478
running = False
479
elif event.type == pygame.KEYDOWN:
480
if event.key == pygame.K_SPACE:
481
# Reset and start animations
482
for anim in animations.values():
483
anim.reset()
484
animation_started = True
485
486
# Update animations
487
if animation_started:
488
for anim in animations.values():
489
anim.update(dt)
490
491
# Get current values
492
x = animations['x'].get_value()
493
y = animations['y'].get_value()
494
size = animations['size'].get_value()
495
rotation = animations['rotation'].get_value()
496
497
# Draw
498
screen.fill((0, 0, 0))
499
500
# Draw animated circle
501
pygame.draw.circle(screen, (255, 255, 255), (int(x), int(y)), int(size))
502
503
# Draw rotating line from circle
504
line_length = size * 2
505
end_x = x + line_length * math.cos(math.radians(rotation))
506
end_y = y + line_length * math.sin(math.radians(rotation))
507
pygame.draw.line(screen, (255, 0, 0), (x, y), (end_x, end_y), 3)
508
509
# Draw instructions
510
font = pygame.font.Font(None, 36)
511
text = font.render("Press SPACE to start animation", True, (255, 255, 255))
512
screen.blit(text, (50, 50))
513
514
# Show animation progress
515
all_finished = all(anim.is_finished() for anim in animations.values())
516
if animation_started:
517
progress_text = "Animation finished!" if all_finished else "Animating..."
518
progress = font.render(progress_text, True, (255, 255, 0))
519
screen.blit(progress, (50, 100))
520
521
pygame.display.flip()
522
523
pygame.quit()
524
```
525
526
### Time-based Particle System
527
528
```python
529
import pygame
530
import random
531
import math
532
533
class Particle:
534
def __init__(self, x, y):
535
self.x = x
536
self.y = y
537
self.vel_x = random.uniform(-100, 100) # pixels per second
538
self.vel_y = random.uniform(-200, -50) # upward bias
539
self.life = random.uniform(2.0, 4.0) # seconds
540
self.max_life = self.life
541
self.size = random.uniform(2, 6)
542
self.color = [255, random.randint(100, 255), 0] # Orange-ish
543
544
def update(self, dt):
545
# Physics (time-based)
546
gravity = 200 # pixels per second squared
547
self.vel_y += gravity * dt
548
549
# Update position
550
self.x += self.vel_x * dt
551
self.y += self.vel_y * dt
552
553
# Update life
554
self.life -= dt
555
556
# Fade out as life decreases
557
life_ratio = self.life / self.max_life
558
self.color[0] = int(255 * life_ratio)
559
self.color[1] = int(self.color[1] * life_ratio)
560
561
def is_dead(self):
562
return self.life <= 0
563
564
def draw(self, screen):
565
if self.life > 0:
566
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), int(self.size * (self.life / self.max_life)))
567
568
class ParticleSystem:
569
def __init__(self):
570
self.particles = []
571
self.spawn_timer = 0
572
self.spawn_rate = 0.1 # seconds between spawns
573
574
def update(self, dt, mouse_pos):
575
# Spawn particles
576
self.spawn_timer -= dt
577
if self.spawn_timer <= 0:
578
# Spawn particle at mouse position
579
self.particles.append(Particle(mouse_pos[0], mouse_pos[1]))
580
self.spawn_timer = self.spawn_rate
581
582
# Update particles
583
for particle in self.particles[:]:
584
particle.update(dt)
585
if particle.is_dead() or particle.y > 700: # Remove if dead or off-screen
586
self.particles.remove(particle)
587
588
def draw(self, screen):
589
for particle in self.particles:
590
particle.draw(screen)
591
592
def get_count(self):
593
return len(self.particles)
594
595
pygame.init()
596
screen = pygame.display.set_mode((800, 600))
597
clock = pygame.time.Clock()
598
599
particle_system = ParticleSystem()
600
font = pygame.font.Font(None, 36)
601
602
running = True
603
while running:
604
dt = clock.tick(60) / 1000.0 # Convert to seconds
605
606
for event in pygame.event.get():
607
if event.type == pygame.QUIT:
608
running = False
609
610
mouse_pos = pygame.mouse.get_pos()
611
612
# Update particle system
613
particle_system.update(dt, mouse_pos)
614
615
# Draw
616
screen.fill((0, 0, 30)) # Dark blue background
617
618
# Draw particle system
619
particle_system.draw(screen)
620
621
# Draw UI
622
particle_count = font.render(f"Particles: {particle_system.get_count()}", True, (255, 255, 255))
623
screen.blit(particle_count, (10, 10))
624
625
fps_text = font.render(f"FPS: {clock.get_fps():.1f}", True, (255, 255, 255))
626
screen.blit(fps_text, (10, 50))
627
628
instructions = font.render("Move mouse to emit particles", True, (255, 255, 255))
629
screen.blit(instructions, (10, 90))
630
631
pygame.display.flip()
632
633
pygame.quit()
634
```
635
636
### Delta Time Comparison
637
638
```python
639
import pygame
640
import math
641
642
pygame.init()
643
screen = pygame.display.set_mode((800, 600))
644
font = pygame.font.Font(None, 36)
645
646
# Two circles - one frame-dependent, one time-dependent
647
circle1_angle = 0 # Frame-dependent rotation
648
circle2_angle = 0 # Time-dependent rotation
649
650
rotation_speed_degrees_per_second = 90 # 90 degrees per second
651
652
clock = pygame.time.Clock()
653
target_fps = 60
654
actual_fps_limit = 60 # Can be changed to test different frame rates
655
656
running = True
657
while running:
658
dt = clock.tick(actual_fps_limit) / 1000.0 # Delta time in seconds
659
660
for event in pygame.event.get():
661
if event.type == pygame.QUIT:
662
running = False
663
elif event.type == pygame.KEYDOWN:
664
if event.key == pygame.K_1:
665
actual_fps_limit = 30
666
elif event.key == pygame.K_2:
667
actual_fps_limit = 60
668
elif event.key == pygame.K_3:
669
actual_fps_limit = 120
670
671
# Frame-dependent rotation (BAD - speed depends on frame rate)
672
circle1_angle += 1.5 # Fixed increment per frame
673
674
# Time-dependent rotation (GOOD - consistent speed regardless of frame rate)
675
circle2_angle += rotation_speed_degrees_per_second * dt
676
677
# Calculate positions
678
center_x = 400
679
radius = 150
680
681
circle1_x = center_x - 100 + radius * math.cos(math.radians(circle1_angle))
682
circle1_y = 300 + radius * math.sin(math.radians(circle1_angle))
683
684
circle2_x = center_x + 100 + radius * math.cos(math.radians(circle2_angle))
685
circle2_y = 300 + radius * math.sin(math.radians(circle2_angle))
686
687
# Draw
688
screen.fill((0, 0, 0))
689
690
# Draw orbits
691
pygame.draw.circle(screen, (50, 50, 50), (center_x - 100, 300), radius, 2)
692
pygame.draw.circle(screen, (50, 50, 50), (center_x + 100, 300), radius, 2)
693
694
# Draw centers
695
pygame.draw.circle(screen, (100, 100, 100), (center_x - 100, 300), 5)
696
pygame.draw.circle(screen, (100, 100, 100), (center_x + 100, 300), 5)
697
698
# Draw rotating circles
699
pygame.draw.circle(screen, (255, 100, 100), (int(circle1_x), int(circle1_y)), 15) # Red - frame-dependent
700
pygame.draw.circle(screen, (100, 255, 100), (int(circle2_x), int(circle2_y)), 15) # Green - time-dependent
701
702
# Labels
703
label1 = font.render("Frame-dependent (RED)", True, (255, 100, 100))
704
screen.blit(label1, (50, 50))
705
706
label2 = font.render("Time-dependent (GREEN)", True, (100, 255, 100))
707
screen.blit(label2, (450, 50))
708
709
# FPS info
710
fps_text = font.render(f"FPS: {clock.get_fps():.1f}", True, (255, 255, 255))
711
screen.blit(fps_text, (50, 500))
712
713
instructions = font.render("Press 1 (30fps), 2 (60fps), 3 (120fps)", True, (255, 255, 255))
714
screen.blit(instructions, (50, 530))
715
716
explanation = font.render("Green circle maintains consistent speed at any FPS", True, (255, 255, 255))
717
screen.blit(explanation, (50, 560))
718
719
pygame.display.flip()
720
721
pygame.quit()
722
```