🤏 Оптимизируем игру до 2 КИЛОБАЙТ

Всем привет! Как вы возможно знаете я люблю всякого рода челленджи, мой первый видос на канале почти два года назад был про то как я делал игру за 10 минут без всякого монтажа, а видос про факторию где тоже стояли жесткие ограничения вообще наверное один из моих самых любимых Так что я давно хотел сделать челлендж на создание игры, которая будет занимать очень мало места на жёстком диске Но просто брать какое-то ограничение, например в 64k было как-то слишком просто Да и до демо сцены мне очень далеко с моими навыками C# и Юнити Но тут я наткнулся на видео чувака где он делал игру чтобы она влезла в qr код, и я решил повторить что-то похожее Конечно в процессе я раскопал кучу всего нового и интересного про qr коды и совсем не только про них, постараюсь этим всем с вами поделиться в этом видео, так что не переключайтесь Если вы вдруг хотите перейти сразу к результату то в описании будут все тайм коды Как всегда спасибо за это видео моим верным подписчикам на патреон Итак, к qr кодам: как же вообще можно впихнуть в него игру? Конечно тут речь идёт не о том чтобы выложить в интернет игру и вставить в qr код ссылку Привычный нам qr код может содержать в себе любой текст, как написано в википедии, или вообще любые бинарные данные То есть проще говоря любой файл, размером до примерно 2,953 байт А это почти три килобайта, то есть просто огромный простор! Самый большой вариант qr кода, если мы захотим использовать стандарт на максимум, выглядит как-то вот так, и вполне себе сканируется моим телефоном Там кстати есть ещё некоторые хитрые режимы, и можно даже разделить данные на несколько Qr кодов, правда вряд ли хоть один ридер это поддерживает Но так глубоко мы вдаваться не будем – а поставим цель так сделать запускаемый файл игры, который занимает меньше 2.8кб Юнити нам конечно тут не подходит однозначно, она конечно в умелых руках совсем не жирная и не тормозная, но уж точно не на 3 кб Так что нужно выбирать другую технологию Тут я вспомнил про такую вещь как data url: это такой стандарт url’ов, которые никуда не ведут, а прямо в себе содержат html код страницы То есть можно просто скопировать строчку в адресную строку, и откроется закодированная там страница, даже без интернета И так как это просто строчка, то ничего не помешает вставить её в qr код В общем первую версию игры я решил сделать браузерной на чистом js Спойлер для тех кто сейчас скривил лицо – вторая версия будет на чистом C, так что не спешите дислайкать Я немного поэкспериментировал с этими data urls, и понял что большинство приложений-сканеров их считывают и позволяют скопировать текст, но если контент qr кода не начинается на http ни одно из них не показывает кнопку “перейти” чтобы сразу открыть в браузере Я немного расстроился что это будет не так красиво, отсканировал и открылась игра, а надо будет что-то куда-то копировать Но всё равно игра то вся в qr коде, даже ни в каком не сжатом виде, так что пока вариант неплохой Игра кстати будет играться и на телефонах, и на компах, потому что обычно люди qr коды всё таки с телефонов сканируют В общем надо было уже начинать что-то делать, хоть я и не решил пока что за игру то буду делать Так что я начал с вот такой простой странички, просто черный canvas, это как я нагуглил такая вебовая штука на которой можно рисовать круги и квадраты, как раз то что мне и нужно Кстати вебом и JS я вообще-то не занимаюсь, так, в детстве баловался, так что если что не обижайтесь на говнокод Эта страничка уже занимала немало места, но сам код когда я его напишу уже можно будет сжать получше, убрав всякие лишние пробелы и назвав переменные одной буквой, как на информатике Кстати небольшой лайфхак – если не использовать всякие хитрые и локальные символы, то размер текстового файла в байтах равен количеству символов Это очень удобно, потому что можно сразу мониторить сколько занимает весь мой код и входит ли он в ограничение Сразу я попробовал скопировать это в браузер, добавить префикс data url, тут ещё понял что решётку на странице придётся заэкраниировать, то есть заменить на %23, значит решетки в коде лучше не использовать, а то они в три символа превратятся В общем тут можно было спокойно начинать кодить, параллельно гугля как там в это js рисовать круги, квадраты, и всё остальное что мне нужно Я ещё подумал над тем какую хочу игру, решил что классика вроде всяких тетрисов и понгов это как-то не актуально, и хочется сделать что-то что сейчас в моде, что ли И хотелось более аркадную игру, чем пошаговую, такие мне кажется более впечатляюще выглядят Немного поскроллил главную страницу GOogle Play, и решил делать минималистичный скроллер, где нужно уворачиваться от препятствий и собирать очки Код писался хорошо, довольно скоро на чёрном квадрате у меня появился пульсирующий круг, будующий главный герой игры, а в файле с кодом появились функции отрисовки

