10+ · одно видео / шесть глав

От окна до игры за шесть задач

Сценарий часового практикума: показываем результат, собираем нужный блок кода и проверяем. Можно записать один ролик с главами или шесть отдельных выпусков. Отметки времени — план занятия, а не готовый монтаж.

Перед записью

Подготовьте Python, Arcade и распакованную папку проекта. Откройте game.py и контрольные файлы, увеличьте шрифт редактора. Установку вынесите в отдельный материал. Час рассчитан на подготовленную среду: служебные блоки можно вставлять, игровые правила разбираем при написании.

Покажите учебный finished.py: стрелки, метеор, два исхода, R. В нём нет стартового меню. После демонстрации вернитесь к пустому game.py. После каждого блока покажите реальный запуск, а самостоятельные модификации предложите выполнить на паузе или после ролика.

Блок 1 · 00:00–08:00 · 8 минут

Окно и космический фон

Урок, рабочий файл и скриншот →
Вводная реплика
«Начинаем с игрового окна. Сегодня собираем одну игру по шести задачам; после каждого блока сразу проверяем результат.»

На экране: Покажите готовую игру за 30 секунд. Затем создайте окно, загрузите PNG и подключите draw. Обратите внимание на последнюю строку arcade.run().

Текстура
Загруженное изображение, которое Arcade может нарисовать.
Обработчик
Функция, которую окно вызывает при событии: рисовании, обновлении или вводе.

1.1. Создай окно

Открой game.py в распакованной мастерской. Вставь этот стартовый блок. Python и Arcade должны быть установлены до начала занятия.

Python
import arcadeWIDTH = 1280HEIGHT = 800window = arcade.Window(WIDTH, HEIGHT, "Звезда и Метеор")
  1. WIDTH и HEIGHT задают размеры в пикселях. Заглавные имена обозначают настройки по соглашению.
  2. arcade.Window создаёт окно. Переменная window позволяет подключить к нему функции.

1.2. Загрузи фон

После создания окна загрузи background.png. Папка assets должна находиться рядом с game.py; запускаем из этой папки проекта.

Python
background = arcade.load_texture("assets/background.png")
  1. load_texture читает PNG. Эту операцию выполняем один раз, до начала игры.

1.3. Нарисуй кадр и запусти

Добавь функцию draw ниже загрузки. Подключения и arcade.run() всегда оставляй внизу файла.

Python
def draw():    window.clear()    arcade.draw_texture_rect(background, arcade.LBWH(0, 0, WIDTH, HEIGHT))window.on_draw = drawarcade.run()
  1. clear очищает предыдущий кадр; следующий вызов рисует фон.
  2. LBWH описывает прямоугольник: левый край, нижний край, ширина, высота. Фон занимает всё окно.
  3. window.on_draw = draw передаёт функцию окну без немедленного вызова. Цикл обновлений запускает arcade.run().

Вопрос перед проверкой

«Почему картинку загружаем один раз, а рисуем много раз?»

После паузы: Файл достаточно прочитать при подготовке. Каждый новый кадр должен заново показать изображение.

Покажите результат: В отдельном окне виден фон; в Shell нет ошибки загрузки файла.

Если не работает: FileNotFoundError: проверьте распаковку, assets/background.png и запуск из папки проекта. ModuleNotFoundError: открыт Python без Arcade.

Завершение блока: Проверьте фон и изменённый заголовок. Дальше добавляем персонажа.

Дополнительная практика: Измени заголовок окна и проверь, что он обновился после перезапуска. Эти изменения можно выполнить после основного часа.

Блок 2 · 08:00–15:00 · 7 минут

Добавляем космонавта

Урок, рабочий файл и скриншот →
Вводная реплика
«Картинка героя уже готова. Наша задача — загрузить её и указать, где рисовать.»

На экране: Добавьте текстуру и две координаты. Покажите разницу между LBWH для фона и XYWH для персонажа. Вынесите повторяемое рисование в функцию.

Координаты
x растёт вправо, y в Arcade растёт вверх. Начало — левый нижний угол.
XYWH
Прямоугольник по центру x, y и размерам width, height.

2.1. Загрузи PNG и задай позицию

Добавь x и y после настроек. После загрузки фона добавь текстуру hero.

Python
x = 210y = 235hero = arcade.load_texture("assets/astronaut.png")
  1. x и y задают центр рисунка; hero хранит текстуру персонажа.
  2. Прозрачные области PNG позволяют показать фон вокруг космонавта.

2.2. Сделай функцию рисования объекта

Перед draw опиши draw_image. Её же позже используем для звезды и метеора.

Python
def draw_image(texture, x, y, size):    arcade.draw_texture_rect(texture, arcade.XYWH(x, y, size, size))
  1. texture — изображение; x и y — его центр; size — размер квадратной области.
  2. Здесь обычная функция Python и прямой вызов Arcade. Все правила остаются в game.py.

2.3. Покажи героя поверх фона

Внутри draw после строки с фоном добавь вызов. Сохрани, закрой старое окно и запусти файл снова.

Python
draw_image(hero, x, y, 132)
  1. Внутри draw у вызова четыре пробела отступа.
  2. Порядок рисования важен: фон первым, персонаж после него.

Вопрос перед проверкой

«Что изменится при увеличении y на 100?»

После паузы: Персонаж поднимется на 100 пикселей; горизонтальная позиция останется прежней.

Покажите результат: Персонаж виден на фоне и меняет положение после изменения x или y.

Если не работает: Если персонаж исчез, проверьте вызов после фона, имя файла и попадание координат в окно.

Завершение блока: Попросите изменить положение одним присваиванием. Следующий блок свяжет координаты с клавишами.

Дополнительная практика: Передвинь героя выше и правее, меняя только начальные координаты. Затем верни x = 210, y = 235. Эти изменения можно выполнить после основного часа.

Блок 3 · 15:00–28:00 · 13 минут

Плавное управление

Урок, рабочий файл и скриншот →
Вводная реплика
«Разделяем ввод, движение и рисование. Клавиша задаёт желание игрока, обновление меняет координаты, кадр показывает новое место.»

На экране: Подключите события и сначала одну стрелку. Проверьте движение и остановку, затем добавьте остальные три направления и границы.

keys = set()
Множество нажатых клавиш без повторов: add добавляет, discard убирает, in проверяет наличие.
delta_time
Время между обновлениями в секундах. Скорость × время даёт пройденный путь.
global
Разрешает функции присваивать новые значения переменным уровня файла.

3.1. Запомни нажатия и отпускания

Под настройками создай SPEED = 270 и keys = set(). После draw добавь два обработчика.

Python
def key_pressed(key, modifiers):    keys.add(key)def key_released(key, modifiers):    keys.discard(key)
  1. Arcade передаёт key — код клавиши; modifiers содержит Shift, Ctrl и подобные признаки, здесь они не нужны.
  2. При удержании клавиша остаётся в keys, при отпускании удаляется. Множество не создаёт дубликатов.

3.2. Обновляй координаты

Добавь update перед строками подключения. Скорость измеряется в пикселях в секунду.

Python
def update(delta_time):    global x, y    if arcade.key.RIGHT in keys:        x += SPEED * delta_time    if arcade.key.LEFT in keys:        x -= SPEED * delta_time    if arcade.key.UP in keys:        y += SPEED * delta_time    if arcade.key.DOWN in keys:        y -= SPEED * delta_time
  1. += прибавляет к текущему значению, -= вычитает. По x двигаемся вправо/влево, по y вверх/вниз.
  2. Независимые if позволяют держать два направления. В этой базовой версии диагональ быстрее прямого движения.
  3. keys.add меняет существующее множество; global нужен для присваиваний x и y.

3.3. Ограничь поле

В конце update, после движения, добавь ограничения для центра героя.

Python
x = max(96, min(1184, x))y = max(171, min(589, y))
  1. min ограничивает сверху, max снизу. Внутри update эти строки имеют отступ четыре пробела.

3.4. Подключи события

Перед arcade.run() добавь подключения. on_draw уже подключён в первом уроке.

Python
window.on_key_press = key_pressedwindow.on_key_release = key_releasedwindow.on_update = updatewindow.on_deactivate = keys.clear
  1. Обработчик ввода меняет набор клавиш, update рассчитывает координаты, draw показывает результат.
  2. При уходе в другое окно keys.clear сбрасывает клавиши, чтобы персонаж не продолжал движение сам.

Вопрос перед проверкой

«Почему движение умножается на delta_time, а не просто на один шаг за кадр?»

После паузы: Так скорость привязана к секундам, а не к количеству обновлений на конкретном компьютере.

Покажите результат: Удержание даёт плавное движение; отпускание останавливает; выйти за поле нельзя.

Если не работает: Нет остановки — проверьте key_released и его подключение. UnboundLocalError — проверьте global. Не ставьте while True вокруг arcade.run().

Завершение блока: Проверьте все направления и отпускание. Теперь добавим цель и движущуюся опасность.

Дополнительная практика: Подбери другую SPEED и проверь четыре края. Дополнительно добавь D для движения вправо через условие or. Эти изменения можно выполнить после основного часа.

Блок 4 · 28:00–38:00 · 10 минут

Цель и движущееся препятствие

Урок, рабочий файл и скриншот →
Вводная реплика
«Используем прежний код рисования для двух новых объектов. Отличие метеора — собственная скорость и правило разворота.»

На экране: Добавьте две текстуры и вызовы draw_image. После проверки сцены включите патруль и дождитесь обоих разворотов.

Знак скорости
Положительная скорость ведёт вправо, отрицательная — влево.

4.1. Создай второй и третий объекты

После координат героя добавь координаты цели и препятствия; после hero загрузи ещё две текстуры.

Python
star_x, star_y = 1070, 540meteor_x, meteor_y = 720, 385meteor_speed = 200star = arcade.load_texture("assets/star.png")meteor = arcade.load_texture("assets/meteor.png")
  1. Запись a, b = 1, 2 присваивает двум именам два значения.
  2. У каждого объекта отдельные координаты. Высота метеора постоянна, меняется только meteor_x.

4.2. Нарисуй оба объекта

Внутри draw после космонавта добавь два вызова уже написанной функции.

Python
draw_image(star, star_x, star_y, 90)draw_image(meteor, meteor_x, meteor_y, 132)
  1. Меняются текстура, координаты и размер. Новая функция рисования для каждого PNG не требуется.

4.3. Запусти патруль

В update добавь global meteor_x, meteor_speed. В конце функции добавь движение и два разворота.

Python
meteor_x += meteor_speed * delta_timeif meteor_x > 1110:    meteor_x = 1110    meteor_speed = -200elif meteor_x < 340:    meteor_x = 340    meteor_speed = 200
  1. Справа скорость становится отрицательной, слева положительной.
  2. Позицию возвращаем точно на границу, чтобы объект не накапливал выход за неё.
  3. Внутри update весь этот блок сдвинут на четыре пробела.

Вопрос перед проверкой

«Что будет, если у правой границы оставить положительную скорость?»

После паузы: Метеор не полетит обратно влево; будет упираться в правую границу.

Покажите результат: На экране три объекта; метеор разворачивается у обеих границ; звезда остаётся на месте.

Если не работает: Пока касания ничего не делают — проверка будет в следующем блоке. Если метеор не движется, проверьте global и расположение кода внутри update.

Завершение блока: Убедитесь, что сценой можно управлять и обходить метеор. Добавляем последствия касаний.

Дополнительная практика: Подбери скорость патруля. Меняй начальное значение и оба значения на разворотах согласованно. Эти изменения можно выполнить после основного часа.

Блок 5 · 38:00–50:00 · 12 минут

Столкновения и результат

Урок, рабочий файл и скриншот →
Вводная реплика
«Графическая сцена превращается в игру, когда появляются цель, опасность и конечный результат.»

На экране: Разберите touches на двух парах координат. Добавьте состояние и ранний выход; отдельно покажите проигрыш и победу.

state
Переменная с выбранными нами строками playing, won или lost.
abs() и and
abs берёт величину разницы без знака. and требует выполнения обоих условий.
Ранний return
Завершает текущий вызов функции до остальных вычислений.

5.1. Добавь состояние и подпись

Под координатами задай state. После создания окна и загрузки текстур создай надпись message.

Python
state = "playing"message = arcade.Text("", 640, 710, (255, 226, 136), 30,                      anchor_x="center", font_name="Arial")
  1. Строки playing, won и lost — наши значения состояния, не команды языка.
  2. Text создаёт надпись. Аргументы задают текст, позицию, цвет RGB и размер шрифта. anchor_x центрирует по x.

5.2. Опиши проверку близости

Перед draw добавь функцию, которая подходит и для звезды, и для метеора.

Python
def touches(ax, ay, bx, by):    return abs(ax - bx) < 60 and abs(ay - by) < 60
  1. Сравниваем разницу по двум осям. Если обе меньше 60 пикселей, считаем объекты соприкоснувшимися.
  2. Это простая прямоугольная область вокруг центров, не контур PNG.

