Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

EktuPy Examples

This page contains example programs demonstrating EktuPy features. Each example includes a code walkthrough explaining the key concepts.


Hello World

The simplest EktuPy program - display a sprite with a speech bubble.

What you’ll learn:

  • Creating a stage and sprite
  • Using the @on_start decorator
  • Basic sprite positioning and speech bubbles
View Code
# Hello Snake - Simple greeting example

from ektupy import Stage, Sprite, on_start

stage = Stage()
stage.set_background("#2d5a27")

snake = Sprite("snake")
stage.add_sprite(snake)

@on_start
def setup():
    snake.go_to(0, 0)
    snake.say("Hello, I'm Py the Snake!", 5)

Code Breakdown

Imports and Setup

from ektupy import Stage, Sprite, on_start

Import the core classes and the @on_start decorator.

Stage Creation

stage = Stage()
stage.set_background("#2d5a27")  # Dark green color

Create a 960x720 pixel stage with a custom background color using CSS hex notation.

Sprite Creation

snake = Sprite("snake")
stage.add_sprite(snake)

Create a sprite using the asset name “snake” and add it to the stage. The sprite won’t be visible until added.

Event Handler

@on_start
def setup():
    snake.go_to(0, 0)
    snake.say("Hello, I'm Py the Snake!", 5)

The @on_start decorator runs this function once when the program starts. say() displays a speech bubble for 5 seconds.


Arrow Key Movement

Move a sprite around the stage using arrow keys.

What you’ll learn:

  • Using @on_forever for continuous checking
  • Reading keyboard input with key_pressed()
  • Sprite direction and movement
  • Edge bouncing
View Code
# Arrow Key Movement Example
# Use arrow keys to move the snake

from ektupy import Stage, Sprite, on_start, on_forever, key_pressed

stage = Stage()
snake = Sprite("snake")
stage.add_sprite(snake)

speed = 5

@on_start
def setup():
    snake.go_to(0, 0)
    snake.say("Use arrow keys to move!", 3)

@on_forever
def game_loop():
    if key_pressed("right arrow"):
        snake.direction = 90
        snake.move(speed)
    if key_pressed("left arrow"):
        snake.direction = -90
        snake.move(speed)
    if key_pressed("up arrow"):
        snake.direction = 0
        snake.move(speed)
    if key_pressed("down arrow"):
        snake.direction = 180
        snake.move(speed)

    snake.bounce_if_on_edge()

Code Breakdown

Game Variables

speed = 5

Store movement speed in a variable for easy adjustment.

The Forever Loop

@on_forever
def game_loop():

This function runs every frame (~60 times per second), perfect for checking continuous input.

Key Checking

if key_pressed("right arrow"):
    snake.direction = 90    # Point right (90 degrees)
    snake.move(speed)       # Move forward

key_pressed() returns True while a key is held. Direction uses Scratch-style angles: 0=up, 90=right, 180=down, -90=left.

Boundary Handling

snake.bounce_if_on_edge()

Prevents the sprite from leaving the stage by reversing direction at edges.


Bouncing Ball

A sprite that bounces around the stage continuously.

What you’ll learn:

  • Random initial direction
  • Continuous movement with bouncing
  • Using Python’s random module
View Code
# Bouncing Ball Example
# Watch the snake bounce around the stage

from ektupy import Stage, Sprite, on_start, on_forever
import random

stage = Stage()
stage.set_background("#1a1a2e")

snake = Sprite("snake")
stage.add_sprite(snake)

speed = 8

@on_start
def setup():
    snake.go_to(0, 0)
    snake.direction = random.randint(30, 60)
    snake.say("Wheee!", 2)

@on_forever
def game_loop():
    snake.move(speed)
    snake.bounce_if_on_edge()

Code Breakdown

Random Direction

import random
snake.direction = random.randint(30, 60)

Python’s standard library works in EktuPy! Set a random diagonal direction at startup.

Continuous Motion

@on_forever
def game_loop():
    snake.move(speed)
    snake.bounce_if_on_edge()

The sprite moves forward continuously and bounces off walls automatically.


Drawing with Pen

Use the pen to draw on the stage as the sprite moves.

What you’ll learn:

  • Pen down/up for drawing
  • Pen color and size
  • Following mouse position
  • Clearing drawings
