Читать книгу MINI DOOM Современный Python через игру Том 1 Учимся держать оружие (Алексей Примак) онлайн бесплатно на Bookz
MINI DOOM Современный Python через игру Том 1 Учимся держать оружие
MINI DOOM Современный Python через игру Том 1 Учимся держать оружие
Оценить:

4

Полная версия:

MINI DOOM Современный Python через игру Том 1 Учимся держать оружие

MINI DOOM Современный Python через игру Том 1 Учимся держать оружие

Предисловие

Привет. Если ты держишь эту книгу, значит, тебе хочется не просто «выучить Python», а сделать что-то своё. Отлично, потому что именно этим мы и займёмся. За двадцать девять глав мы с тобой с нуля напишем игру в духе Doom: с коридорами, дверями, импами, которые швыряются огнём, зомби с винтовками, дробовиком и статус-баром внизу экрана. Без готовых движков, без скачанных картинок и звуков — всё своими руками, строчка за строчкой.




Рисунок 0.1 — Так выглядит MINI DOOM в конце книги: титульный экран, имп в проёме, дробовик в деле, второй уровень. Ни одной картинки из интернета — всё нарисовано кодом


Посмотри на рисунок 0.1. Каждый пиксель на нём — результат кода, который ты напишешь. Текстура кирпича, рожа импа, огонь на титульнике и даже звук выстрела — всё это генерируется программой при запуске. Звучит как магия, но к концу книги ты будешь знать, как работает каждый фокус.

Для кого эта книга

Для тех, кто ни разу не программировал или пробовал, но бросил на третьей странице скучного учебника. Я не буду требовать от тебя знаний математики сверх школьной, а ту, что понадобится, — синус, косинус, векторы, — объясню на пальцах прямо тогда, когда она понадобится, и ни минутой раньше.

Если ты уже немного знаешь Python, тоже не уходи. Первую часть можешь пролистать по диагонали, а со второй начнётся то, чего в обычных учебниках нет: своё окно, пиксели на numpy, псевдо-3D, искусственный интеллект монстров, синтез звука, тесты и профилировщики. Здесь Python не «язык для скриптов», а язык, на котором пишут настоящую игру — и разбираются, почему она тормозит и как её ускорить.

Книга маркирована 18+. Не потому, что тут кровь и кишки (хотя импы истекают пиксельной кровью), а потому что я разговариваю с тобой как со взрослым: иногда крепким словом, когда программа ведёт себя как последняя сволочь. Дозированно, как соль в супе.

Почему Doom и почему Python

Потому что Doom — идеальный учебный проект. Он достаточно простой, чтобы один человек мог написать его за книгу, и достаточно сложный, чтобы по дороге встретилось всё важное: списки и словари, классы и датаклассы, массивы numpy и алгоритмы, конечные автоматы, файлы и исключения, звук и тесты. А ещё потому, что стрелять по демонам веселее, чем считать среднюю зарплату сотрудников в отделе, как в половине учебников.

А Python — потому что на нём проще всего начать и дальше всего можно уйти. Говорят, что Python медленный и игры на нём не пишут. Отчасти правда: цикл по пикселям на чистом Python действительно в сотню раз медленнее, чем надо. Но мы научимся думать массивами и отдавать тяжёлую работу numpy, мерить время профилировщиком и находить, где тормозит на самом деле. Это самый ценный навык, который можно вынести из книги, — и он пригодится далеко за пределами игр: в анализе данных, машинном обучении, науке.



Как устроена книга

Книга разбита на пять частей. Каждая следующая опирается на предыдущую, поэтому читать лучше по порядку.




Таблица 0.1 — Пять частей книги


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

И главное правило: каждая глава заканчивается работающей программой. Не «допишем в следующей главе», а прямо сейчас: запустил — увидел результат. Я называю это контрольной точкой — как в игре, где можно сохраниться.

Как читать листинги

Код в книге выглядит как окно редактора. Сверху — имя файла и язык, слева — номера строк, на которые я ссылаюсь в тексте. Под кодом тёмная панель: это то, что программа напечатала, когда я её запустил. Вот пример — листинг 0.1.




