Курси програмування для дітей безкоштовно

23. Гра на Python Теніс

Гра на Python Теніс (Пінг-понг) — це ваш рецепт створення власної аркадної гри, де гравці керують ракетками, щоб відбивати м’ячик по черзі.
Ви навчитеся програмувати рух об’єктів, рахувати очки й робити гру динамічною та цікавою.
І найкрутіше — ви зможете налаштувати гру під себе: змінювати кольори, швидкість і навіть рівні складності, щоб грати ще веселіше!

Практика. Гра на Python Теніс

Створення ігрового поля

1: Почнемо з бази — налаштуємо ігрове поле. Вам потрібно створити головне вікно, область для відображення анімації та додати основні елементи гри.

Створімо заготовку для гри разом з глобальними змінними. Як бачите, у нас буде блок глобальних змінних, блок функцій і блок основної логіки. Розташовуйте код відповідно до цієї схеми — це допоможе створити правильну структуру й уникнути помилок в майбутньому. Створіть вказані змінні.

from tkinter import *
### глобальні змінні
# налаштування вікна
Створіть змінні ширини WIDTH у  900 пікселів та висоти HEIGHT у 300 пікселів.

# налаштування ракеток
Створіть змінні PAD_W та PAD_H для ширини та висоти ракетки відповідно. Встановіть ширину ракетки у 10 пікселів, а висоту у 100 пікселів.

# налаштування мʼячика
Додайте змінну BALL_RADIUS для радіуса м’яча та встановіть її значення у 30 пікселів.

### Функції для роботи з ігровими об'єктами


### Основна програма

Зверніть увагу на глобальні змінні на початку коду. Вони значно спрощують роботу. Наприклад, якщо ширина вікна 900 пікселів, набагато зручніше створити змінну WIDTH, присвоїти їй значення 900 і використовувати її в коді, ніж щоразу вручну писати 900.
Ви особливо оціните цей підхід, коли захочете щось змінити — наприклад, ширину вікна. Достатньо буде змінити лише одне значення, і вся гра автоматично підлаштується 🙂

2: Створімо базове вікно гри та додамо всі основні елементи.

Гра на Python Теніс | Python для дітей | ITisFuture
Гра на Python Теніс | Python для дітей | ITisFuture

Пояснення коду :

