Игра "Прыг-скок" на Python
Проект из глав 13–14 книги Джейсона Бриггса "Python для детей" (пер. С. Ломакина, 2017). Перед стартом полезны Программа на Python, Классы и объекты и базовые циклы.
Установите Python 3 с галочкой Add Python to PATH. Редактор — IDLE или VS Code. Онлайн-интерпретатор Trinket подходит для простых скриптов; для tkinter нужен локальный Python.
"Прыг-скок!" — первая полноценная игра в самоучителе Бриггса. Красный мяч отскакивает от стен, синяя ракетка двигается стрелками ← →. Если мяч упал ниже ракетки — игра останавливается.
tkinter — стандартная библиотека Python для окон. Canvas (холст) — прямоугольная область, на которой рисуют фигуры и двигают их каждый кадр.
Идеи, которые Вы освоите
| Идея | Где встречается в коде |
|---|---|
| Окно и холст | Tk(), Canvas(...) |
Классы Ball и Paddle | мяч и ракетка как объекты |
| Игровой цикл | while True + update + sleep |
| Столкновения | метод hit_paddle, функция coords() |
| Клавиатура | bind_all для стрелок |
Шаг 1. Холст и окно
Создайте файл pryg_skok.py:
from tkinter import *
import random
import time
tk = Tk()
tk.title("Прыг-скок!")
tk.resizable(0, 0)
tk.wm_attributes("-topmost", 1)
canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
tk.update()
Параметры окна:
resizable(0, 0)— нельзя растянуть мышью-topmost— окно поверх других (удобно при отладке)bd=0,highlightthickness=0— без рамки вокруг поля
Шаг 2. Класс мяча
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
starts = [-3, -2, -1, 1, 2, 3]
random.shuffle(starts)
self.x = starts[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
def draw(self):
self.canvas.move(self.id, self.x, self.y)
create_oval(x1, y1, x2, y2)рисует эллипс в прямоугольникеmove(id, dx, dy)сдвигает фигуру наdxиdyпикселейrandom.shuffle(starts)перемешивает список — мяч стартует в случайном направлении по горизонтали
self.x и self.y — скорость (сколько пикселей добавлять за один кадр).
Шаг 3. Отскок от стен
Добавьте в draw проверку границ:
def draw(self):
self.canvas.move(self.id, self.x, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 3
if pos[3] >= self.canvas_height:
self.y = -3
if pos[0] <= 0:
self.x = 3
if pos[2] >= self.canvas_width:
self.x = -3
coords(id) возвращает список [x1, y1, x2, y2] — координаты ограничивающего прямоугольника фигуры.
pos[1]— верхний край; удар о верх → меняемself.ypos[3]— нижний крайpos[0]иpos[2]— левый и правый края
Шаг 4. Главный цикл
В конце файла:
ball = Ball(canvas, 'red')
while True:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Запустите (F5 в IDLE). Мяч летает по полю.
Игровой цикл — бесконечный while, который каждый кадр:
- двигает объекты
- обновляет экран (
update) - делает короткую паузу (
sleep(0.01)≈ 10 мс)
Без паузы движение будет слишком быстрым.
Шаг 5. Класс ракетки
class Paddle:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_rectangle(0, 0, 100, 10, fill=color)
self.canvas.move(self.id, 200, 300)
self.x = 0
self.canvas_width = self.canvas.winfo_width()
self.canvas.bind_all('<KeyPress-Left>', self.turn_left)
self.canvas.bind_all('<KeyPress-Right>', self.turn_right)
def draw(self):
self.canvas.move(self.id, self.x, 0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
elif pos[2] >= self.canvas_width:
self.x = 0
def turn_left(self, evt):
self.x = -2
def turn_right(self, evt):
self.x = 2
bind_all привязывает нажатие клавиши к методу. evt — объект события (здесь не используется, но параметр обязателен).
Кликните по окну игры, затем жмите стрелки — иначе фокус может остаться в редакторе.
В цикле добавьте:
paddle = Paddle(canvas, 'blue')
ball = Ball(canvas, 'red')
while True:
ball.draw()
paddle.draw()
...
Шаг 6. Столкновение мяча с ракеткой
В конструкторе Ball добавьте аргумент paddle и строку self.paddle = paddle.
Метод проверки:
def hit_paddle(self, pos):
paddle_pos = self.canvas.coords(self.paddle.id)
if pos[2] >= paddle_pos[0] and pos[0] <= paddle_pos[2]:
if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]:
return True
return False
Логика: нижняя точка мяча (pos[3]) попала в прямоугольник ракетки по горизонтали и вертикали.
В draw после движения:
if self.hit_paddle(pos):
self.y = -3
Создание объектов:
paddle = Paddle(canvas, 'blue')
ball = Ball(canvas, paddle, 'red')
Шаг 7. Проигрыш
В __init__ мяча добавьте self.hit_bottom = False.
В draw, когда низ мяча коснулся пола:
if pos[3] >= self.canvas_height:
self.hit_bottom = True
Главный цикл:
while True:
if not ball.hit_bottom:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Мяч проскочил мимо ракетки — анимация останавливается.
Полный код (шпаргалка)
Развернуть готовую программу
from tkinter import *
import random
import time
class Ball:
def __init__(self, canvas, paddle, color):
self.canvas = canvas
self.paddle = paddle
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
starts = [-3, -2, -1, 1, 2, 3]
random.shuffle(starts)
self.x = starts[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
self.hit_bottom = False
def hit_paddle(self, pos):
paddle_pos = self.canvas.coords(self.paddle.id)
if pos[2] >= paddle_pos[0] and pos[0] <= paddle_pos[2]:
if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]:
return True
return False
def draw(self):
self.canvas.move(self.id, self.x, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 3
if pos[3] >= self.canvas_height:
self.hit_bottom = True
if self.hit_paddle(pos):
self.y = -3
if pos[0] <= 0:
self.x = 3
if pos[2] >= self.canvas_width:
self.x = -3
class Paddle:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_rectangle(0, 0, 100, 10, fill=color)
self.canvas.move(self.id, 200, 300)
self.x = 0
self.canvas_width = self.canvas.winfo_width()
self.canvas.bind_all('<KeyPress-Left>', self.turn_left)
self.canvas.bind_all('<KeyPress-Right>', self.turn_right)
def draw(self):
self.canvas.move(self.id, self.x, 0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
elif pos[2] >= self.canvas_width:
self.x = 0
def turn_left(self, evt):
self.x = -2
def turn_right(self, evt):
self.x = 2
tk = Tk()
tk.title("Прыг-скок!")
tk.resizable(0, 0)
tk.wm_attributes("-topmost", 1)
canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
tk.update()
paddle = Paddle(canvas, 'blue')
ball = Ball(canvas, paddle, 'red')
while True:
if not ball.hit_bottom:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Улучшения из книги
- Старт по клику — привязка к
<Button-1>, игра ждёт клика по полю - Надпись "Конец игры" —
canvas.create_text(...)иitemconfig(..., state='hidden') - Счёт очков — +1 при отскоке от ракетки, текст в углу экрана
- Ускорение мяча — менять
self.x/self.yпосле каждого удара
Следующий проект в книге — Человечек спешит к выходу с GIF-спрайтами и платформами.
Частые ошибки
| Симптом | Причина |
|---|---|
| Окно мигнуло и закрылось | Нет цикла while или синтаксическая ошибка — смотрите текст в Shell |
| Стрелки не работают | Не кликнули по окну игры |
| Мяч проходит сквозь ракетку | Шаг 3 px велик; проверяйте пересечение по всей высоте ракетки |
TclError при закрытии | Цикл ещё крутится, окно уже закрыто — остановите через Ctrl+C |
Связанные материалы
- Классы и объекты — как устроены
BallиPaddle - Человечек спешит к выходу — вторая игра из книги
- Строки, списки и словари
- Черепашка turtle — более простая графика
- Арканоид в Scratch — та же механика в блоках