Листинг 0.1 — Как выглядит листинг: код, номера строк и вывод программы


Первая зелёная строка в тёмной панели, «$ python3 main.py», — команда запуска. На Windows та же команда пишется python main.py, а проще всего — uv run main.py: об этом в главе 1. Строки, начинающиеся с решётки, — мои комментарии, программа их не печатала.

Весь вывод в книге настоящий. Каждый пример я запускал на Python 3.15 и проверял двумя строгими сторожами: линтером ruff с большим набором правил и проверкой типов mypy в строгом режиме. Если у тебя вывод отличается — скорее всего, отличается и код. Сверься с архивом примеров. Исключение — замеры времени: у тебя другой компьютер, и миллисекунды будут другими, а вот соотношения — такими же.

Если Python отказывается запускать программу, панель подписана «интерпретатор ругается». Такие листинги я показываю специально: читать трейсбеки — такой же навык, как писать код. Окна игры в книге сняты «роботом» — программой, которая нажимает клавиши и двигает мышь вместо человека, — поэтому каждый скриншот тоже настоящий и повторяемый. Как это устроено, ты узнаешь в главе 9.

Цветные врезки

По тексту разбросаны цветные врезки четырёх видов. Вот они все.












Что понадобится

— Компьютер с Windows, macOS или Linux. Игра работает везде, где есть Python.

— Python 3.15 и менеджер uv — как их поставить, расскажу в главе 1.

— Редактор Visual Studio Code с расширениями Python и Ruff. Можно и любой другой, но объяснять я буду на нём.

— Две библиотеки: numpy и pygame-ce. Их uv поставит одной командой в главе 9.

— Желание разобраться. Серьёзно, это важнее всего остального.

Мышь тоже пригодится: в Doom без мыши играть можно, но это как есть суп вилкой. И колонки — в главе 26 игра заговорит.

Архив примеров

Весь код из книги лежит на GitHub: https://github.com/RushSex/MiniDoom-Python. Скачать его можно одним архивом — зелёная кнопка Code, затем Download ZIP — или командой git clone; дальше в книге я называю его просто архивом примеров. В папке examples там по папке на каждую главу — от ch01 до ch29, — и в каждой папке программы в том виде, в каком они должны быть в конце главы. Готовая игра целиком — в папке minidoom: uv sync, uv run main.py — и вперёд.

Но не превращай архив в копипасту. Код, набранный своими пальцами, запоминается в десять раз лучше, а опечатки, которые ты при этом наделаешь, научат читать трейсбеки. Это не баг обучения, это его фича.

Поехали

Хватит предисловий. Ставим Python, пишем первую программу и заставляем компьютер сказать «Hello, Doom!». Переворачивай страницу.

Пять томов

Книга «MINI DOOM. Современный Python через игру» выходит в пяти томах — по одному на каждую часть. Каждый заканчивается работающей программой, и в каждом свои ответы, глоссарий и указатель.

— Том 1. Учимся держать оружие — главы 1–8: основы Python в консоли, от первой программы до ссылок и ресурсов.

— Том 2. Окно в ад — главы 9–13: окно pygame-ce, пиксели на numpy, игровой цикл, клавиатура и мышь, свой шрифт.

— Том 3. Третье измерение — главы 14–19: рейкастинг, стены, текстуры, пол и свет, профилировщик, двери.

— Том 4. Здесь кто-то есть — главы 20–25: спрайты, монстры, оружие, конечные автоматы, поиск пути, смерть.

— Том 5. Шум, свет и финальные титры — главы 26–29: звук, экраны, уровни в файлах, uv, ruff, mypy и pytest.

Все исходники серии — на GitHub: https://github.com/RushSex/MiniDoom-Python. Папка examples/chNN для каждой главы, готовая игра — папка minidoom. Там же README: uv sync, uv run main.py — и вперёд.

Часть I. Учимся держать оружие

Прежде чем стрелять по демонам, надо научиться держать оружие. В этой части мы освоим основы Python в самой простой обстановке — в окне терминала. Никакой графики, никаких окон, только ты, код и интерпретатор. Зато к концу части у тебя будет консольный рогалик: герой бегает по карте, монстры бродят вокруг, а код устроен так, как пишут настоящие программисты.

Глава 1. Hello, Doom

Любое путешествие начинается с первого шага, а любой программист — с программы, которая печатает «Hello, World!». Мы поступим так же, только поздороваемся не с миром, а с Doom. В этой главе ты поставишь Python и редактор, создашь первый проект, настроишь его как профи, нарисуешь титульный экран игры буквами и, что не менее важно, научишься не бояться, когда Python начинает ругаться.

Что такое программа и зачем нужен интерпретатор

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

Поэтому придумали языки программирования. Ты пишешь текст, понятный человеку, а специальная программа переводит его для машины. В языках вроде C++ переводчик — компилятор — переводит весь текст заранее и выдаёт готовый .exe. В Python всё устроено иначе: переводчик сидит рядом с поваром и читает ему рецепт вслух, строчка за строчкой, прямо во время готовки. Такой переводчик называется интерпретатор, и он тоже называется python.

Если заглянуть под капот, интерпретатор сначала быстро переводит твой файл в промежуточный байт-код — компактные команды для виртуальной машины Python — и потом выполняет их одну за другой. Весь путь показан на рисунке 1.1.




Рисунок 1.1 — Путь от текста программы до результата: перевод и выполнение идут сразу, при каждом запуске


У такого подхода есть плюс и минус, и оба нам важны. Плюс: написал строчку — сразу запустил, никакой долгой сборки. Минус: многие ошибки интерпретатор замечает, только когда до них дойдёт. Опечатка в пятой строке не помешает выполниться первым четырём, а программа упадёт посреди работы. В C++ такую ошибку поймал бы компилятор до запуска. Поэтому в Python мы позовём на помощь двух сторожей — линтер и проверку типов. Знакомство с ними — в этой же главе.



Ставим Python

Нам нужен свежий Python — версии 3.15, она вышла в октябре 2026 года. Подойдёт и любая более новая. Ставить его будем через uv — это современный менеджер Python от команды, которая сделала линтер Ruff. Он ставит сам Python нужной версии, инструменты для него и библиотеки, и делает это быстро. Старый способ — скачать установщик с python.org и не забыть галочку Add python.exe to PATH — тоже работает, но с uv меньше граблей.

— Открой PowerShell (на Windows) или терминал (на Linux и macOS).

— Поставь uv. На Windows: powershell -c «irm https://astral.sh/uv/install.ps1 | iex». На Linux и macOS: curl -LsSf https://astral.sh/uv/install.sh | sh. Точные команды всегда есть на сайте docs.astral.sh/uv.

— Закрой терминал и открой заново, чтобы он увидел uv. Потом поставь Python: uv python install 3.15.

— Поставь двух сторожей: uv tool install ruff и uv tool install mypy. О них — чуть ниже.

Проверить, что всё на месте, можно командами python --version, ruff --version и mypy --version. Каждая должна напечатать номер версии, а не «команда не найдена».






Редактор и первый проект

Писать код можно хоть в «Блокноте», но мы возьмём Visual Studio Code — бесплатный редактор от Microsoft, стандарт для Python. Поставь его с сайта code.visualstudio.com, открой, слева на панели найди «Расширения» и поставь два: Python от Microsoft и Ruff от Astral. Первое учит редактор понимать Python, второе подсвечивает подозрительные места прямо по ходу набора.

Теперь проект. Создай где-нибудь папку mini_doom, в VS Code выбери «Файл → Открыть папку» и открой её. Создай файл main.py — в Python это обычное имя для главного файла программы. Запускать программу будем из встроенного терминала (меню «Терминал → Создать терминал») командой python main.py. Полезные клавиши собраны в таблице 1.1.