View Code
# Drawing with Pen Example
# Click and drag to draw, press 'c' to clear

from ektupy import Stage, Sprite, on_start, on_forever
from ektupy import mouse_x, mouse_y, mouse_down, key_pressed

stage = Stage()
pen = Sprite("snake")
pen.size = 30
stage.add_sprite(pen)

@on_start
def setup():
    pen.pen_up()
    pen.set_pen_color("#00ff00")
    pen.set_pen_size(3)
    pen.say("Click to draw, 'c' to clear", 3)

@on_forever
def draw():
    pen.go_to(mouse_x(), mouse_y())

    if mouse_down():
        pen.pen_down()
    else:
        pen.pen_up()

    # Press 'c' to clear
    if key_pressed("c"):
        pen.clear()

    # Color keys
    if key_pressed("1"):
        pen.set_pen_color("#ff0000")  # Red
    if key_pressed("2"):
        pen.set_pen_color("#00ff00")  # Green
    if key_pressed("3"):
        pen.set_pen_color("#0000ff")  # Blue

Code Breakdown

Pen Setup

pen.pen_up()                    # Start with pen up
pen.set_pen_color("#00ff00")    # Green color
pen.set_pen_size(3)             # 3 pixel line width

Configure pen before drawing. Use CSS colors (hex, rgb, or named).

Mouse Tracking

pen.go_to(mouse_x(), mouse_y())

Move sprite to current mouse position every frame.

Drawing Logic

if mouse_down():
    pen.pen_down()
else:
    pen.pen_up()

Only draw when mouse button is pressed.

Color Switching

if key_pressed("1"):
    pen.set_pen_color("#ff0000")

Change colors with number keys for variety.


Two Sprites

Each sprite can have its own scripts, just like in Scratch!

What you’ll learn:

  • Per-sprite event decorators (@sprite.on_start, @sprite.on_key_press)
  • Multiple sprites with independent controls
  • Collision detection
  • Broadcasting messages between sprites
View Code
# Two Sprites Example - Cat and Dog
# This shows how each sprite can have its own scripts, just like Scratch!

# Create the stage
stage = Stage()
stage.set_background("#87CEEB")  # Sky blue background

# Create our two sprites
cat = Sprite("cat")
dog = Sprite("dog")

# Add sprites to the stage
stage.add_sprite(cat)
stage.add_sprite(dog)

# ============================================
# CAT'S SCRIPTS - Use arrow keys to move
# ============================================

@cat.on_start
def cat_setup():
    cat.go_to(-100, 0)
    cat.say("I'm the cat! Use arrow keys to move me.")
    wait(3)
    cat.say("")

@cat.on_key_press("up arrow")
def cat_up():
    cat.change_y(10)

@cat.on_key_press("down arrow")
def cat_down():
    cat.change_y(-10)

@cat.on_key_press("left arrow")
def cat_left():
    cat.change_x(-10)

@cat.on_key_press("right arrow")
def cat_right():
    cat.change_x(10)

@cat.on_message("woof")
def cat_hears_woof():
    cat.say("Meow back!")
    wait(1)
    cat.say("")

# ============================================
# DOG'S SCRIPTS - Use WASD keys to move
# ============================================

@dog.on_start
def dog_setup():
    dog.go_to(100, 0)
    dog.say("I'm the dog! Use WASD to move me.")
    wait(3)
    dog.say("")

@dog.on_key_press("w")
def dog_up():
    dog.change_y(10)

@dog.on_key_press("s")
def dog_down():
    dog.change_y(-10)

@dog.on_key_press("a")
def dog_left():
    dog.change_x(-10)

@dog.on_key_press("d")
def dog_right():
    dog.change_x(10)

@dog.on_key_press("space")
def dog_bark():
    dog.say("Woof!")
    broadcast("woof")
    wait(1)
    dog.say("")

# ============================================
# COLLISION DETECTION - Check if they touch
# ============================================

@cat.on_forever
def check_collision():
    if cat.touching(dog):
        cat.say("Hey!")
        dog.say("Hi friend!")

Code Breakdown

Per-Sprite Decorators

@cat.on_start       # Only for cat
@dog.on_key_press("w")  # Only for dog

Each sprite has its own event handlers, keeping code organized.

Different Control Schemes

  • Cat uses arrow keys
  • Dog uses WASD keys Both can move simultaneously!

Message Broadcasting

broadcast("woof")           # Dog sends message

@cat.on_message("woof")     # Cat receives it
def cat_hears_woof():
    cat.say("Meow back!")

Sprites can communicate through broadcast messages.

Collision Detection

if cat.touching(dog):
    cat.say("Hey!")

Check if two sprites are overlapping using bounding box collision.


Many Sprites

Click on sprites to select them, then move with arrow keys.

What you’ll learn:

  • Managing multiple sprites
  • Click handlers with on_clicked()
  • Global state for selection
  • Dynamic behavior based on selection
View Code
# Many Sprites Example
# Click on a sprite to select it, then use arrow keys to move it!

from ektupy import Stage, Sprite, on_start, on_key_press
import random

stage = Stage()
stage.set_background("#1a1a2e")

# Create all the sprites
lion = Sprite("lion")
fish = Sprite("fish")
cat = Sprite("cat")
shark = Sprite("shark")
snake = Sprite("snake")

# Add them to the stage
stage.add_sprite(lion)
stage.add_sprite(fish)
stage.add_sprite(cat)
stage.add_sprite(shark)
stage.add_sprite(snake)

# Track which sprite is selected
selected_sprite = None

def select_sprite(sprite, name):
    """Select a sprite and make it say its name."""
    global selected_sprite
    selected_sprite = sprite

    # Clear speech from all sprites
    lion.say("")
    fish.say("")
    cat.say("")
    shark.say("")
    snake.say("")

    # Selected sprite says its name
    sprite.say(f"I'm {name}!")

@on_start
def setup():
    # Place sprites at random positions
    for sprite in [lion, fish, cat, shark, snake]:
        x = random.randint(-350, 350)
        y = random.randint(-250, 250)
        sprite.go_to(x, y)
        sprite.size = 80

    print("Click on a sprite to select it!")
    print("Use arrow keys to move the selected sprite.")

# Click handlers for each sprite
def click_lion():
    select_sprite(lion, "Lion")

def click_fish():
    select_sprite(fish, "Fish")

def click_cat():
    select_sprite(cat, "Cat")

def click_shark():
    select_sprite(shark, "Shark")

def click_snake():
    select_sprite(snake, "Snake")

# Register click handlers
lion.on_clicked(click_lion)
fish.on_clicked(click_fish)
cat.on_clicked(click_cat)
shark.on_clicked(click_shark)
snake.on_clicked(click_snake)

# Arrow key movement
@on_key_press("up arrow")
def move_up():
    if selected_sprite:
        selected_sprite.change_y(20)

@on_key_press("down arrow")
def move_down():
    if selected_sprite:
        selected_sprite.change_y(-20)

@on_key_press("left arrow")
def move_left():
    if selected_sprite:
        selected_sprite.change_x(-20)

@on_key_press("right arrow")
def move_right():
    if selected_sprite:
        selected_sprite.change_x(20)

Code Breakdown

Click Handler Registration

lion.on_clicked(click_lion)

Register a callback function to run when this sprite is clicked.

Selection State

selected_sprite = None

def select_sprite(sprite, name):
    global selected_sprite
    selected_sprite = sprite

Use a global variable to track the currently selected sprite.

Conditional Movement

if selected_sprite:
    selected_sprite.change_y(20)

Only move if a sprite is selected (not None).


Costume Animation

Watch the snake change costumes as it moves!

What you’ll learn:

  • Costume system
  • Frame-based animation timing
  • Accessing costume properties
View Code
# Costume Animation Example
# Watch the snake change colors as it moves!

from ektupy import Stage, Sprite, on_start, on_forever

stage = Stage()
stage.set_background("#2d3436")

# Create a snake sprite - automatically loads all costumes
snake = Sprite("snake")
stage.add_sprite(snake)

frame_count = 0

@on_start
def setup():
    snake.go_to(0, 0)
    snake.set_size(150)
    # Show available costumes
    print("Available costumes:", snake.costumes)
    snake.say("I can change colors!", 2)

@on_forever
def animate():
    global frame_count
    frame_count += 1

    # Move in a circle
    snake.turn_right(3)
    snake.move(5)

    # Change costume every 20 frames
    if frame_count % 20 == 0:
        snake.next_costume()
        print(f"Now wearing: {snake.costume_name}")