5.3. Выбери исход и останови правила

В update добавь global state. Сразу после global поставь ранний выход, а после движения и патруля — проверки исхода.

Python
if state != "playing":    return# После движения и патруля:if touches(x, y, meteor_x, meteor_y):    state = "lost"elif touches(x, y, star_x, star_y):    state = "won"
  1. После победы или проигрыша update сразу завершится. draw продолжит показывать кадр.
  2. Опасность проверяем первой: цепочка if/elif выбирает один исход.

5.4. Покажи результат

В конце draw выбери текст и нарисуй подготовленную надпись.

Python
message.text = ""if state == "won":    message.text = "Звезда найдена!"elif state == "lost":    message.text = "Столкновение."message.draw()
  1. Меняем свойство text у уже созданной надписи; draw() показывает её на текущем кадре.
  2. Для проверки двух исходов перезапусти файл. В следующем блоке добавим новую попытку клавишей R.

Вопрос перед проверкой

«Почему return стоит в update, а не в draw?»

После паузы: Нужно остановить изменение мира, сохранив рисование результата.

Покажите результат: Касание метеора даёт проигрыш, звезды — победу; после исхода герой и метеор стоят.

Если не работает: Если исход меняется повторно, ранний return стоит ниже проверок. Если надпись не видна, проверьте message.draw() и порядок рисования.

Завершение блока: У игры есть оба исхода. Осталось дать возможность начать новый раунд без перезапуска файла.

Дополнительная практика: Проверь победу и проигрыш отдельно. Измени порог близости и оцени, как меняется сложность. Эти изменения можно выполнить после основного часа.

Блок 6 · 50:00–60:00 · 10 минут

Перезапуск и проверка игры

Урок, рабочий файл и скриншот →
Вводная реплика
«Последняя задача — замкнуть цикл: начало, движение, результат, новый раунд. После этого получаем завершённую мини-игру.»

На экране: Добавьте restart и R. Пройдите весь проверочный маршрут, затем попросите ученика изменить старт или скорость и объяснить, где ещё нужно поправить значения.

Сброс состояния
Возврат всех изменявшихся значений к началу раунда.

6.1. Собери сброс в функцию

Добавь restart перед обработчиком клавиш. Возвращаем старт героя, положение и направление метеора, состояние и набор клавиш.

Python
def restart():    global x, y, meteor_x, meteor_speed, state    x, y = 210, 235    meteor_x = 720    meteor_speed = 200    state = "playing"    keys.clear()
  1. global перечисляет только переменные, которым присваиваем новые значения.
  2. star_x, star_y и meteor_y не меняются в раунде, поэтому сбрасывать их не требуется.
  3. keys.clear() убирает ввод от предыдущей попытки.

6.2. Подключи R

Замени прежний key_pressed на этот блок. Остальные подключения окна сохраняются.

Python
def key_pressed(key, modifiers):    keys.add(key)    if key == arcade.key.R:        restart()
  1. R вызывает сброс в ходе игры, после победы и после проигрыша.
  2. Правила движения повторно не пишем: состояние playing снова разрешает update.

6.3. Проверь цикл целиком

Запусти игру: двигайся, отпусти клавишу, проверь края, столкнись, нажми R, найди звезду, снова нажми R. Затем сверяй свой файл с полным итогом.

  1. После каждого R начальные координаты и направление должны совпадать.
  2. Если изменил старт героя или скорость патруля для своей версии, обнови и начальные значения, и restart.

Вопрос перед проверкой

«Почему недостаточно вернуть только координаты героя?»

После паузы: Метеор, его направление, состояние результата и старый ввод тоже влияют на новый раунд.

Покажите результат: Оба исхода проверены; R работает после каждого; старая нажатая клавиша не переносится в новую попытку.

Если не работает: Если новая попытка сразу завершена, забыли playing. Если герой летит сам, проверьте keys.clear(). Если меняли старт, сверьте значения в двух местах.

Завершение блока: Покажите итог и перечислите шесть написанных блоков. Домашнее задание — один собственный маршрут или одна новая механика.

Дополнительная практика: Сделай свой маршрут и объясни три изменения. Дополнительно добавь WASD, таймер или паузу после основной проверки. Эти изменения можно выполнить после основного часа.