Таблица 1.1 — Горячие клавиши VS Code, которые понадобятся сразу



Hello, Doom

Набери в main.py код из листинга 1.1. Именно набери руками, не копируй: пальцы тоже должны учиться.




Листинг 1.1 — Первая программа


Запусти. В терминале появится ровно то, что в тёмной панели под кодом: строка Hello, Doom!. Поздравляю, ты программист. Ну, почти. Одна строчка — и программа готова: ни заголовков, ни функции main, ни точки с запятой. За это Python и любят.

Разберём эту строку по частям. print — это функция, готовая команда, которая печатает в терминал то, что ей дали. Круглые скобки после имени означают «вызвать функцию». Внутри скобок — то, что ей передают, аргумент. Здесь это строка «Hello, Doom!»: текст в кавычках. Кавычки говорят Python, что это не команда, а просто буквы, которые надо оставить как есть. После печати print сам переводит курсор на новую строку.

Функции print можно передать сразу несколько значений через запятую — она напечатает их через пробел. А два особых именованных аргумента меняют поведение: sep задаёт, чем разделять значения, а end — чем закончить строку. Всё это в листинге 1.2.




Листинг 1.2 — Возможности print: несколько значений, sep и end


Строка 1. Четыре значения через запятую — две строки и два числа. Числа пишутся без кавычек, и Python понимает, что это числа. print напечатал всё через пробел.

Строка 2. sep=»» — разделитель пустой, поэтому HEALTH: и 100 слиплись.

Строки 3–5. end=»» отменяет перевод строки в конце. Три вызова print напечатали одну строку Loading… OK — так рисуют индикаторы загрузки.

Строка 6. print без аргументов печатает пустую строку.

Строка 7. «2 + 2 =» в кавычках — это текст, его напечатали как есть. А 2 + 2 без кавычек — выражение: Python его посчитал и напечатал 4. Как print раскладывает значения по строке, показано на рисунке 1.2.




Рисунок 1.2 — Функция print: значения по очереди уезжают в терминал, между ними — разделитель



Рисуем титульный экран

Строка Hello, Doom! — это мило, но у настоящей игры должен быть титульный экран. Нарисуем его буквами, как делали в восьмидесятых, — это называется ASCII-арт. Код в листинге 1.3.




Листинг 1.3 — Титульный экран MINI DOOM из символов


Строки 1 и 2 начинаются с решётки. Это комментарии: всё от # до конца строки Python пропускает. Комментарии пишут для людей — для коллег и для себя через полгода, когда забудешь, что тут происходит. А забудешь обязательно.

Строка 3. TITLE =… — первая в книге переменная: имя, к которому привязано значение. Подробно о переменных — в следующей главе, а пока считай, что мы положили рисунок в коробку с надписью TITLE и в строке 11 достали его оттуда, чтобы напечатать. Имя написано заглавными буквами — так в Python договорились обозначать константы, значения, которые не меняются.

Сам рисунок — это строка в тройных кавычках «»»…»»». Обычная строка в одинарных кавычках должна уместиться в одну строку кода, а тройная может тянуться сколько угодно и сохраняет все переносы. Обрати внимание на первую строку вывода: она пустая. Это не баг: строка начинается сразу после открывающих кавычек, а там стоит перевод строки — он тоже попал в вывод.

Самое хитрое — буква r перед кавычками. В обычной строке обратный слэш \ — особый символ: \n означает перевод строки, \t — табуляцию. А в нашем рисунке слэшей полно. Буква r делает строку «сырой» (raw): все символы в ней — просто символы. Что будет, если её забыть, видно в листинге 1.4: там рисунок без r, и мы проверили файл линтером Ruff.




Листинг 1.4 — Забыли r: Ruff находит подозрительные слэши


Ruff говорит: в строке 3, в столбце 4 стоит неправильная escape-последовательность \/ — такой особой комбинации в Python нет. Таких мест в рисунке два, второе он нашёл в столбце 14, мы его сократили. Ниже Ruff даже подсказывает лекарство: поставить r перед кавычками. Сам Python в таком случае выдаст предупреждение SyntaxWarning, но картинку напечатает. А если бы в рисунке оказалось сочетание \n, на его месте молча появился бы перенос строки, и рисунок разъехался бы без всяких предупреждений.



Когда Python ругается

Рано или поздно — скорее рано — ты запустишь программу, а вместо результата получишь простыню красного текста. Это нормально. Опытные программисты видят такое по сто раз на дню и не расстраиваются. Ошибки бывают трёх видов, и полезно понимать, кто именно ругается и когда.

Первый вид — синтаксическая ошибка. Ты написал то, что вообще не похоже на Python. Такие ошибки интерпретатор замечает сразу, на этапе перевода в байт-код, и не выполняет ни одной строчки. Классика — незакрытая скобка в листинге 1.5.




Листинг 1.5 — Забыли закрывающую скобку в строке 2


Сообщение выглядит пугающе, но устроено всегда одинаково. Разберём его по частям на рисунке 1.3.




Рисунок 1.3 — Анатомия сообщения об ошибке: файл, строка, тип и суть


Python говорит: в файле main.py, в строке 2, открытая скобка так и не закрылась, и галочкой ^ показывает, какая именно. Обрати внимание: строка 1 тоже не выполнилась, MINI DOOM не напечатался. Синтаксическая ошибка останавливает всё ещё до старта.




Второй вид — ошибка во время выполнения. Код написан грамматически правильно, но делает невозможное. Такие ошибки проявляются, только когда выполнение до них дойдёт. В листинге 1.6 вместо print написано pirnt.




Листинг 1.6 — Опечатка в имени функции


Видишь? Первые две строки отработали и напечатали своё, а на третьей программа упала. Это трейсбек (traceback) — отчёт о том, где и почему всё сломалось. Читать его надо снизу: последняя строка — суть. NameError: имени pirnt не существует. Python даже догадывается, что ты имел в виду print. Строкой выше — сама виноватая строка, а над ней — файл и номер строки. Четвёртая строка, Ready., так и не выполнилась.

Вот почему такие ошибки опасны. Представь, что опечатка не в третьей строке, а в коде, который срабатывает, только когда игрок подберёт секретную ракетницу на пятом уровне. Ты можешь сто раз запустить игру, и всё будет работать. А потом игрок дойдёт до ракетницы, и игра вылетит у него. Лучше бы поймать ошибку заранее. Для этого и нужен линтер — программа, которая читает код, не выполняя его, и ищет подозрительное. Мы поставили Ruff. Проверим им тот же файл в листинге 1.7.




Листинг 1.7 — Тот же файл глазами Ruff: ошибка найдена без запуска


Ruff нашёл ошибку, не запуская программу: F821 — неопределённое имя pirnt, строка 3, столбец 1. Буква с цифрами в начале — код правила, по нему удобно искать объяснения в документации Ruff.

Третий вид — ошибка типов. Python строго следит, что с чем можно делать. Число можно сложить с числом, строку — со строкой, но строку с числом — нельзя: непонятно, что получится. В листинге 1.8 мы пытаемся приклеить к надписи число патронов.




Листинг 1.8 — Строку нельзя сложить с числом


Снова трейсбек, и снова строка 2 успела выполниться. TypeError: склеивать можно только строку со строкой, а не с int, то есть целым числом. Волнистая линия под кодом показывает, какая операция не удалась, а галочка — где именно плюс.

Такие ошибки ловит второй сторож — mypy, программа проверки типов. Она тоже читает код без запуска, но смотрит не на опечатки, а на то, сходятся ли типы. Результат — в листинге 1.9.




Листинг 1.9 — Ошибку типов находит mypy


mypy пишет: строка 3, неподдерживаемые типы для + — str и int, то есть строка и целое число. Как правильно печатать числа вместе с текстом, разберём в следующей главе. Самый простой способ ты уже знаешь: print(«AMMO:», ammo) — через запятую, и print сам превратит число в текст.

bannerbanner