и update, который вызывался раз в 20мс, и в который можно было спокойно писать логику и физику, ничего не умножая на Time.deltaTime – доступное количество символов ведь не позволяет Тут ещё надо сказать про такие сервисы как минификаторы JS, если вы не очень знакомы с миром веба, то это программы которые не меняя логики кода его уменьшают Они как раз используются чтобы страница занимала меньше места в памяти Самое действенное что они делают это просто удаляют ненужные пробелы, табы, и переносы строк, и ещё могут сокращать всякие записи и переименовывать локальные переменные Я если честно так и не разобрался какие переменные в JS локальные, какие не локальные, что за слово var, на которое у меня уж точно не было свободного места в файле, так что в итоге я сам в самом конце разработки переименовывал глобальные переменные и функции Но пока что это было удобно, чтобы оценить сколько примерно займёт сжатый код игры Ещё спустя какое-то время в игре появилась логика, управление, препятствия и возможность проиграть, а вы знаете что игра становится игрой когда в ней появляется возможность проиграть Пришло время проверить влезает ли оно в qr код, иначе писать дальше было слишком страшно Я загуглил генераторы qr кодов, и после нескольких вообще не работающих с текстом длиннее средней ссылки, нашёл вот этот Он позволял ещё и выбирать уровень коррекции ошибок, кстати есть и такая фишка в этих qr кодах, и выбирать размер картинки, чтобы скачать код в png Хоть и немного подтормаживал Я открыл эту картинку, навёл на неё свой бюджетный xiaomi, ии.. он не справился Сканировал какие-то номера Я попробовал ещё несколько сканеров из Google Play, но они все работали подозрительно одинаково, и все выдавали одно и тоже Тогда я попробовал сделать обычную фотку, и загрузил её в онлайн распознователь – и вот он уже выдал мне мой код игры, правда с ошибками, где-то стояли пробелы, где-то были перепутаны символы Раньше я думал что там есть какие-то контрольные суммы, но похоже что даже с коррекцией ошибок qr код может считаться неправильно, интересно! Кстати я вставил туда свой неминифицированный код, на 2 с небольшим килобайта, и если его минифицировать думаю код бы считался корректно Так что я ещё пол часика покодил, дополировал управление, сделал увеличивающуюся скорость, ещё всякие мелочи, и попытался всунуть в код уже минифицированную версию игры Тут всё ещё были длинные названия функций, но она занимала уже около 1500 символов Я вставил код в тот же генератор, получил код, и вуаля, он отсканировался стандартным приложением, и при копировании в браузер можно было спокойно играть И конечно же проиграть Было ещё что доделать, но когда всё чётко запустилось на телефоне, и я знал что ещё можно сжать всякие имена переменных, я понял что полноценная игра точно влезет в qr код, и спокойно продолжил добавлять всякие фичи Я добавил зелёные круги которые надо собирать, отображение счёта в углу, немного вынес похожий код в функции Вот часто оптимизирую код игры по времени выполнения, но по количеству символов в файле с кодом если честно в первый раз Потвикал внешний вид точки-главного героя, немного подебажил это в консоли разработчика хрома, кстати она довольно таки удобная Потом добавил систему спауна разных чанков, чтобы игровой уровень был разнообразнее, там в итоге около 10 чанков, и они иногда отражаются по горизонтали, так что первые несколько минут даже интересно играть Эти чанки, которые были по сути набором координат препятствий и зелёных кругов я тоже постарался как можно компактнее сжать, даже координаты тут писал поделённые на десять, а потом в функции которая спаунит чанк умножал на десять, чтобы не добавлять везде в конце нолик и сэкономить на этом пару байт Плейтестил, настраивал цвета объектов, в общем как обычно все делают игры, тут совершенно ничего необычного Были кончено тонкости, в какой-то момент я на калькуляторе делил 3.14 на четыре, потому что на строчку Math.PI/4 было жалко места, а точность мне особо не была нужна Когда игра была в готовом для релиза состоянии, я таки вернулся к изучению здоровенных qr кодов, чтобы понять почему всё таки тогда код не сканировался стандартным приложением Размер моей игры уже даже в минифицированном виде приблизился к 2k, и я мог снова столкнуться с этой проблемой Путём экспериментов я выяснил, что для определённого содержимого, если поперебирать размеры qr в пикселях в настройках этого сайта, то некоторые почти всегда сканируются, а некоторые никогда не сканируются Я долго об этом думал, кстати если вы думаете что я открвал код в каком-то дробном масштабе, и из-за интерполяции при скейле картинки он был некорректным, то нет, всё было открыто пиксель перфектно В общем я смотрел на этот код минут пять, и вдруг резко всё понял Открыл этот код в пеинте, увеличил, и увидел то что

я и подозревал Некоторые столбцы и строчки было на один пиксель уже других Глазу это на маленьком масштабе было почти не заметно, но вот сканер похоже от таких неточностей офигевал, 3 пикселя вместо 4х, это почти минус 25% Даже не буду это комментировать, просто поблагодарю что такие баги с округлением находятся в генераторе qr кодов, а не в коде моего интернет банка Я нашел от этого идеальный рецепт, просто скачивал код в svg, и увеличивал в хроме на 400% Так что я минифицировал игру, попереименовывал всякие переменные, вставил в генератор, убедился что png версия не работает, скачал svg версию, и она уже отлично отсканировалась и игралась на телефоне Так что я сохранил этот код в папочку, и решил на этом js версию официально считать законченной Вы можете прямо сейчас отсканировать код, и поиграть Из-за сжатия видоса он возможно тоже не отсканируется, так что в описании ссылка на картинку Да, кстати потом я проверил, на айфонах стандартная камера отказывается сканировать всё что начинается на data:, так что вы не сможете его отсканить, только если сторонними приложениями, или через какой-нибудь сайт Обязательно пишите в комментах ваш рекорд, на сколько вас хватило или как быстро вам наскучило Игра кстати готова, а вы только на середине видео, обратите внимание! Это потому что как и чувак из видео, которым я вдохновлялся, я решил что гораздо круче чем браузерка на JS, будет настоящий исполняемый файл, прям тот что с расширением exe Сделать игру в .exe в 2 с небольшим кб, чтобы она не требовала специфичных библиотек, и соответственно входила в QR, по моему отличная задачка! Тут был выбор писать либо на ассемблере, либо на С Был ещё конечно Rust, но я его вообще не знаю, C++, но я не верю что он влезет в 2к И наверное ещё куча мертвых или странных языков Я выбрал С, потому что ассемблер я не очень знаю и люблю, а вот С просто обожаю Тут у меня скорее сложное отношение, потому что сам С я люблю, а вот всё что вокруг него: куча каких-то непонятных компиляторов, линкеров, библиотек, систем сборки, не очень люблю Так что первая и самая сложная для меня задача была всё настроить, выбрать как выводить графику, и скомпилировать hello world Написать сам код вообще фигня, подержите мой чаёк, и всё сделаю На эту настройку и выбор я наверное в итоге убил часов 10 времени Начал я с попыток настроить CLion+gcc Конечно же на винде через cygwin Для графики я решил использовать winapi, потому что мне нужен был именно исполняемый на винде файл, иначе точно никто в мою игру так и не поиграет, и с gcc я даже не смог просто собрать hello world через winapi, не касаясь темы размера Потом я пытался настроить компилятор микрософта через консоль, но опять же после часов сражений с директориями стандартных библиотек пришёл к выводу что оно того не стоит Так что пришёл к самому простому варианту, просто открыть visual studio Студия конечно содержит почти все настройки консольного компилятора вот в этом окошке, но у неё тоже было несколько проблем для моих целей Во первых она просто не очень, особенно стандартные хоткеи Ну и во вторых, самое малеькое что я смог скомпилировать, это 7.59кб Намного больше чем мне нужно Тут у меня в голове возникли флешбеки к школьным годам, когда трава была зеленее, жесткие диски и программы в сто раз меньше, а люди использовали студию 2008 И я решил попробовать скачать её, с ней старую версию компилятора, и посмотреть сколько получится там Нашёл, скачал, установил, открыл тот же самый код, скомпилировал Получил кучу ошибок, попытался настроить её на стандарт С99, не смог, начал подозревать что он ещё не вышел в 2008 году, так что переписал прогу быстренько на стандарт С89 По дороге заметил что в новой студии нет хоткея для настроек проекта, а в старой есть, значит уже не зря скачивал, сэкономлю время на клики мышкой по меню Тут я ещё догадался задать свою точку входа линкеру, и получил файл размером 2,5к, победа! Это кстати пока просто тестовая прога которая рисует шум, но т.к. я отключил библиотеку с функцией рандома, она теперь рисует не совсем случайный шум Места свободного почти не было, уже близок был предел в 2.8кб, но я решил начать писать код, а там уже разобраться по ходу как сжимать дальше Вскоре после начала разработки я понял что похоже из-за того что я отключил все библиотеки у меня не получится использоваться float’ы, потому я понятия не имел где должны быть функции конвертации между int и float, поэтому при использовании дробных чисел прога не компилировалась Ну, никто не говорил что будет легко, и я решил просто обойтись целыми числами Деды вон вообще микроконтроллеры программировали, а мы тут

жалуемся Чтобы увеличить точность я просто некоторые переменные, вроде скорости скролла уровня и положения игрока хранил в сотых долях То есть в переменной лежало например 1230, и это значило что он находится примерно в 12.3 пикселя от центра Потратил какое-то время чтобы разобраться с винапишными таймерами, сделать функцию которая вызывается в цикле, сделать перерисовку окна несколько раз в секунду Когда у меня получился летящий круг, я понял что по умолчанию тут нет двойной буферизации, и всё вот так вот дергается из-за этого, так что с помощью гугла добавил её Круг стал лететь сглаженно, но появилась проблема – exeшник разросся до неприемлемых 4 килобайт! Тут я решил попробовать такие программы как пакеры – это что-то вроде архиватора, но у вас получается не zip архив, а обычный исполняемый файл, который при запуске распаковывается уже в оперативной памяти в нашу исходную программу Некоторые из них просто крашились при запуске на моём exeшнике, но upx, самый популярный, смог сжать программу до 3кб Недостаточно хорошо Так что я продолжить гуглить, и после ещё тысячи вариантов настроек компилятора и линкера пришёл вот к таким вот директивам – они объединяли секции программы с кодом, статическими переменными, и строками в одну, убирая кучу неиспользуемого места И мы снова в игре, файл сжался до 2кб Я кстати попробовал натравить на него upx, думал может выиграю ещё 5-10 процентов, но тут уже и он упал, когда увидел такое извращение Снова можно было отвлечься на пол часа от этого всего конфига и просто писать игру Я получил круг посредине экрана, начал делать управление, потом сделал отображение счёта Стоит ли говорить что конвертацию числа в строку тоже пришлось писать самому И с ней размер превратился в 3кб А ведь ещё не было ни чанков, ни системы их спауна, не хватало кучи фишек управления, и даже нельзя было проиграть Надо было менять подход, тут уже настройками не обойтись Оставался ещё один вариант, который я не пробовал Вообще нативные программы обычно собираются в два этапа – сначала компилятор переводит код в почти машинные команды, а потом линкер собирает один или несколько файлов полученных от компилятора и нужные библиотеки в готовый exe файл, или какой нужен для вашей системы Я тут использовал идущие со студией стандартные компилятор и линкер, которые конечно не предназначены для супер маленьких файлов Например тот же линкер выравнивает файлы до 512б, то есть по сути округляет размер вверх, и я не смог уменьшить это число В нормальной жизни лишние 400б вам никак не помешают, но мне очень сильно могут К счастью на просторах интернета я нашёл альтернативу стандартному линкеру, crinkler, который делает ту же работу, но при этом ориентирован на уменьшение размера файла Он вроде бы был создан как раз для разработки демок И он уже по дефолту сливает все секции, ничего не выравнивает, использует самые короткие варианты команд, и наверное ещё много всего делает о чём я даже не подозреваю Я его скачал, и пытался натравить на obj файл, который лежит в папке с проектом после билда Это как раз промежуточный файл который произвёл компилятор, и потом уже стандартный линкер сделал из него exeшник, а я хотел сделать другой exeшник кринклером Кринклер у меня тоже крашился Я не смог ни разу его запустить так, чтобы он не крашнулся Не знаю почему, наверное я накосячил с какими-то директориями опять Уже почти отчаявшись, я нашел на каком-то сайте с ужаасным шрифтом лайфхак – можно переименовать crinkler в linker.exe, подложить его в директорию студии, и тогда она будет думать что это её стандартный линкер, и будет использовать его Заодно можно будет собирать прогу одной кнопкой как раньше, а не жать билд, а потом запускать батник В общем когда это сработало, я конечно очень удивился, но и очень обрадовался, потому что размер уменьшился до 1.16кб, что во первых дало огромный простор, а во вторых теперь я точно видел сколько байт добавляет каждая новая строчка, а не просто ждал пока размер внезапно раздуется на 512байт На радостях я добавил систему спауна чанков, перенёс все чанки из веб версии игры, конечно всё равно игры ощущаются немного по разному, из-за того что в нативной нет сглаживания, немного другая проверка коллизий, но всё же чанки и логика в целом одинаковая Я добавил всякие твики управления, проверки границ, разобрался с проигрышем и корректным завершением процесса, потом ещё цвета скопировал из веб версии, в общем довольно скоро игра была готова По крайней мере настолько же готова насколько веб версия Там были все чанки, ускорение со временем, и даже нельзя

было вылететь за границы поля, чуть не забыл про это Пришло время запихивать её в qr код Exe файл, как и многие другие, он не текстовый, а бинарный То есть состоит изо всяких рандомных на первый взгляд байт Текстовый тоже из байт, но в тексте не может быть байт со значниями меньше 13, и ещё кучи других байт То есть вы можете преобразовать текст в байты, но если сделаете наоборот, то неизбежно потеряйте часть данных Многие QR ридеры рассчитаны на текст, потому что текст можно отобразить на страничке в каком-то поле, и потом скопировать куда надо И генераторы точно так же, вводишь в текстовое поле текст, он генерит по нему код Перетащить файл никуда нельзя, а нам нужен именно сканер и генератор, который может принять любой файл, и по нему сгенерить код А сканер соответственно выдаёт готовый файл который можно скачать Я таких не нашёл, нашёл только open source программы, которые можно скачать, поставить, и они все собранные почему-то под linux Так что я решил поступить немного по другому Начнём с генерации такого кода – я скачал с гитхаба javascript’овый open source генератор qr кодов, да, без js и тут не обойтись Этот генератор выглядит вот так, в общем обычный генератор из текста Я открыл исходники демо странички, и тут просто передаётся строковое значение в функцию, а та возвращает картинку Значение передаётся строкой, но можно немного извратиться, и передать туда массив байт Если мы просто преобразуем байты нашего файла в текст и скопируем его в текстовое поле, то мы что-то потеряем А вот если мы сразу в коде передадим сюда строку, состоящую из байт файла игры – всё должно сработать В общем звучит сложно, но должно сработать Сначала конечно надо загуглить как в js преобразовать байты в текст, это вот таким заклинанием делается Сначала для теста я написал сюда 042 и 50, это примерно по кодам должны быть какие-то буквы, чисто чтобы проверить что этот код работает Это сработало, сгенерился код с буквами BP, вот эти буквы это обычные текстовые буквы, их можно было бы и просто скопировать Но сейчас мы вставим туда байты файла игры Для этого нужно будет их как-то получить, для этого проще всего использовать hex редактор, которого у меня почему-то нет, так что скачаю HxD, открою им exeшник, и тут как раз все байты, а рядом их текстовое представление Вот всякие эти 07 как раз и потерялись бы если бы мы копировали текст Я отсюда просто выделю и скопирую сами шестнадцатеричные значения байт, вставлю их в блокнотик, и вот – это и есть моя игра Не её исходный код, а сама игра, машинные команды, и всё что нужно для её запуска Можно распечатать это в газете, потом переписать к себе на комп, запускать и играть Так кстати и делали с играми лет тридцать назад, насколько я знаю А у нас пытаются трекеры блокировать, ничего не понимают в информатике В общем эту мешанину нужно переделать в массив для вставки в js, это владея магией sublimе’a очень просто сделать, разделю байты запятыми и добавлю 0х в начало, и можно копировать в страничку генератора Прям так вставляю, обновляю, и появился код, и текстовое значение, где кривые символы заменились на квадраты Это значение нам не нужно, нам нужен только сам код, который правда надо немного увеличить, до 1000 пикселей скажем, ещё раз обновить, и вот он уже огромный код Сохраню его, это абсоллютно корректный код, который корректно по стандарту содержит исполняемый файл игры Следующая задача это как-то его считать Тут есть один хороший сайт, ZXing.org, который умеет почти что хорошо читать бинарные qr коды Заходим на него, пока я просто перетащу оригинал кода, а не фотку, немного читерство, но с фоткой будем экспериментировать позже Он выдаёт нам этот кривенький текст, который нас не интересует, и ещё чистые бинарные данные Тут есть пара нюансов Во первых в эти данные он включает служебную информацию qr кода – там в начале кодируется тип данных и длинна сообщения Если мы зайдём в наш оригинальный файл, то можно посмотреть что он начинается на 4d5a, а заканчивается на d355 Тут у нас есть как раз нужные 4d5a, вот только первые два с половиной байта С половиной потому что тип кода, как мы в самом начале видели на вики, занимает 4 бита, то есть пол байта, одну 16ричную циферку И во вторых в конце qr кода, так как код квадратный, до конца он заполняется байтами ec 11 А перед ними, как раз нужные нам d355 Ну и ещё нолик, потому что файл у нас занимает целое число байт, а в начале отступ 2.5 Есть вот этот сканер, который не пишет лишних символов, но тут блин намешаны номера строк и текст, так что так

просто тоже не скопируешь В общем главное нам убрать всё лишнее, это не так сложно Ну и компактно же это выглядит Обычно в игре ведь сотни файлов кода, не считая всех ассетов, а тут на треть fullhd экрана текст всей игры целиком помещается Потом я создам уже новый файл в HxD, и туда вставлю наши шестнадцатеричные байты Сохраню, смело пишу расширение файла .exe, запускаю, и.. получилось, работает как и раньше Пока это всё не совсем честно, мы ведь заливали сам qr код, а не его фотку Но сейчас проверим всё честно! Кстати всё что я делал я складывал в одну папочку, и я обязательно залью её на гитхаб, ссылка будет в описании, если кто-то захочет попробовать повторить эксперимент, или сделать на основе этого что-нибудь другое Но только прежде чем качать, да и если не собираетесь качать то тоже хорошая идея поставить лайк этому видео, ну и все остальные нужные ритуалы ютуба В итоге я решил что надо всё это проверить с настоящими кодами на листах бумаги, которые будут сфотканы на телефон Я распечатал qr коды двух полученных версий игры на принтере, чтобы всё было совсем честно Первым идёт браузерная веб версия игры – вот фотка этого кода сделанная на средненькую камеру моего телефона Кстати я долго пробовал разные онлайн сканеры, и в итоге вот этот мне понравился больше всего Загружаем в сканер эту фотку, он нам выдаёт что зашифрованно в этом коде Копируем целиком этот текст, правда из него нужно удалить переносы строк который сканер зачем-то добавил, вставляем в адресную строку браузера – и открывается игра Это всё кстати точно так же работает с обычным qr сканером с телефона Обратите внимание что вся игра содержится в qr коде, она не требует никаких дополнительных скачиваний и переходов, и вообще у меня на телефоне сейчас выключен весь интернет Если браузерная версия вам кажется недостаточно крутой – то есть такая же игра на PC Тут отсканировать уже чуть сложнее, так как получить мы должны не текст, а exe файл Но и не очень сложно – загружаем фотку qr кода в сканер, он нам выдаёт бинарные данные Копируем эти данные в любой hex редактор, у меня бесплатный hxd, и сохраняем файл с расширением exe Можно запускать и играть! Кстати если вам люди в интрнете говорят скачать или создать что-то с расширением exe и запустить, то обычно этого лучше не делать, а то мало ли что там внутри будет, всякая вирусня и меньше килобайта может занимать то есть запускайте только если вы мне доверяете Кстати js версия занимает в итоге 2214 байт, а нативная 1956 Но это ничего не говорит об этих технологиях, думаю и ту и ту версию можно спокойно ещё дополнительно сжать Пишите в комментах как у вас полуичлось отсканировать код, и круто это или бесполезно А если вдруг сами решите попробовать сделать игру в 3 килобайта, то тем более обязательно пишите c #qrgamechallenge Надеюсь было интересно, подписывайтесь чтобы не пропустить будущие крутые видосы, и спасибо за просмотр!