🔹 Створюємо вікно
root = Tk()
root.title(“Pythonic Pong”) – назва вікна зверху
🔹 Поле для гри
c = Canvas(root, width=WIDTH, height=HEIGHT, background=”#003300″) — створюємо “полотно” (Canvas), де буде гра. Вказуємо розмір і колір фону (темно-зелений)
c.pack() – Показуємо це полотно у вікні
🔹 Малюємо лінії поля.
c.create_line(PAD_W, 0, PAD_W, HEIGHT, fill=”white”) малюємо ліву вертикальну лінію (відступаємо від краю поля на ширину ракетки, щоб вона помістилася)
c.create_line(WIDTH-PAD_W, 0, WIDTH-PAD_W, HEIGHT, fill=”white”) малюємо праву лінію (відступаємо від краю поля на ширину ракетки)
c.create_line(WIDTH/2, 0, WIDTH/2, HEIGHT, fill=”white”) малюємо центральну лінію (як у тенісі)

🟨 Створення ліній :

create_line(x1, y1, x2, y2, width=…, fill=…)

  • (x1, y1) — точка початку лінії
  • (x2, y2) — точка кінця лінії
  • width — товщина
  • fill — колір

🔹 Створення м’яча : овал, який вписаний в прямокутник. Координати прямокутника вказані за допомогою двох точок : лівого верхнього і правого нижнього кута.
BALL = c.create_oval(…)

create_oval(x1, y1, x2, y2, fill=…)

  • (x1, y1) — верхній лівий кут уявного прямокутника
  • (x2, y2) — нижній правий кут
  • fill — колір фігури

BALL = c.create_oval(WIDTH/2-BALL_RADIUS/2,
HEIGHT/2-BALL_RADIUS/2,
WIDTH/2+BALL_RADIUS/2,
HEIGHT/2+BALL_RADIUS/2, fill=”white”)

🔹 Ракетки.
LEFT_PAD = c.create_line(PAD_W/2, 0, PAD_W/2, PAD_H, width=PAD_W, fill=”yellow”) малюємо ліву вертикальну лінію (ліву ракетку)
RIGHT_PAD = c.create_line(WIDTH-PAD_W/2, 0, WIDTH-PAD_W/2, PAD_H, width=PAD_W, fill=”yellow”) малюємо праву лінію (праву ракетку)

🟨 Створення ракеток : Ракетка — це товста лінія шириною width=PAD_W, як дуже витягнутий прямокутник.

LEFT_PAD = c.create_line(PAD_W/2, 0, PAD_W/2, PAD_H, width=PAD_W, fill=”yellow”)

Перша точка: PAD_W/2 → це X (по горизонталі, половинка ширини ракетки = центр лінії), а 0 → це Y (верх екрана)
Друга точка: PAD_W/2 (той самий X!) y = PAD_H (вниз на висоту ракетки)

Аналогічно, друга ракетка. Тільки в кінці поля, тому розраховуємо від ширини поля WIDTH-PAD_W.

🔹 Запуск гри: root.mainloop() йде за допомогою циклу. Функція mainloop() для динамічної гри, вона створить нескінченний цикл і слухатиме дії клавіатури чи мишки.

Перший запуск

У результаті у вас має вийти приблизно таке ігрове поле:

Гра на Python Теніс | Python для дітей | ITisFuture

Це наш “скелет” гри. Далі додамо рух і зробимо її по-справжньому інтерактивною 😉

Змушуємо м’яч рухатися

Додаймо цій грі трохи драйву! Створимо рух мʼячика. Для цього нам знадобиться нова функція move_ball, у якій ми опишемо логіку руху м’яча. Саме тут м’яч буде “оживати” і переміщатися по полю.
Також створімо функцію main. Вона буде:
🥎 викликати move_ball
🥎 запускати саму себе знову через root.after|
Таким чином, утворюється цикл, який постійно оновлює стан гри. Це і є основа будь-якої простої анімації в tkinter.

3: Додайте глобальні змінні швидкості м’яча:
рух по горизонталі: BALL_X_CHANGE зі значенням 20
рух по вертикалі BALL_Y_CHANGE зі значенням 0

4: Створіть функцію def move_ball() без параметрів. Функція має викликати команду :
c.move() — рух об’єкта з області анімації Canvas (ігрового поля). Ця функція потребує параметрів, тому передайте в неї :
BALL — це саме той об’єкт, який рухаємо (м’яч)
BALL_X_CHANGE — на скільки пікселів рухаємо по горизонталі
BALL_Y_CHANGE — на скільки пікселів рухаємо по вертикалі

5: Також створіть функцію main, яка буде робити дві команди:
🥎 викликати функцію move_ball() без параметрів
🥎 викликати функцію main кожні 30 мілісекунд : root.after(30, main). Це явище в програмування називається рекурсія

6: Додайте виклик функції main() в основну програму. (Перед рядком root.mainloop()). Це запустить рух м’яча із запуском гри


Якщо ви все зробили правильно, після запуску м’яч буде рухатися вправо. Запустіть програму

Ви можете змінювати швидкість і напрямок руху по горизонталі, змінюючи значення змінної BALL_X_CHANGE.

Рекурсія в Python

Рекурсія в Python — це коли функція викликає сама себе, щоб розв’язати задачу по частинах. Уявіть це як “розбирати задачу на менші копії самої себе” 🧩. У рекурсії завжди є 2 частини:
Рекурсивний крок — коли вона викликає сама себе
Базовий випадок — коли функція зупиняється

Рекурсія у Python | Python для дітей | ITisFuture

Як це виглядає в Python на практиці:

Приклад: порахувати факторіал числа (наприклад, 5! = 5×4×3×2×1)


def factorial(n):
# базовий випадок (коли зупинка)
    if n == 1: 
        return 1
    # виклик самої себе
    return n * factorial(n - 1)   

🔹 Як це працює
factorial(5)
→ 5 * factorial(4)
→ 5 * 4 * factorial(3)
→ 5 * 4 * 3 * factorial(2)
→ 5 * 4 * 3 * 2 * factorial(1)
→ 5 * 4 * 3 * 2 * 1 = 120

Не хвилюйтеся, якщо слово “рекурсія” звучить складно — на початку вона може трохи дивувати, але її розуміння дає ключ до розв’язання складніших задач у програмуванні 🙂

Керуємо ракетками

Тепер додамо керування ракетками, щоб гра стала інтерактивною.

Логіка тут дуже проста:
🏓у кожної ракетки є швидкість
🏓 спочатку вона дорівнює 0 — ракетка стоїть на місці
🏓коли користувач натискає клавішу — швидкість змінюється
🏓 ракетка починає рухатися вгору або вниз
🏓коли клавішу відпускають — швидкість знову стає 0

Тобто, фактично ви керуєте не позицією ракетки напряму, а її швидкістю. Цей підхід, використовується в багатьох іграх 🙂

Далі ми реалізуємо це в коді та прив’яжемо рух до клавіш клавіатури

7: Задайте глобальні змінні швидкості ракеток:
🏓PAD_SPEED – швидкість руху ракеток, значення 20
🏓LEFT_PAD_SPEED – швидкість лівої ракетки, значення 0
🏓RIGHT_PAD_SPEED – швидкість правої ракетки, значення 0

8: Створіть функцію руху ракеток:

Гра на Python Теніс | Python для дітей | ITisFuture

🏓 c.move(pad, 0, PADS[pad]) – рух об’єкта
pad – об’єкт, який рухаємо (одна з ракеток)
по X = 0 (вліво/вправо не рухається)
по Y = швидкість (вгору/вниз)
Тобто: ракетка рухається вверх або вниз

🏓 Перевірка: чи ракетка не вилізла вгору? if c.coords(pad)[1] < 0:
[1] — це верхня точка ракетки. Якщо вона < 0 → ракетка вилізла за верх
c.move(pad, 0, -c.coords(pad)[1]) – повертаємо її назад на поле

🏓 Перевірка: не вилізла вниз? elif c.coords(pad)[3] > HEIGHT:
[3] — це нижня точка. Якщо > HEIGHT → вилізла вниз
Тоді виправляємо: c.move(pad, 0, HEIGHT – c.coords(pad)[3]) – підтягуємо її назад в поле

9: Оновлюємо функцію main, додамо туди ще постійний виклик руху ракеток:

def main():
    move_ball()
    move_pads()
    root.after(30, main)

10: Створімо функцію обробки натискання клавіш. Допишіть код нижче, керуючись поясненнями:

movement_handler(event) – це функція, яка спрацьовує, коли гравець натискає кнопку на клавіатурі
event — це “повідомлення” від клавіатури. В ньому є: event.keysym – назва клавіші, наприклад “w” чи “Up” (стрілка вгору)

🏓 Клавіші керування лівою ракеткою : “w” – рух вгору і “s” – рух вниз
🏓 Клавіші керування правою ракеткою : “Up” (стрілка вгору) – рух вгору і “Down”(стрілка вгору) – рух вниз

🔹 Логіка кнопок
if event.keysym == “w”: – якщо натиснули w
LEFT_PAD_SPEED = -PAD_SPEED → Ліва ракетка їде вгору
elif event.keysym == “s”: – якщо натиснули s
LEFT_PAD_SPEED = PAD_SPEED → Ліва ракетка їде вниз

# обробка натискання клавіш
def movement_handler(event):
    global LEFT_PAD_SPEED, RIGHT_PAD_SPEED
    
    if event.keysym == "w":
        LEFT_PAD_SPEED = -PAD_SPEED
    elif event.keysym == "s":
        LEFT_PAD_SPEED = PAD_SPEED
    elif натуснули_стрілку_вгору:
        🏓🏓🏓🏓🏓= 🏓🏓🏓🏓
    elif натуснули_стрілку_вниз:
        🏓🏓🏓🏓🏓= 🏓🏓🏓🏓

Зупинка ракеток

11: Створімо функцію stop_pad з параметром event, яка буде зупиняти рух ракеток, якщо клавішу відпустили:
🏓Вкажіть глобальні змінні LEFT_PAD_SPEED, RIGHT_PAD_SPEED у функції
🏓Якщо event.keysym in (“w”, “s”), то LEFT_PAD_SPEED = 0. Тобто, якщо відпустили одну з клавіш w чи s, то швидкість лівої ракетки стає 0
🏓Аналогічно, якщо відпустили одну зі стрілочок “Up” чи “Down”, то швидкість правої ракетки стає 0.

12: Тепер час змінити основну програму і слухати події натискання клавіш. Додайте цей код перед викликом main():

# встановлюємо фокус на Canvas (щоб ловити натискання клавіш)
c.focus_set()

# прив’язуємо натискання клавіш
c.bind("<KeyPress>", movement_handler)

# прив’язуємо відпускання клавіш
c.bind("<KeyRelease>", stop_pad)

13: Запустіть і перевірте, що ви можете керувати обома ракетками 🎮. М’ячик поки не має відбиватися, це ми додамо згодом

Відскок м’яча від стінок і ракеток

Тепер прокачаємо м’ячик, щоб він вмів відбиватися від перешкод:
🥎 при зіткненні зі стінкою або ракеткою змінюємо напрямок руху м’яча
🥎 при ударі об ракетку:
🥎🥎 трохи збільшується швидкість
🥎🥎 вертикальний рух змінюється випадковим чином

14: Додайте бібліотеку import random для обробки випадкових подій. Також додайте кілька нових глобальних змінних:
BALL_SPEED_UP – наскільки збільшується швидкість після удару, значення 1.05
BALL_MAX_SPEED – максимальна швидкість м’яча, значення 40
BALL_X_SPEED – початкова швидкість по горизонталі, значення 20
BALL_Y_SPEED – початкова швидкість по вертикалі, значення 20
right_line_distance – відстань до правої межі поля, значення WIDTH – PAD_W

14: Створімо функцію відскоку м’яча bounce.

Гра на Python Теніс | Python для дітей | ITisFuture

Є 2 ситуації
🥎 if action == “strike”: 1: М’яч вдарився об ракетку
🥎 else: 2. М’яч вдарився об стінку

🥎 1. Удар об ракетку
BALL_Y_SPEED = random.randrange(-10, 10) м’яч отримує випадковий рух вверх/вниз
Тобто: кожен удар – це трохи змінює напрямок його руху

Зміна напрямку по X: if abs(BALL_X_SPEED) < BALL_MAX_SPEED:
BALL_X_SPEED *= -BALL_SPEED_UP

Що тут відбувається:
* -1 – змінює напрямок (вліво ↔ вправо)
BALL_SPEED_UP → трохи прискорює
Тобто, м’яч відскакує і стає швидшим

Якщо м’ячик вже дуже швидкий: else:
BALL_X_SPEED = -BALL_X_SPEED
то просто змінює напрямок (без прискорення)

🥎 2. Удар об стінку
BALL_Y_SPEED = -BALL_Y_SPEED міняємо напрямок по вертикалі
летів вниз → полетить вверх
летів вверх → полетить вниз

Програмуємо основну логіку м’ячика

15: Знайдіть функцію move_ball і розширте її логіку (поточний рядок можна буде замінити на новий код):

# функція руху м’яча
def move_ball():
    # координати м’яча
    ball_left, ball_top, ball_right, ball_bot = c.coords(BALL)
    ball_center = (ball_top + ball_bot) / 2

    # перевірка руху по горизонталі
    if ball_right + BALL_X_SPEED < right_line_distance and \
       ball_left + BALL_X_SPEED > PAD_W:
        c.move(BALL, BALL_X_SPEED, BALL_Y_SPEED)

    # торкання межі
    elif ball_right == right_line_distance or ball_left == PAD_W:
        
        if ball_right > WIDTH / 2:
            # права ракетка
            if c.coords(RIGHT_PAD)[1] < ball_center < c.coords(RIGHT_PAD)[3]:
                bounce("strike")
            else:
                # промах (додамо пізніше)
                pass
        else:
            # ліва ракетка
            if c.coords(LEFT_PAD)[1] < ball_center < c.coords(LEFT_PAD)[3]:
                bounce("strike")
            else:
                pass
    else:
        # якщо м’яч вилітає за межі — повертаємо його
        if ball_right > WIDTH / 2:
            c.move(BALL, right_line_distance - ball_right, BALL_Y_SPEED)
        else:
            c.move(BALL, -ball_left + PAD_W, BALL_Y_SPEED)

    # відскок від верхньої/нижньої межі
    if ball_top + BALL_Y_SPEED < 0 or ball_bot + BALL_Y_SPEED > HEIGHT:
        bounce("ricochet")

🧠 Загальна логіка м’яча:
🥎 летить
🥎 перевіряє — не вдарився?
🥎 якщо вдарився → змінює напрямок

🥎 1. Беремо координати ball_left, ball_top, ball_right, ball_bot = c.coords(BALL)
Це межі м’яча: ліва, верхня, права, нижня

ball_center = (ball_top + ball_bot) / 2 центр по вертикалі (щоб перевірити попадання в ракетку)

🥎 2. Якщо нічого не заважає — просто летимо
if ball_right + BALL_X_SPEED < right_line_distance and \ ball_left + BALL_X_SPEED > PAD_W: (якщо не вийшов справа чи зліва), тоді:
c.move(BALL, BALL_X_SPEED, BALL_Y_SPEED) просто рухаємо м’яч (летимо)

🥎 3. Торкання ракетки
elif ball_right == right_line_distance or ball_left == PAD_W: якщо м’яч дійшов до краю (де ракетки)

Якщо справа if ball_right > WIDTH / 2: значить це права сторона
Перевірка попадання
if c.coords(RIGHT_PAD)[1] < ball_center < c.coords(RIGHT_PAD)[3]: центр м’яча в межах ракетки?

✔ так → bounce(“strike”)
❌ ні → pass (пізніше тут буде “гол”)

Якщо зліва — те саме тільки для лівої ракетки

🥎 4. Якщо м’яч виліз за межі else:, то трохи “поправляємо” його назад у поле:

Якщо справа ball_right > WIDTH / 2, то c.move(BALL, right_line_distance – ball_right, BALL_Y_SPEED)
повертаємо назад

Якщо зліва, то теж повертаємо c.move(BALL, -ball_left + PAD_W, BALL_Y_SPEED)

🥎 5. Відскок від верху/низу
if ball_top + BALL_Y_SPEED < 0 or ball_bot + BALL_Y_SPEED > HEIGHT: м’яч торкнувся верху або низу
то bounce(“ricochet”) відскакує

Тепер м’яч відбивається від стінок і ракеток. Якщо гравець не встиг відбити — м’яч “зависає” біля краю. Далі це виправимо, додавши рахунок і перезапуск м’яча.

Підрахунок очок і респаун м’яча

16: Додамо рахунок для обох гравців. Створіть глобальні змінні:
PLAYER_1_SCORE = 0
PLAYER_2_SCORE = 0

17: Тепер відобразимо рахунок на екрані (можна додати в місце, де малюються лінії):

p_1_text = c.create_text(WIDTH - WIDTH/6, PAD_H/4,
                         text=PLAYER_1_SCORE,
                         font="Arial 20",
                         fill="white")

Аналогічно p_2_text, тільки координати : WIDTH/6, PAD_H/4 і текст PLAYER_2_SCORE.

Запустіть гру, має відображатися початковий рахунок:

Гра на Python Теніс | Python для дітей | ITisFuture

Залишилося додати оновлення рахунку й у вас буде повноцінний Pong 🏓

18: Створимо функції для оновлення рахунку та респауну. Для цього створіть ще одну глобальну змінну: INITIAL_SPEED — початкова швидкість м’яча, значення 20.

19: Створіть функцію оновлення рахунку update_score з параметром player – це гравець, якому оновлюємо рахунок (правий чи лівий):
🤾‍♀️ Визначаємо глобальні змінні PLAYER_1_SCORE, PLAYER_2_SCORE
🤾‍♀️Якщо передали правого гравця (player == “right”), то збільшуємо рахунок першого PLAYER_1_SCORE на 1. І відображаємо рахунок c.itemconfig(p_1_text, text=PLAYER_1_SCORE)
🤾‍♀️Інакше, збільшуємо рахунок лівого (другого) грався і відображаємо новий рахунок для нього

20: Створимо функцію респауну (оновлення м’яча). Вона буде ставити м’яч у центр і почне його рухати у бік гравця, який тільки-но пропустив з початковою швидкістю.

def spawn_ball():
    global BALL_X_SPEED
    
    # ставимо м’яч у центр
    c.coords(BALL,
             WIDTH/2 - BALL_RADIUS/2,
             HEIGHT/2 - BALL_RADIUS/2,
             WIDTH/2 + BALL_RADIUS/2,
             HEIGHT/2 + BALL_RADIUS/2)
    
    # задаємо напрямок у сторону гравця, який пропустив
    # і повертаємо початкову швидкість
    BALL_X_SPEED = -(BALL_X_SPEED * -INITIAL_SPEED) / abs(BALL_X_SPEED)

21: Тепер потрібно замінити pass у функції move_ball на виклик функцій оновлення рахунку і респану.
Замініть ТІЛЬКИ виділений жовтим код. Було:

if c.coords(RIGHT_PAD)[1] < ball_center < c.coords(RIGHT_PAD)[3]:
    bounce("strike")
else:
    pass
else:
    if c.coords(LEFT_PAD)[1] < ball_center < c.coords(LEFT_PAD)[3]:
        bounce("strike")
    else:
        pass

Має стати 👇

if c.coords(RIGHT_PAD)[1] < ball_center < c.coords(RIGHT_PAD)[3]:
   bounce("strike")
else:
   update_score("left")
   spawn_ball()
else:
   if c.coords(LEFT_PAD)[1] < ball_center < c.coords(LEFT_PAD)[3]:
       bounce("strike")
   else:
       update_score("right")
       spawn_ball()

 Що тепер відбувається:
🤾‍♂️якщо гравець не відбив м’яч — суперник отримує очко
🤾‍♂️рахунок оновлюється прямо на екрані
🤾‍♂️м’яч повертається в центр і летить у сторону того, хто програв розіграш

Готово 🎉

Тепер ваш «Пінг-понг» — повноцінна гра, у яку вже можна грати з другом.

Гра на Python Теніс | Python для дітей | ITisFuture

Приємної гри! 🏓

Уроки програмування для дітей. Домашня робота

Домашня робота

1: Звершіть гру та протестуйте, щоб усе працювало правильно. Перефарбуйте ігрове поле та ракетки у кольори на свій смак.
2: Зробіть так, щоб при відбитті м’ячика ракеткою додавалися не одне, а два очки.
3: Домалюйте вертикальну лінію до поля, як на фото.
4: (Завдання виконується окремо від гри). Роздивіться приклад рекурсії. Зробіть одне з завдань на вибір за допомогою рекурсії:

Гра на Python Теніс | Python для дітей | ITisFuture

🪐Знайдіть суму чисел від 1 до n. Приклад: sum(5) = 1 + 2 + 3 + 4 + 5 = 15
🪐Піднесіть число до степеня без використання **. Приклад : pow(2, 3) = 8

Мем про ІТ  | Python для дітей | ITisFuture
Уроки програмування для дітей ускладнені завдання level up

Level Up!
Гра на Python Теніс

1: Додайте можливість грати одному гравцю (користувач обирає в консолі).
Підказка: простіше реалізувати, якщо збільшити одну з ракеток до максимальної висоти.

2: Додайте 3 рівні складності. Нехай користувач обирає рівень:
Новачок
Середній
Геймер

І залежно від рівня змінюйте швидкість м’яча і розмір ракеток.

3: Розв’яжіть одне з завдань на ваш вибір за допомогою рекурсії:

🪐Послідовність Фібоначчі — це такі числа, де кожне наступне число є сумою попередніх (крім перших): 0, 1, 1, 2, 3, 5, 8, 13, 21…
Поверніть n-те число Фібоначчі.
🪐Переверніть рядок за допомогою рекурсії. Приклад:
"hello" → "olleh"
🪐 Знайдіть суму цифр числа. Приклад: 123 → 1 + 2 + 3 = 6

Рекурсія у Python | Python для дітей | ITisFuture

Не зупиняйтеся у вивченні Python! До зустрічі на наступних уроках!

Залишити коментар

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *