Как я 13 лет делал футбольный симулятор
История, которую я расскажу, началась 13 лет назад на уроке информатики. Мы с друзьями-семиклассниками решили все задачи на Паскале и весело играли в первый Quake. Наша учительница увидела это, подошла ко мне и сказала всего одну фразу, которая перекосила мою картину мира: «Если ты хочешь играть в игры на уроке, пиши их сам». С тех пор я эпизодически делаю игры. Одна из них — футбольный симулятор, о котором и пойдёт речь.
Это захватывающая история про мой путь разработчика, недоделанные проекты и о том, почему иногда их и не нужно доделывать.
Дисклеймер: с момента событий, описанных в этом тексте, я сделал курс по разработке на Unity и выложил несколько своих игр в Google Play и Windows Store. Сейчас я редактор блога Яндекс.Денег и рассказываю эту историю, чтобы поделиться опытом с сообществом.
В русскоязычном геймдеве тех времён была интересная особенность — MS DOS уже почти закончился и отечественные программисты активно пилили игрушки под Windows. В огромном количестве выходили симуляторы компьютерщика, танчики, разные карточные и «логические» игры. А еще массово продавались диски с этими играми. О том, как составлялись такие сборники, немного написано в «Хрониках тестировщика», но тогда я об этом ещё не знал.
Наверняка многие из вас помнят это прекрасное меню:
Жизнь, его, конечно потрепала
На одном из таких дисков я впервые встретил игру Coach — текстовый футбольный менеджер. Там можно было взять какую-нибудь команду из английской премьер-лиги и попробовать привести её к чемпионству. А еще эта игра распространялась по модели shareware: можно было сыграть два сезона, а дальше — платить. Конечно, платить никак не представлялось возможным, потому что для этого нужно было отправить банковский перевод создателю игры, а в 13 лет в банк не то что не пойдешь, о нём даже не думаешь. Яндекс.Деньги тогда уже существовали, но до появления Кассы оставалось 8 лет, поэтому тоже неудобно. Мы с друзьями играли по два сезона, а потом начинали заново.
Пришлось искать решение, и в процессе я написал одну из своих первых игр — Coach, с более интересной мне российской лигой, кубком и кучей багов. Название я менять не стал, только изменил двойку на тройку. Получилось так:
Coach 3 v2.52
Что нужно любой футбольной команде? Конечно, игроки. Естественно, ни о каких лицензиях на клубы и игроков тогда никто не знал — мы просто собрались с одноклассником и по памяти составляли списки команд. Поэтому, например, в ЦСКА и Спартаке всё-таки набралось по 11 человек (да, это был симулятор без замен), а вот Луч-Энергия из Владивостока остался с пустыми плашками вместо игроков.
Как сейчас помню: 2005 год, я вооружился учебником по информатике за 9 класс (там как раз было про мой любимый Visual Basic 6) и открыл редактор формы — слово IDE я узнал сильно позже. Перетянул на форму кнопку, кликнул и открыл редактор кода. Полный гордости за себя я впечатал
Private Sub Command1_Click()
End
End Sub
и нажал «Запустить». Всё сработало и очень порадовало меня. Через некоторое время появился текстовый футбольный симулятор — Coach v2.52.
Юристы из РПЛ, если вы это читаете — эта игра никогда не выходила в свет и не продавалась за деньги
Тогда мне казалось, что это жемчужина программного кода. Конечно, в тот момент я многого не знал и, например, турнирная таблица хранилась в шести массивах из Label прямо на форме. Никаких сохранений, никаких массивов переменных — только хардкор. Естественно, из-за этого возникали странные баги:
Все команды сыграли по 2 матча, но некоторые из них по 3, а другие по одному
Ещё можно было купить Роя Кина и Андрея Шевченко в Спартак. Не уверен, что такое было хоть в одном футбольном симуляторе того времени
Вообще, представьте, что вы резко забыли как использовать базы данных, записывать что-то в текстовые файлы и хранить данные в массивах. Единственное, что осталось — элементы управления на формах. Страшно? А как-то так и выглядели будни семиклассника, который решил, что умеет в геймдев.
У меня не осталось исходников этой версии, но я уверен, что там было что-то такое:
m = Form1.Label5(0).Caption
Form2.Label3(m).Caption = Form2.Label3(m).Caption + 3
О некоторых возможностях VB6 в учебнике не писали. Поэтому, например, когда я узнал о том, что Shape можно залить разными способами, сделал под это целое окно с выбором газона на клубном стадионе:
А вы газон подстричь смогли бы на флейте вэбэсточных труб?
Важная мораль #1 — изучайте возможности инструментов, которыми пользуетесь каждый день.
Я ведь был уверен, что однажды отправлю эту игру на какую-нибудь многоигровку — поэтому даже написал справку. Конечно, её тогда увидели два с половиной тестировщика. Но раз уж так вышло — пусть будет опубликована.
Coach 3 V2.08 — 2006 Edition
Здравствуйте! Спасибо что приобрели эту игру.
1.Установка
2.Системные требования
3.Тестовая платформа
4.Что новенького
5.Советы
6.Деинсталляция
7.Огромное спасибо…
1.Установка
Запустите файл Setup.exe и распакуйте файлы в папку с:\Program files\Football Manager 2005
А потом еще распакуйте файл setup2.exe
2.Системные требования
Никаких P4–2500.Все предельно просто.Итак,
Pentium 1–233 MHz (рекомендуется 300 и выше)
HDD 5MB
16 MB RAM (рекомендуется 32 и выше)
Video 16 MB
Windows 9x, Me, NT,2003, Longhorn. На XP работает только при наличии файла msvbvm60.dll в корневом каталоге с:\windows\system
Мышь.
3.Тестовая платформа
Celeron 1100 MHz
160 MB RAM
Video 16MB
Windows XP SP2
4.Что новенького.
Cделал много нового.
Например, теперь в чемпионате России участвуют 14(!) команд вместо 8.
Изменен диалог «Команда», появилось меню о тренере клуба.
Теперь игра ЗАВИСИТ от тактики!
Сильные соперники.
Впервые со времен Coach 1 вам нормально начаисляются очки.
Лига Чемпионов.Супер-Кубок России.
5.Советы.
Не изменяйте и не удаляйте файлы из папки с:\program files\football manager 2005\clubs\emblems
После установки скопируйте файл MSVBVM60.DLL в папку c:\windows\system
6.Деинсталляция
Деинсталляция не требуется.Просто сотрите папку с игрой.
7.Огромное спасибо…
Кушербаеву Данияру за бета-тестирование.
Карнееву Михаилу, Трофименко Михаилу за бета тестирование и ценные советы.
Сайту rfpl.org за форму команд Премьер-Лиги.
27.01.06
P.S Наши победят!
Если бы я знал, что я буду возвращаться к этому проекту в течение следующих 13 лет, то, конечно, более ответственно отнесся бы к сохранению всего, что с ним связано. Тем не менее, Coach 2.5 остался в истории и запускается даже сейчас, прямо из папки, которая так и называется:
E:\prod\dev\My_old_dev_projects\Dev\Старые игрули,но работают
Наверняка были и более ранние версии, но бэкапить мне тогда было некуда, и исходники до 2005 года вместе с их ранними версиями однажды потерялись при сбое жесткого диска объемом 2.3 гигабайта. ностальгически вздыхает А помните, когда чтобы поставить Blood 2, нужно было стереть Age of Empires 2?
Скриншот версии 1.62. Если бы Windows 10 1803 вышла в 2004, то у меня бы не осталось и этого экшешника
Важная мораль #2 — делайте бэкапы.
Ну и самое главное — геймплей. Случайные события, не зависящие ни от чего — вот, что захватывало внимание на долгие часы разработки и тестирования. А тестировали так — играли, пока что-нибудь смешно не отвалится.
Не спрашивайте, что такое очки атаки. Я не помню
Видите этот нервный розовый квадратик? Сейчас расскажу и о нём.
«Шинник» и визуальный симулятор футбола
Вместе с Coach на тех дисках распространялись еще две игры. Первая — «ФК Шинник» от Евгения Шарова — в ней было посредственное управление командой, но можно было смотреть за ходом матча. Судя по титрам, её тоже делали довольно долго — с 1996 по 2000. Второй игрой был досовский «Футбол 4×4» — там не было совершенно ничего впечатляющего, кроме бегающих человечков, но это так сильно меня очаровало, что я решил сделать такую же игру для себя.
Сначала из-за недостатка опыта появился розовый квадратик. Он двигался по полю, обозначая, где сейчас идёт игра. И, вроде, даже на что-то влиял. Этого было мало и я сделал второй футбольный проект в своей жизни — Football View.
Да-да, это замазанные баннеры. В прошлом десятилетии с этим было как-то попроще
Загадка — как двигать игроков по полю постоянно, когда нельзя на каждого повесить отдельный скрипт, который будет учитывать позицию, соседей и расстояние до мяча? Отгадка — использовать таймер и крутить игроков в цикле, случайно смещая их на ширину спрайта. Код прямиком из 2005, поэтому мне сейчас очень стыдно.
Private Sub Igroki_Timer()
k = k + 1
′ движения игроков
For i = 1 To 8
Randomize (x)
x = Int(Rnd * 4)
If x = 1 Then Image2(i).Top = Image2(i).Top — 360
If x = 2 Then Image2(i).Top = Image2(i).Top + 360
If x = 3 Then Image2(i).Left = Image2(i).Left + 360
If x = 4 Then Image2(i).Left = Image2(i).Left — 360
y = Int(Rnd * 4)
If y = 1 Then Image3(i).Top = Image3(i).Top — 360
If y = 2 Then Image3(i).Top = Image3(i).Top + 360
If y = 3 Then Image3(i).Left = Image3(i).Left — 360
If y = 4 Then Image3(i).Left = Image3(i).Left + 360
′ остановка и пас
If Image2(i).Top = Image1.Top And Image2(i).Left = Image1.Left Then Igroki.Enabled = False: pas_vpravo.Enabled = True
If Image3(i).Top = Image1.Top And Image3(i).Left = Image1.Left Then Igroki.Enabled = False: pas_vlevo.Enabled = True
’ уход за верхнюю линию и нижнюю линию
If Image2(i).Top <= 1080 Then Image2(i).Top = Image2(i).Top + 720
If Image3(i).Top <= 1080 Then Image3(i).Top = Image3(i).Top + 720
If Image2(i).Top >= 4320 Then Image2(i).Top = Image2(i).Top — 720
If Image3(i).Top >= 4320 Then Image3(i).Top = Image3(i).Top — 720
′ Уход игроков за левую линию и правую линию
If Image2(i).Left <= 480 Then Image2(i).Left = Image2(i).Left + 3720
If Image3(i).Left <= 480 Then Image3(i).Left = Image3(i).Left + 3720
If Image2(i).Left >= 6600 Then Image2(i).Left = Image2(i).Left — 3720
If Image3(i).Left >= 6600 Then Image3(i).Left = Image3(i).Left — 3720
Next i
End Sub
Важная мораль #3 — пишите понятные и подробные комментарии.
Ещё один таймер проверял, что матч закончился:
If Val(Label5.Caption) = "90" And Val(Label2.Caption) > Val(Label1.Caption) Then MsgBox ("Матч окончен.Победила команда" + Label3.Caption): End
А сейчас — самое страшное. Мяч двигался по полю аж четырьмя таймерами, которые так и назывались — pas_vverh, pas_vniz, pas_vpravo и pas_vlevo. У некоторых сейчас задергался глаз, другие срочно пошли перечитывать Страуструпа, третьи задают немой вопрос — «Но мы же видим, что на гифке мяч не двигается вверх и вниз». Конечно, ведь я не смог правильно скоординировать четыре таймера между собой.
Смотрю сейчас в код и понимаю, что там даже предусмотрено непрямое управление мячом, но оно не работает.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If (KeyCode = vbKeyLeft) Then pas_vlevo.Enabled = True
If (KeyCode = vbKeyRight) Then pas_vpravo.Enabled = True
If (KeyCode = vbKeyUp) Then Pas_verh.Enabled = True
If (KeyCode = vbKeyDown) Then Pas_vniz.Enabled = True
End Sub
Весь девятый и десятый класс мы с моим одноклассником были заняты написанием симулятора операционной системы, поэтому до футбола руки не доходили. «Да это же курам на смех», — подумал я, когда в 2009 году всё-таки откопал старый экзешник, — «Нужно всё переписать с нуля».
Как я рефакторил нерефакторируемое
Переписать с нуля — отличная мысль, когда проект небольшой, а в голове появилось с десяток способов сделать лучше и удобнее, чем было. Мне нужен был идеальный текстовый футбольный симулятор, и конечно же, всё снова пошло не так.
В 2009 с интернетом стало полегче, и я пошёл на сайт РФПЛ и стал думать, как забрать оттуда составы команд, которые хранились в таком виде:
Грепать я тогда не умел, парсить страницы — тоже, а про xml и csv знал очень обрывочно. Поэтому техническое решение было очень странным:
- Руками скопировать содержимое страницы и вставить его в Excel. Информация по каждому игроку занимала четыре строчки, а где-то рядом висела фотография. Их я не использовал и легко избавлялся.
- Все строки из экселя нужно было скопировать в текстовый файл parse.txt
- Был написан парсер, который читал файл построчно и раскидывал информацию об игроках по четырем спискам — по номинальной позиции на поле.
- Все эти списки сохранялись в текстовый файл определенного формата, который игра легко съедала после запуска.
В коде парсера есть несколько смешных мест, например такая закомментированная строчка. Не могу представить, почему и откуда она там взялась.
’ If Val(List1.List(I)) * 0 = 0 Then MsgBox (List1.List(I))
Иногда парсер захлебывался и файл с командой выглядел так:
gk
71 Бабурин
22 Бородин
81 Дронов
30 Жевнов
91 Зайцев
16 Малафеев
df
3 Алвеш Португалия
2 Анюков
83 Васильев
14 Губочан Словакия
96 Зуев
Это приводило к странным багам внутри самой игры.
В составе Зенита мог играть полузащитник Португалия с рейтингом 86.
В этой версии стало больше управления командой и меньше веселья. Тактика снова почти ни на что не влияла, но можно было покупать и продавать игроков, они травмировались и получали карточки. Хранение турнирной таблицы на форме ушло в прошлое, теперь всё лежало в памяти и работать с этим стало гораздо удобнее.
Оставалась проблема с отрисовкой на форме кучи цифр, но она решилась элегантно:
Так себе UX
Конечно, в коде всё еще встречались куски, на которые без слёз не взглянешь:
For i = 1 To 15
If tbl(i) = team2 Then i = i + 1
k = k + 1
If k = 1 Then tr = Int(Rnd * 5) + 2: xa = Int(Rnd * tr): ia = i
If k = 2 Then
xb = Int(Rnd * tr)
If xa > xb Then pts(ia) = pts(ia) + 3: gm(2, ia) = gm(2, ia) + 1: gm(4, i) = gm(4, i) + 1: gm(5, ia) = gm(5, ia) + xa: gm(5, i) = gm(5, i) + xb: gm(6, ia) = gm(6, ia) + xb: gm(6, i) = gm(6, i) + xa
If xa < xb Then pts(i) = pts(i) + 3: gm(2, i) = gm(2, i) + 1: gm(4, ia) = gm(4, ia) + 1: gm(5, ia) = gm(5, ia) + xa: gm(5, i) = gm(5, i) + xb: gm(6, ia) = gm(6, ia) + xb: gm(6, i) = gm(6, i) + xa
If xa = xb Then pts(i) = pts(i) + 1: pts(ia) = pts(ia) + 1: gm(3, i) = gm(3, i) + 1: gm(3, ia) = gm(3, ia) + 1: gm(5, ia) = gm(5, ia) + xa: gm(5, i) = gm(5, i) + xb: gm(6, ia) = gm(6, ia) + xb: gm(6, i) = gm(6, i) + xa
Form6.List1(ng).AddItem tbl(ia) + Str(xa) + «:» + Str(xb) + " " + tbl(i)
k = 0
End If
Эта игра была почти идеальной — можно было законно купить Игоря Акинфеева нападающим в Амкар, а потом посмотреть, как нижегородская Волга становится чемпионом России. Да и в целом, всё стало гораздо приличнее — ну, это я сначала так думал. Через четыре года всё оказалось не так радужно.
RFPL 2 — Resurrection
К 2013 году я уже успел поработать программистом за деньги. Я подумал и захотел написать новую версию симулятора — свежий взгляд на старый код показал, что там не было вообще ничего хорошего. Ну, то есть совсем ничего.
Важная мораль #4 — иногда лучше переписать с нуля.
Сайт РФПЛ к тому моменту изменился, поэтому я написал еще один парсер (его код канул в лету), но теперь досадных ошибок со страной уже не было. Для игроков я завёл отдельную структуру, где указывал, в том числе, клубную принадлежность — так стало проще реализовать трансферы и вести статистику. А ещё они все получали очки, в зависимости от «звездности» и позиции на поле.
Type player
Surname As String
FirstName As String
Num As String
Team As String
Goals As Integer
Shots As Integer
Saves As Integer
AttPts As Integer
DefPts As Integer
MidPts As Integer
GoalPts As Integer
Position As String
Star As Boolean
Stamina As Integer
Yellow As Integer
TeamPts As Integer
Injured As Integer
fromStart As Boolean
fromBench As Boolean
End Type
Появилась кнопка «Автосостав» для особо ленивых игроков — она сама заполняла позиции самыми сильными «футболистами». К сожалению, в это всё еще играл только я
С таблицей к тому моменту я тоже разобрался — не поленился и сделал понятную «классическую» таблицу с нужными показателями, которые видно сразу. Вместе с этим появилась статистика лучших бомбардиров.
Добавились сохранения, еще более хорошие трансферы, результаты параллельных матчей каждого тура. Сейчас я смотрю на это и понимаю, что всё это очень детское и наивное занятие, но тогда выглядело внушительно. Всё, кроме одной вещи. Смотрите:
Позорный. Мячик. Вместо. Розового. Квадратика.
Важная мораль #5 — иногда лучше не делать никак, чем сделать розовый квадратик.
Прошло еще несколько лет и я решил научить компьютер играть в футбол. Или хотя бы делать вид, что он играет в футбол. С мячиком, игроками на поле, без текста и списков. И тут место для еще одной важной морали, которая совпадает с заголовком раздела:
Если вы не сделаете бэкапы, то вам будет не о чем рассказывать в этом разделе
От большого проекта на Unity из 2016 года, в котором были хорошие модели движения агентов, позиционная игра и почти умное движение мяча, осталась маленькая гифка с одного из ранних этапов разработки.
Поэтому об этом расскажу как-нибудь в другой раз, когда снова перепишу всё с нуля.
О чём это всё?
Мне нравится вспоминать эту историю с самого начала. Нравится думать о том, что этот игрушечный «симулятор» принёс больше опыта и веселья, чем некоторые университетские занятия и рабочие задачи. Вспоминать о том, как регистрировал первую почту, на которую благодарные игроки будут присылать письма, и о том, как в 2007 году писал на городском форуме «Читать учебники в интернете это читерство, я буду всё изучать методом тыка». Это прекрасные ощущения, которые, наверное, в разном виде есть и у вас.
В этом посте было много очевидных советов — про бэкапы, комментарии и необходимость рефакторинга — все это знают и без меня. И самое важное, обязательно запомните мысль Любови Николаевны Кафтункиной, моей учительницы информатики:
«Если ты хочешь играть в игры на уроке, пиши их сам».