От окна до игры за шесть задач
Сценарий часового практикума: показываем результат, собираем нужный блок кода и проверяем. Можно записать один ролик с главами или шесть отдельных выпусков. Отметки времени — план занятия, а не готовый монтаж.
Перед записью
Подготовьте Python, Arcade и распакованную папку проекта. Откройте game.py и контрольные файлы, увеличьте шрифт редактора. Установку вынесите в отдельный материал. Час рассчитан на подготовленную среду: служебные блоки можно вставлять, игровые правила разбираем при написании.
Покажите учебный finished.py: стрелки, метеор, два исхода, R. В нём нет стартового меню. После демонстрации вернитесь к пустому game.py. После каждого блока покажите реальный запуск, а самостоятельные модификации предложите выполнить на паузе или после ролика.
Окно и космический фон
Урок, рабочий файл и скриншот →«Начинаем с игрового окна. Сегодня собираем одну игру по шести задачам; после каждого блока сразу проверяем результат.»
На экране: Покажите готовую игру за 30 секунд. Затем создайте окно, загрузите PNG и подключите draw. Обратите внимание на последнюю строку arcade.run().
- Текстура
- Загруженное изображение, которое Arcade может нарисовать.
- Обработчик
- Функция, которую окно вызывает при событии: рисовании, обновлении или вводе.
1.1. Создай окно
Открой game.py в распакованной мастерской. Вставь этот стартовый блок. Python и Arcade должны быть установлены до начала занятия.
import arcadeWIDTH = 1280HEIGHT = 800window = arcade.Window(WIDTH, HEIGHT, "Звезда и Метеор")- WIDTH и HEIGHT задают размеры в пикселях. Заглавные имена обозначают настройки по соглашению.
- arcade.Window создаёт окно. Переменная window позволяет подключить к нему функции.
1.2. Загрузи фон
После создания окна загрузи background.png. Папка assets должна находиться рядом с game.py; запускаем из этой папки проекта.
background = arcade.load_texture("assets/background.png")- load_texture читает PNG. Эту операцию выполняем один раз, до начала игры.
1.3. Нарисуй кадр и запусти
Добавь функцию draw ниже загрузки. Подключения и arcade.run() всегда оставляй внизу файла.
def draw(): window.clear() arcade.draw_texture_rect(background, arcade.LBWH(0, 0, WIDTH, HEIGHT))window.on_draw = drawarcade.run()- clear очищает предыдущий кадр; следующий вызов рисует фон.
- LBWH описывает прямоугольник: левый край, нижний край, ширина, высота. Фон занимает всё окно.
- window.on_draw = draw передаёт функцию окну без немедленного вызова. Цикл обновлений запускает arcade.run().
Вопрос перед проверкой
«Почему картинку загружаем один раз, а рисуем много раз?»
После паузы: Файл достаточно прочитать при подготовке. Каждый новый кадр должен заново показать изображение.
Покажите результат: В отдельном окне виден фон; в Shell нет ошибки загрузки файла.
Если не работает: FileNotFoundError: проверьте распаковку, assets/background.png и запуск из папки проекта. ModuleNotFoundError: открыт Python без Arcade.
Завершение блока: Проверьте фон и изменённый заголовок. Дальше добавляем персонажа.
Дополнительная практика: Измени заголовок окна и проверь, что он обновился после перезапуска. Эти изменения можно выполнить после основного часа.
Добавляем космонавта
Урок, рабочий файл и скриншот →«Картинка героя уже готова. Наша задача — загрузить её и указать, где рисовать.»
На экране: Добавьте текстуру и две координаты. Покажите разницу между LBWH для фона и XYWH для персонажа. Вынесите повторяемое рисование в функцию.
- Координаты
- x растёт вправо, y в Arcade растёт вверх. Начало — левый нижний угол.
- XYWH
- Прямоугольник по центру x, y и размерам width, height.
2.1. Загрузи PNG и задай позицию
Добавь x и y после настроек. После загрузки фона добавь текстуру hero.
x = 210y = 235hero = arcade.load_texture("assets/astronaut.png")- x и y задают центр рисунка; hero хранит текстуру персонажа.
- Прозрачные области PNG позволяют показать фон вокруг космонавта.
2.2. Сделай функцию рисования объекта
Перед draw опиши draw_image. Её же позже используем для звезды и метеора.
def draw_image(texture, x, y, size): arcade.draw_texture_rect(texture, arcade.XYWH(x, y, size, size))- texture — изображение; x и y — его центр; size — размер квадратной области.
- Здесь обычная функция Python и прямой вызов Arcade. Все правила остаются в game.py.
2.3. Покажи героя поверх фона
Внутри draw после строки с фоном добавь вызов. Сохрани, закрой старое окно и запусти файл снова.
draw_image(hero, x, y, 132)- Внутри draw у вызова четыре пробела отступа.
- Порядок рисования важен: фон первым, персонаж после него.
Вопрос перед проверкой
«Что изменится при увеличении y на 100?»
После паузы: Персонаж поднимется на 100 пикселей; горизонтальная позиция останется прежней.
Покажите результат: Персонаж виден на фоне и меняет положение после изменения x или y.
Если не работает: Если персонаж исчез, проверьте вызов после фона, имя файла и попадание координат в окно.
Завершение блока: Попросите изменить положение одним присваиванием. Следующий блок свяжет координаты с клавишами.
Дополнительная практика: Передвинь героя выше и правее, меняя только начальные координаты. Затем верни x = 210, y = 235. Эти изменения можно выполнить после основного часа.
Плавное управление
Урок, рабочий файл и скриншот →«Разделяем ввод, движение и рисование. Клавиша задаёт желание игрока, обновление меняет координаты, кадр показывает новое место.»
На экране: Подключите события и сначала одну стрелку. Проверьте движение и остановку, затем добавьте остальные три направления и границы.
- keys = set()
- Множество нажатых клавиш без повторов: add добавляет, discard убирает, in проверяет наличие.
- delta_time
- Время между обновлениями в секундах. Скорость × время даёт пройденный путь.
- global
- Разрешает функции присваивать новые значения переменным уровня файла.
3.1. Запомни нажатия и отпускания
Под настройками создай SPEED = 270 и keys = set(). После draw добавь два обработчика.
def key_pressed(key, modifiers): keys.add(key)def key_released(key, modifiers): keys.discard(key)- Arcade передаёт key — код клавиши; modifiers содержит Shift, Ctrl и подобные признаки, здесь они не нужны.
- При удержании клавиша остаётся в keys, при отпускании удаляется. Множество не создаёт дубликатов.
3.2. Обновляй координаты
Добавь update перед строками подключения. Скорость измеряется в пикселях в секунду.
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- += прибавляет к текущему значению, -= вычитает. По x двигаемся вправо/влево, по y вверх/вниз.
- Независимые if позволяют держать два направления. В этой базовой версии диагональ быстрее прямого движения.
- keys.add меняет существующее множество; global нужен для присваиваний x и y.
3.3. Ограничь поле
В конце update, после движения, добавь ограничения для центра героя.
x = max(96, min(1184, x))y = max(171, min(589, y))- min ограничивает сверху, max снизу. Внутри update эти строки имеют отступ четыре пробела.
3.4. Подключи события
Перед arcade.run() добавь подключения. on_draw уже подключён в первом уроке.
window.on_key_press = key_pressedwindow.on_key_release = key_releasedwindow.on_update = updatewindow.on_deactivate = keys.clear- Обработчик ввода меняет набор клавиш, update рассчитывает координаты, draw показывает результат.
- При уходе в другое окно keys.clear сбрасывает клавиши, чтобы персонаж не продолжал движение сам.
Вопрос перед проверкой
«Почему движение умножается на delta_time, а не просто на один шаг за кадр?»
После паузы: Так скорость привязана к секундам, а не к количеству обновлений на конкретном компьютере.
Покажите результат: Удержание даёт плавное движение; отпускание останавливает; выйти за поле нельзя.
Если не работает: Нет остановки — проверьте key_released и его подключение. UnboundLocalError — проверьте global. Не ставьте while True вокруг arcade.run().
Завершение блока: Проверьте все направления и отпускание. Теперь добавим цель и движущуюся опасность.
Дополнительная практика: Подбери другую SPEED и проверь четыре края. Дополнительно добавь D для движения вправо через условие or. Эти изменения можно выполнить после основного часа.
Цель и движущееся препятствие
Урок, рабочий файл и скриншот →«Используем прежний код рисования для двух новых объектов. Отличие метеора — собственная скорость и правило разворота.»
На экране: Добавьте две текстуры и вызовы draw_image. После проверки сцены включите патруль и дождитесь обоих разворотов.
- Знак скорости
- Положительная скорость ведёт вправо, отрицательная — влево.
4.1. Создай второй и третий объекты
После координат героя добавь координаты цели и препятствия; после hero загрузи ещё две текстуры.
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")- Запись a, b = 1, 2 присваивает двум именам два значения.
- У каждого объекта отдельные координаты. Высота метеора постоянна, меняется только meteor_x.
4.2. Нарисуй оба объекта
Внутри draw после космонавта добавь два вызова уже написанной функции.
draw_image(star, star_x, star_y, 90)draw_image(meteor, meteor_x, meteor_y, 132)- Меняются текстура, координаты и размер. Новая функция рисования для каждого PNG не требуется.
4.3. Запусти патруль
В update добавь global meteor_x, meteor_speed. В конце функции добавь движение и два разворота.
meteor_x += meteor_speed * delta_timeif meteor_x > 1110: meteor_x = 1110 meteor_speed = -200elif meteor_x < 340: meteor_x = 340 meteor_speed = 200- Справа скорость становится отрицательной, слева положительной.
- Позицию возвращаем точно на границу, чтобы объект не накапливал выход за неё.
- Внутри update весь этот блок сдвинут на четыре пробела.
Вопрос перед проверкой
«Что будет, если у правой границы оставить положительную скорость?»
После паузы: Метеор не полетит обратно влево; будет упираться в правую границу.
Покажите результат: На экране три объекта; метеор разворачивается у обеих границ; звезда остаётся на месте.
Если не работает: Пока касания ничего не делают — проверка будет в следующем блоке. Если метеор не движется, проверьте global и расположение кода внутри update.
Завершение блока: Убедитесь, что сценой можно управлять и обходить метеор. Добавляем последствия касаний.
Дополнительная практика: Подбери скорость патруля. Меняй начальное значение и оба значения на разворотах согласованно. Эти изменения можно выполнить после основного часа.
Столкновения и результат
Урок, рабочий файл и скриншот →«Графическая сцена превращается в игру, когда появляются цель, опасность и конечный результат.»
На экране: Разберите touches на двух парах координат. Добавьте состояние и ранний выход; отдельно покажите проигрыш и победу.
- state
- Переменная с выбранными нами строками playing, won или lost.
- abs() и and
- abs берёт величину разницы без знака. and требует выполнения обоих условий.
- Ранний return
- Завершает текущий вызов функции до остальных вычислений.
5.1. Добавь состояние и подпись
Под координатами задай state. После создания окна и загрузки текстур создай надпись message.
state = "playing"message = arcade.Text("", 640, 710, (255, 226, 136), 30, anchor_x="center", font_name="Arial")- Строки playing, won и lost — наши значения состояния, не команды языка.
- Text создаёт надпись. Аргументы задают текст, позицию, цвет RGB и размер шрифта. anchor_x центрирует по x.
5.2. Опиши проверку близости
Перед draw добавь функцию, которая подходит и для звезды, и для метеора.
def touches(ax, ay, bx, by): return abs(ax - bx) < 60 and abs(ay - by) < 60- Сравниваем разницу по двум осям. Если обе меньше 60 пикселей, считаем объекты соприкоснувшимися.
- Это простая прямоугольная область вокруг центров, не контур PNG.
5.3. Выбери исход и останови правила
В update добавь global state. Сразу после global поставь ранний выход, а после движения и патруля — проверки исхода.
if state != "playing": return# После движения и патруля:if touches(x, y, meteor_x, meteor_y): state = "lost"elif touches(x, y, star_x, star_y): state = "won"- После победы или проигрыша update сразу завершится. draw продолжит показывать кадр.
- Опасность проверяем первой: цепочка if/elif выбирает один исход.
5.4. Покажи результат
В конце draw выбери текст и нарисуй подготовленную надпись.
message.text = ""if state == "won": message.text = "Звезда найдена!"elif state == "lost": message.text = "Столкновение."message.draw()- Меняем свойство text у уже созданной надписи; draw() показывает её на текущем кадре.
- Для проверки двух исходов перезапусти файл. В следующем блоке добавим новую попытку клавишей R.
Вопрос перед проверкой
«Почему return стоит в update, а не в draw?»
После паузы: Нужно остановить изменение мира, сохранив рисование результата.
Покажите результат: Касание метеора даёт проигрыш, звезды — победу; после исхода герой и метеор стоят.
Если не работает: Если исход меняется повторно, ранний return стоит ниже проверок. Если надпись не видна, проверьте message.draw() и порядок рисования.
Завершение блока: У игры есть оба исхода. Осталось дать возможность начать новый раунд без перезапуска файла.
Дополнительная практика: Проверь победу и проигрыш отдельно. Измени порог близости и оцени, как меняется сложность. Эти изменения можно выполнить после основного часа.
Перезапуск и проверка игры
Урок, рабочий файл и скриншот →«Последняя задача — замкнуть цикл: начало, движение, результат, новый раунд. После этого получаем завершённую мини-игру.»
На экране: Добавьте restart и R. Пройдите весь проверочный маршрут, затем попросите ученика изменить старт или скорость и объяснить, где ещё нужно поправить значения.
- Сброс состояния
- Возврат всех изменявшихся значений к началу раунда.
6.1. Собери сброс в функцию
Добавь restart перед обработчиком клавиш. Возвращаем старт героя, положение и направление метеора, состояние и набор клавиш.
def restart(): global x, y, meteor_x, meteor_speed, state x, y = 210, 235 meteor_x = 720 meteor_speed = 200 state = "playing" keys.clear()- global перечисляет только переменные, которым присваиваем новые значения.
- star_x, star_y и meteor_y не меняются в раунде, поэтому сбрасывать их не требуется.
- keys.clear() убирает ввод от предыдущей попытки.
6.2. Подключи R
Замени прежний key_pressed на этот блок. Остальные подключения окна сохраняются.
def key_pressed(key, modifiers): keys.add(key) if key == arcade.key.R: restart()- R вызывает сброс в ходе игры, после победы и после проигрыша.
- Правила движения повторно не пишем: состояние playing снова разрешает update.
6.3. Проверь цикл целиком
Запусти игру: двигайся, отпусти клавишу, проверь края, столкнись, нажми R, найди звезду, снова нажми R. Затем сверяй свой файл с полным итогом.
- После каждого R начальные координаты и направление должны совпадать.
- Если изменил старт героя или скорость патруля для своей версии, обнови и начальные значения, и restart.
Вопрос перед проверкой
«Почему недостаточно вернуть только координаты героя?»
После паузы: Метеор, его направление, состояние результата и старый ввод тоже влияют на новый раунд.
Покажите результат: Оба исхода проверены; R работает после каждого; старая нажатая клавиша не переносится в новую попытку.
Если не работает: Если новая попытка сразу завершена, забыли playing. Если герой летит сам, проверьте keys.clear(). Если меняли старт, сверьте значения в двух местах.
Завершение блока: Покажите итог и перечислите шесть написанных блоков. Домашнее задание — один собственный маршрут или одна новая механика.
Дополнительная практика: Сделай свой маршрут и объясни три изменения. Дополнительно добавь WASD, таймер или паузу после основной проверки. Эти изменения можно выполнить после основного часа.