Code Breakdown

Costume Access

print("Available costumes:", snake.costumes)

Sprites can have multiple costumes. Access the list via the costumes property.

Frame Counting

frame_count += 1
if frame_count % 20 == 0:
    snake.next_costume()

Use modulo (%) to trigger actions periodically. At 60fps, % 20 triggers ~3 times per second.

Costume Properties

snake.costume_name   # Current costume name
snake.costume_number # Current costume index

Mouse Tracker

A sprite that follows and reacts to the mouse.

What you’ll learn:

  • Distance calculations
  • Mouse sensing
  • Point-towards behavior
  • Conditional responses based on distance
View Code
# Mouse Tracker - Sensing Mouse Position
# Demonstrates distance_to_mouse and touching_mouse

from ektupy import Stage, Sprite, on_start, on_forever
from ektupy import mouse_x, mouse_y

stage = Stage()
stage.set_background("#2d5a27")

snake = Sprite("snake")
stage.add_sprite(snake)

@snake.on_start
def setup():
    snake.go_to(0, 0)
    snake.size = 80

@snake.on_forever
def track_mouse():
    # Calculate distance to mouse
    dist = snake.distance_to_mouse()

    # Check if touching mouse pointer
    if snake.touching_mouse():
        snake.say("You caught me!")
    elif dist < 80:
        snake.say("Too close!")
    elif dist < 150:
        snake.say("Getting warmer...")
    else:
        snake.say(f"Distance: {int(dist)}")

    # Slowly move toward mouse
    snake.point_towards_mouse()
    if dist > 10:
        snake.move(2)

Code Breakdown

Distance Sensing

dist = snake.distance_to_mouse()

Get the pixel distance between sprite center and mouse position.

Conditional Responses

if snake.touching_mouse():
    snake.say("You caught me!")
elif dist < 80:
    snake.say("Too close!")

Create zones of different behavior based on distance.

Following Behavior

snake.point_towards_mouse()
if dist > 10:
    snake.move(2)

Point toward mouse and move only if not already there (prevents jittering).


Ask Name

Get user input through an on-screen text box.

What you’ll learn:

  • Using ask() for user input
  • Working with the returned answer
  • Sequential prompts
View Code
# Ask Your Name - User Input Example
# Demonstrates the ask() and answer() functions

from ektupy import Stage, Sprite, on_start, on_key_press
from ektupy import ask, answer, wait

stage = Stage()
stage.set_background("#4a90d9")

snake = Sprite("snake")
stage.add_sprite(snake)

@snake.on_start
def greet():
    snake.go_to(0, 100)
    snake.size = 100

    # Ask for the user's name
    name = ask("What's your name?")

    # Use the answer
    snake.say(f"Hello, {name}! Nice to meet you!")
    wait(2)

    # Ask another question
    color = ask("What's your favorite color?")
    snake.say(f"{color} is a great color!")
    wait(2)

    snake.say("Press SPACE to ask again!")

@snake.on_key_press("space")
def ask_again():
    response = ask("Tell me something interesting:")
    snake.say(f'You said: "{response}"')

Code Breakdown

Getting Input

name = ask("What's your name?")

Shows an input box at the bottom of the stage. Program pauses until user submits.

Using the Answer

snake.say(f"Hello, {name}!")

The returned value is a string you can use immediately.

Multiple Questions

name = ask("What's your name?")
color = ask("What's your favorite color?")

Each ask() waits for its own response before continuing.


Digital Clock

Display the current time and date using sensing functions.

What you’ll learn:

  • Date/time functions
  • Timer functions
  • String formatting
  • Creating a live display
View Code
# Digital Clock - Sensing Time Example
# Demonstrates timer and date/time functions

from ektupy import Stage, Sprite, on_start, on_forever
from ektupy import timer, reset_timer
from ektupy import current_hour, current_minute, current_second, current_day_of_week

stage = Stage()
stage.set_background("#1a1a2e")

clock = Sprite("snake")
stage.add_sprite(clock)

