Menu
CoddyTech

A Smooth Camera

Part of the Sound and Game Feel section of Coddy's Pygame journey. Lesson 17 of 23.

The camera in this road game is glued to the player: whatever the player does, they stay at the centre of the screen and the world slides under them. It works, but every start and stop jerks the whole view.

Let the camera follow with the same easing as the circle. Its target is the position that would centre the player:

camera_x += (player.centerx - 240 - camera_x) * 0.1
camera_x = max(0, min(camera_x, WORLD_WIDTH - 480))

The player moves as before; only the view eases. When the player starts running, the camera lags a little, so the player runs ahead of the centre. When they stop, the camera glides to catch up and settles with the player in the middle again. The second line still keeps the camera inside the world.

The world is drawn at -int(camera_x) as before. camera_x is a decimal number now; the int() keeps the drawing on whole pixels, while the variable keeps its exact value for the next frame.

A menu panel that slides in, a health bar that drains, a score that counts up: anything that moves to a new value can ease the same way.

challenge icon

Challenge

Medium

Make the camera follow smoothly: replace camera_x = player.centerx - 240 with a line that moves camera_x a tenth of the way toward player.centerx - 240 each frame. Keep the line that clamps it.

Try it yourself

import pygame

pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Long Road")
clock = pygame.time.Clock()

WORLD_WIDTH = 1440
player = pygame.Rect(220, 240, 40, 40)
camera_x = 0

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        player.x -= 6
    if keys[pygame.K_RIGHT]:
        player.x += 6
    player.left = max(player.left, 0)
    player.right = min(player.right, WORLD_WIDTH)

    camera_x = player.centerx - 240
    camera_x = max(0, min(camera_x, WORLD_WIDTH - 480))

    screen.fill((30, 30, 46))
    pygame.draw.rect(screen, (80, 80, 100), (0, 280, 480, 80))
    for post_x in range(0, WORLD_WIDTH, 120):
        pygame.draw.rect(screen, (80, 160, 255), (post_x - int(camera_x), 220, 20, 60))
    pygame.draw.rect(screen, (90, 200, 120), player.move(-int(camera_x), 0))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Sound and Game Feel

Practice on your own: Pygame playground