or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-drawing.mdaudio-sound.mdcore-system.mddrawing-shapes.mdevent-input.mdgame-objects.mdgraphics-display.mdindex.mdinput-devices.mdjoystick-gamepad.mdmath-utils.mdsurface-image.mdtext-font.mdtime-animation.mdtransform-image.md

time-animation.mddocs/

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

```