days = ["", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

@clock.on_start
def setup():
    clock.go_to(0, 100)
    clock.size = 100
    reset_timer()

@clock.on_forever
def update_clock():
    h = current_hour()
    m = current_minute()
    s = current_second()
    day = days[current_day_of_week()]

    # Format time with leading zeros
    time_str = f"{h:02d}:{m:02d}:{s:02d}"
    elapsed = int(timer())

    clock.say(f"{day}\n{time_str}\nRunning: {elapsed}s")

Code Breakdown

Time Functions

h = current_hour()      # 0-23
m = current_minute()    # 0-59
s = current_second()    # 0-59

Get current time from the user’s system.

Day of Week

days = ["", "Sunday", "Monday", ...]
day = days[current_day_of_week()]  # 1=Sunday

Convert numeric day (1-7) to a name using a lookup list.

String Formatting

f"{h:02d}:{m:02d}:{s:02d}"  # "09:05:03"

Python f-strings with format specifiers for leading zeros.

Timer

reset_timer()           # Start at 0
elapsed = int(timer())  # Seconds since reset

Track time elapsed since program started.


Background Images

Use backdrop images and cycle through them.

What you’ll learn:

  • Setting background images
  • Discovering available assets
  • Cycling through options
View Code
# Background Image Example
# Demonstrates using backdrop images with sprites

from ektupy import Stage, Sprite, on_start, on_forever, key_pressed
from ektupy import get_available_backdrops

stage = Stage()
stage.set_background_image("mountain-sunrise")

snake = Sprite("snake")
stage.add_sprite(snake)

# Track current backdrop index
backdrops = ["mountain-sunrise", "jungle", "paddy-field", "sky-and-clouds"]
current_backdrop = 0

@on_start
def setup():
    snake.go_to(0, -200)
    snake.say("Press SPACE to change backdrop!", 3)
    print("Available backdrops:", get_available_backdrops())

@on_forever
def game_loop():
    global current_backdrop

    # Move with arrow keys
    if key_pressed("right arrow"):
        snake.direction = 90
        snake.move(5)
    if key_pressed("left arrow"):
        snake.direction = -90
        snake.move(5)
    if key_pressed("up arrow"):
        snake.direction = 0
        snake.move(5)
    if key_pressed("down arrow"):
        snake.direction = 180
        snake.move(5)

    # Change backdrop with space
    if key_pressed("space"):
        current_backdrop = (current_backdrop + 1) % len(backdrops)
        stage.set_background_image(backdrops[current_backdrop])
        snake.say(backdrops[current_backdrop], 1)

Code Breakdown

Setting Background Image

stage.set_background_image("mountain-sunrise")

Use asset names for backgrounds, just like sprites.

Asset Discovery

print("Available backdrops:", get_available_backdrops())

Runtime function to list available backdrop names.

Cycling with Modulo

current_backdrop = (current_backdrop + 1) % len(backdrops)

Wrap around to 0 when reaching the end of the list.


Pong Game

A complete two-player Pong game with sound effects.

What you’ll learn:

  • Game state management
  • Two-player controls
  • Ball physics with bouncing
  • Score tracking
  • Sound effects
View Code
# Pong Game Example
# Classic two-player Pong game
# Player 1 (Left): W/S keys
# Player 2 (Right): Up/Down arrow keys
# Press SPACE to start/restart the ball

# Game variables (scaled for 960x720 stage)
ball_speed_x = 0
ball_speed_y = 0
paddle_speed = 10
score1 = 0
score2 = 0
ball_moving = False

# Create the stage with dark background
stage = Stage()
stage.set_background("#1a1a2e")

# Load sound effects
load_sound("hit", "sfx_sound_neutral1")
load_sound("score", "sfx_sound_neutral5")

# Create sprites
paddle1 = Sprite("paddle")
paddle2 = Sprite("paddle")
ball = Sprite("ball")

# Add sprites to stage
stage.add_sprite(paddle1)
stage.add_sprite(paddle2)
stage.add_sprite(ball)

@on_start
def setup():
    global ball_speed_x, ball_speed_y, score1, score2, ball_moving

    # Position paddles on left and right sides
    paddle1.go_to(-440, 0)
    paddle2.go_to(440, 0)

    # Center the ball
    ball.go_to(0, 0)
    ball.size = 80

    # Reset scores and ball state
    score1 = 0
    score2 = 0
    ball_speed_x = 0
    ball_speed_y = 0
    ball_moving = False

    # Show instructions
    ball.say("Press SPACE to start!")

@on_key_press("space")
def start_ball():
    global ball_speed_x, ball_speed_y, ball_moving

    if not ball_moving:
        ball_speed_x = 6
        ball_speed_y = 4
        ball_moving = True
        ball.say("")

@on_forever
def game_loop():
    global ball_speed_x, ball_speed_y, score1, score2, ball_moving

    # Player 1 controls (W/S)
    if key_pressed("w"):
        if paddle1.y < 280:
            paddle1.change_y(paddle_speed)
    if key_pressed("s"):
        if paddle1.y > -280:
            paddle1.change_y(-paddle_speed)

    # Player 2 controls (Up/Down arrows)
    if key_pressed("up arrow"):
        if paddle2.y < 280:
            paddle2.change_y(paddle_speed)
    if key_pressed("down arrow"):
        if paddle2.y > -280:
            paddle2.change_y(-paddle_speed)

    # Only move ball if game is active
    if not ball_moving:
        return

    # Move the ball
    ball.change_x(ball_speed_x)
    ball.change_y(ball_speed_y)

    # Ball bounces off top and bottom walls
    if ball.y > 330 or ball.y < -330:
        ball_speed_y = -ball_speed_y

    # Ball bounces off paddles
    if ball.touching(paddle1) and ball_speed_x < 0:
        ball_speed_x = -ball_speed_x
        ball_speed_y += (ball.y - paddle1.y) * 0.1
        start_sound("hit")

    if ball.touching(paddle2) and ball_speed_x > 0:
        ball_speed_x = -ball_speed_x
        ball_speed_y += (ball.y - paddle2.y) * 0.1
        start_sound("hit")

    # Limit vertical speed
    if ball_speed_y > 16:
        ball_speed_y = 16
    if ball_speed_y < -16:
        ball_speed_y = -16

    # Score when ball goes past paddles
    if ball.x < -480:
        score2 += 1
        reset_ball()

    if ball.x > 480:
        score1 += 1
        reset_ball()

def reset_ball():
    global ball_speed_x, ball_speed_y, ball_moving

    start_sound("score")
    ball.go_to(0, 0)
    ball_speed_x = 0
    ball_speed_y = 0
    ball_moving = False
    ball.say(f"{score1} - {score2}  Press SPACE")

Code Breakdown

Game State

ball_speed_x = 0
ball_speed_y = 0
ball_moving = False
score1 = 0
score2 = 0

Track all game state in global variables.

Sound Effects

load_sound("hit", "sfx_sound_neutral1")
start_sound("hit")  # Play when ball hits paddle

Load sounds with asset names, play them at appropriate moments.

Paddle Movement with Bounds

if key_pressed("w"):
    if paddle1.y < 280:  # Don't go off top
        paddle1.change_y(paddle_speed)

Check bounds before moving to keep paddles on screen.

Ball Physics

# Bounce off walls
if ball.y > 330 or ball.y < -330:
    ball_speed_y = -ball_speed_y

# Bounce off paddle with angle
if ball.touching(paddle1) and ball_speed_x < 0:
    ball_speed_x = -ball_speed_x
    ball_speed_y += (ball.y - paddle1.y) * 0.1  # Add spin

Simple physics with direction reversal and spin based on hit position.

Scoring

if ball.x < -480:
    score2 += 1
    reset_ball()

Check if ball passes paddles to award points.


Racing Game

Avoid traffic in this endless racing game.

What you’ll learn:

  • Object pooling for traffic
  • Lane-based movement
  • Collision detection
  • Background music with looping
  • Progressive difficulty
View Code
"""
Racing Down - Avoid the Traffic!

Use LEFT and RIGHT arrow keys to move your red car
Avoid the oncoming traffic. Don't crash!
Press SPACE to restart after game over
"""
from ektupy import (
    Stage, Sprite, on_start, on_forever, on_key_press,
    load_sound, start_sound, set_sound_loop, set_sound_volume
)
import random

# Create stage with race track background
stage = Stage()
stage.set_background_image("race-track")

# Player's red car
player = Sprite("car")
player.switch_costume("red")
player.size = 80
stage.add_sprite(player)

# Traffic vehicles pool
traffic_types = [
    ("car", "blue", 70),
    ("car", "green", 70),
    ("car", "default", 70),
    ("truck", "yellow", 80),
    ("truck", "default", 80),
]
traffic = []

# Create pool of traffic vehicles
for i in range(6):
    sprite_name, costume_name, size = traffic_types[i % 3]
    car = Sprite(sprite_name)
    car.switch_costume(costume_name)
    car.size = size
    car.hide()
    stage.add_sprite(car)
    traffic.append(car)

# Lane positions (x coordinates for 3 lanes)
LANES = [-130, 0, 130]

# Game state
game_over = False
score = 0
speed = 5

# Load sounds
load_sound("crash", "sfx_damage_hit1")
load_sound("music", "boss_battle")


@on_start
def setup():
    global game_over, score, speed
    game_over = False
    score = 0
    speed = 5

    player.go_to(0, -280)
    player.show()

    for car in traffic:
        car.hide()
        car.go_to(0, 500)

    # Start background music
    set_sound_loop("music", True)
    set_sound_volume("music", 30)
    start_sound("music")


def spawn_traffic():
    for car in traffic:
        if not car.visible:
            lane = random.choice(LANES)
            car.go_to(lane, 400)
            car.show()
            return


def check_collision(car):
    px, py = player.x, player.y
    cx, cy = car.x, car.y
    player_w, player_h = 30, 50
    car_w, car_h = 30, 50

    if abs(px - cx) < (player_w + car_w) / 2:
        if abs(py - cy) < (player_h + car_h) / 2:
            return True
    return False


@on_forever
def game_loop():
    global game_over, score, speed

    if game_over:
        return

    for car in traffic:
        if car.visible:
            car.change_y(-speed)

            if check_collision(car):
                game_over = True
                start_sound("crash")
                player.say("GAME OVER!")
                return

            if car.y < -400:
                car.hide()
                score += 1

    # Spawn new traffic (max 3 at a time)
    if count_active_traffic() < 3:
        if random.random() < 0.03:
            spawn_traffic()

    # Gradually increase speed
    if score > 0 and score % 10 == 0:
        speed = min(12, 5 + score // 10)


@on_key_press("left arrow")
def move_left():
    if not game_over:
        current_x = player.x
        for i, lane in enumerate(LANES):
            if abs(current_x - lane) < 50:
                if i > 0:
                    player.set_x(LANES[i - 1])
                break


@on_key_press("right arrow")
def move_right():
    if not game_over:
        current_x = player.x
        for i, lane in enumerate(LANES):
            if abs(current_x - lane) < 50:
                if i < len(LANES) - 1:
                    player.set_x(LANES[i + 1])
                break


@on_key_press("space")
def restart_game():
    global game_over, score, speed
    if game_over:
        game_over = False
        score = 0
        speed = 5
        player.go_to(0, -280)
        player.say("")
        for car in traffic:
            car.hide()

Code Breakdown

Object Pooling

traffic = []
for i in range(6):
    car = Sprite(sprite_name)
    car.hide()
    traffic.append(car)

Pre-create sprites and reuse them instead of creating new ones. Improves performance.

Lane-Based Movement

LANES = [-130, 0, 130]
for i, lane in enumerate(LANES):
    if abs(current_x - lane) < 50:
        if i > 0:
            player.set_x(LANES[i - 1])

Snap player to discrete lanes instead of free movement.

Background Music

set_sound_loop("music", True)   # Loop the music
set_sound_volume("music", 30)   # 30% volume
start_sound("music")            # Start playing

Configure and play looping background music.

Progressive Difficulty

if score > 0 and score % 10 == 0:
    speed = min(12, 5 + score // 10)

Increase traffic speed every 10 points, capped at 12.


Summary

These examples demonstrate the core features of EktuPy:

CategoryExamples
BasicsHello World, Movement
AnimationBouncing Ball, Costumes
InputArrow Keys, Mouse Tracker, Ask Name
DrawingPen Drawing
Multiple SpritesTwo Sprites, Many Sprites
SensingClock, Mouse Tracker
GamesPong, Racing

Try modifying these examples to learn more. Experiment with:

  • Changing colors and speeds
  • Adding more sprites
  • Creating new game mechanics
  • Combining features from different examples