Начало
В этой статье я хочу поделиться своим опытом с создании игры ping-pong на пайтон с использованием модуля turtle. Мне 14 лет и я только начинаю программировать, так что не судите строго)

Инициализируем библиотеки turtle и random (чтобы выбирать случайное направление мяча)
from turtle import * from random import choice
Часть кода для закрытия окна без ошибки
Если честно эту часть кода я подсмотрел в интернете
scr = Screen() canvas = scr.getcanvas() root = canvas.winfo_toplevel() scr.setup(600, 400) scr.title('Ping-Pong') scr.cv._rootwindow.resizable(False, False) def on_close(): global running running = False root.destroy() root.protocol("WM_DELETE_WINDOW", on_close) running = True
Эта часть программы нужна для правильного закрытия окна (далее будет бесконечный цикл поэтому это нужно)
Также здесь я настроил размеры окна и убрал увеличение/уменьшение окна
Настройка счёта
В этой части я обычной черепашкой (предварительно подняв её перо) перемещаюсь в верхнюю часть экрана и пишу там счёт 0-0
penup() hideturtle() setposition(0, 150) write(f'Красный: 0 Синий: 0', font=("Arial", 20, "bold"), align="center")

Также я добавил список с направлениями в которых будет двигаться мяч
lst = [30, 60, 120, 150, 210, 240, 300, 330]
Создание класса-наследника Sprint
Не знаю почему именно Sprint но пускай будет
Этот класс наследует всё от класса Turtle(), а также я добавил туда свои свойства и методы
class Sprint(Turtle): score = 0 def __init__(self, x, width, height, color_, shape="square"): super().__init__(shape) self.color(color_) self.x = x self.shapesize(width, height) def change_settings(self): self.penup() self.speed(0) self.setposition(self.x, 0) def move_up(self): x, y = self.pos() if y <= 150: self.setposition(x, y+20) def move_down(self): x, y = self.pos() if y >= -140: self.setposition(x, y-20)
В этот класс первым делом я добавляю свойство score для счётчика очков
Потом инициализирую следующие свойства — x, width, height, color_, shape
shape я сразу передаю супер-классу
Методы:
-
Настройки всего класса
-
Двигаться вверх
-
Двигаться вниз
В методе change_settings я просто даю настройки черепашки — поднимаю перо, устанавливаю скорость 0 (чтобы не было анимации) и устанавливаю позицию черепашки
Методы move_up и move_down говорят сами за себя. В них я распаковкой получай значения x и y черепашки и проверяю: если она не выходит за границы, то двигаю её на 20 пикселей вверх или вниз
Создание класса-наследника Ball
Также класс-наследник от turtle со своими свойствами и методами
class Ball(Turtle): def __init__(self, shape="circle"): super().__init__(shape) def change_settings(self): self.penup() self.speed(0) self.shapesize(0.7) self.setheading(choice(lst))
Также супер классу передаю форму и такой-же метод change_settings
В методе change_settings я: поднимаю перо, устанавливаю скорость 0 (чтобы убрать анимацию), устанавливаю размер мяча 0.7 (я посчитал этот размер оптимальным), и выбираю случайное направление мяча
Основная часть программы
Вот мы и подошли к методу класса Ball в котором и будет твориться вся дичь
def go(self, enemy1, enemy2): while running: if not running: break self.forward(2.5) if self.ycor() >= 190: self.setheading(360 - self.heading()) elif self.ycor() <= -180: self.setheading(360 - self.heading()) if self.xcor() < -300: self.setposition(0, 0) self.setheading(choice(lst)) enemy2.score += 1 clear() write(f'Красный: {enemy1.score} Синий: {enemy2.score}', font=("Arial", 20, "bold"), align="center") elif self.xcor() > 300: self.setposition(0, 0) self.setheading(choice(lst)) enemy1.score += 1 clear() write(f'Красный: {enemy1.score} Синий: {enemy2.score}', font=("Arial", 20, "bold"), align="center") if -179 > self.xcor() > -181 and abs(self.ycor() - enemy1.ycor()) <= 50: self.setheading(180 - self.heading()) self.forward(2) if 179 < self.xcor() < 181 and abs(self.ycor() - enemy2.ycor()) <= 50: self.setheading(180 - self.heading()) self.forward(2)
Давайте я буду рассказывать всё по частям
1-4 строка:
Объявление метода go с двумя параметрами (это наши черепашки)
Запуск бесконечного цикла и вместо while True я пишу while running, помните первую часть кода? Так вот. В ней проверяется не закрылось ли окно и если так то выполняется функция on_close, где переменная running объявляется глобальной и ей присваивается значение False, тем самым завершая цикл (также в той функции происходит закрытие окна).
Пятая строка нужна чтобы наш мяч двигался
6-9 строка:
Проверяется не вышил ли мяч за границы по y и если это так то зеркально отражает шарик (меняет его направление)
11-22 строка:
В этой части кода происходит проверка на выход мяча за границы по x. Если это так то мяч перемещается на координаты 0, 0 и заново выбирается направление
Также здесь происходит подсчёт очков. Если мяч вышел за границы красной платформы то добавляется очко синей и наоборот. Потом обычная черепашка стирает старую информацию про счёт и печатает обновлённую
24-30 строка:
Тут происходит проверка соприкосновения мяча с платформой
Как это примерно работает:
Сначала проверяется не дошёл ли мяч до платформы (а именно до координаты -180 или 180). Затем я проверяю что координаты y совпадают у обоих элементов, выглядит это примерно вот так:
abs(self.ycor() - enemy1.ycor()) <= 50
Тут из координаты y мяча вычитается координата y платформы и если эти значения по модулю не превышают 50, то значит платформа и мяч находятся в соприкосновении
Если условие выполняется и меняю направление мяча на противоположное и сразу иду вперёд на 2 пикселя, чтобы не было повторного выполнения условия
Создание экземпляров класса Sprint
t1 = Sprint(-200, 4, 1, 'red') t1.change_settings() t2 = Sprint(200, 4, 1, 'blue') t2.change_settings()
Создаю 2 экземпляра передавая каждому свои аргументы и сразу выполняю метод change_settings()
Создание экземпляра класса Ball
ball = Ball() ball.change_settings()
В этот класс мне не нужно передавать аргументов. Также сразу запускаю метод change_settings()
Настройка обработки нажатий
Начинаю считывать нажатия с помощью scr.listen()
scr.listen() onkey(t1.move_up, 'w') onkey(t1.move_down, 's') onkey(t2.move_up, 'Up') onkey(t2.move_down, 'Down')
На клавиши w, s у нас реагирует красная платформа, а на стрелочки вверх и вниз реагирует синяя платформа
Запуск метода go
Запускаем метод класса Ball передав два аргумента в виде черепашек и наслаждаемся игрой)
ball.go(t1, t2)
Полный код:
from turtle import * from random import choice from time import sleep scr = Screen() canvas = scr.getcanvas() root = canvas.winfo_toplevel() scr.setup(600, 400) scr.title('Ping-Pong') scr.cv._rootwindow.resizable(False, False) def on_close(): global running running = False root.destroy() root.protocol("WM_DELETE_WINDOW", on_close) running = True penup() hideturtle() setposition(0, 150) write(f'Красный: 0 Синий: 0', font=("Arial", 20, "bold"), align="center") lst = [30, 60, 120, 150, 210, 240, 300, 330] class Sprint(Turtle): score = 0 def __init__(self, x, width, height, color_, shape="square"): super().__init__(shape) self.color(color_) self.x = x self.shapesize(width, height) def change_settings(self): self.penup() self.speed(0) self.setposition(self.x, 0) def move_up(self): x, y = self.pos() if y <= 150: self.setposition(x, y+20) def move_down(self): x, y = self.pos() if y >= -140: self.setposition(x, y-20) class Ball(Turtle): def __init__(self, shape="circle"): super().__init__(shape) def change_settings(self): self.penup() self.speed(0) self.shapesize(0.7) self.setheading(choice(lst)) def go(self, enemy1, enemy2): while running: self.forward(2.5) if self.ycor() >= 190: self.setheading(360 - self.heading()) elif self.ycor() <= -180: self.setheading(360 - self.heading()) if self.xcor() < -300: self.setposition(0, 0) self.setheading(choice(lst)) enemy2.score += 1 clear() write(f'Красный: {enemy1.score} Синий: {enemy2.score}', font=("Arial", 20, "bold"), align="center") elif self.xcor() > 300: self.setposition(0, 0) self.setheading(choice(lst)) enemy1.score += 1 clear() write(f'Красный: {enemy1.score} Синий: {enemy2.score}', font=("Arial", 20, "bold"), align="center") if -179 > self.xcor() > -181 and abs(self.ycor() - enemy1.ycor()) <= 50: self.setheading(180 - self.heading()) self.forward(2) if 179 < self.xcor() < 181 and abs(self.ycor() - enemy2.ycor()) <= 50: self.setheading(180 - self.heading()) self.forward(2) t1 = Sprint(-200, 4, 1, 'red') t1.change_settings() t2 = Sprint(200, 4, 1, 'blue') t2.change_settings() ball = Ball() ball.change_settings() scr.listen() onkey(t1.move_up, 'w') onkey(t1.move_down, 's') onkey(t2.move_up, 'Up') onkey(t2.move_down, 'Down') ball.go(t1, t2)
Заранее извиняюсь за возможные некорректные формулировки
ссылка на оригинал статьи https://habr.com/ru/articles/884982/
Добавить комментарий