惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
雷峰网
雷峰网
Recent Announcements
Recent Announcements
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
博客园_首页
The Cloudflare Blog
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
S
SegmentFault 最新的问题
P
Proofpoint News Feed
Y
Y Combinator Blog
Jina AI
Jina AI
博客园 - 聂微东
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
F
Full Disclosure
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
T
Tailwind CSS Blog
J
Java Code Geeks
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Blog of Author Tim Ferriss
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
宝玉的分享
宝玉的分享
IT之家
IT之家
Hacker News: Ask HN
Hacker News: Ask HN
The Register - Security
The Register - Security
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Flappy Bird: код веб-клона под микроскопом
Laborant_Cod · 2026-05-06 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение9 мин

Охват и читатели16

Обзор

Flappy Bird

Flappy Bird

Введение: феномен, который никто так и не смог скопировать

В январе 2014 года мир сошёл с ума по Flappy Bird, хотя сама игра вышла ещё 24 мая 2013 года и была предельно простой. Всё, что в ней нужно делать игроку — тапать по экрану, чтобы птица не врезалась в трубы. Тем не менее игра внезапно стала вирусной, а её создатель зарабатывал на рекламе по $50 000 в день.

Но главное в этой истории не только популярность самой игры, но и её быстрый финал. Из‑за ошеломляющего успеха и давления разработчик Донг Нгуен удалил игру из магазинов приложений в феврале 2014 года, что вызвало ещё больший ажиотаж. Flappy Bird стала феноменом, который породил тысячи клонов, и даже на чёрном рынке начали продавать смартфоны с предустановленной игрой, так как самой игры в интернете больше не было.

Объясняя мотивы, подтолкнувшие его к удалению хита, Нгуен признаётся, что популярность приложения лишила его покоя — вплоть до проблем со сном. После удаления Flappy Bird он на несколько дней отключился от интернета и смог наконец нормально отдохнуть (источник — здесь объясняется подробнее, что произошло).

Но вот парадокс: при всей простоте механики почти ни один клон не смог передать то самое чувство оригинала. Разница в пикселях? В частоте обновления? В физике? Или в чём-то неуловимом, что делает игру одновременно бесящей и вызывающей зависимость?

Я подумал и решил, что нужно создать собственный веб-клон Flappy Bird и разобрать его до винтиков. Понять, что мне не нравится в нём или почему я не хочу в него играть, в отличие от оригинала. В этой статье мы заглянем в архитектуру игрового движка, разберём формулы физики, оптимизацию рендеринга и — самое интересное (по крайней мере для меня) — честно перечислим, почему браузерная версия никогда не догонит легендарный оригинал.

Часть 1. Архитектура проекта: модульный подход к ретроигре

Когда я проектировал веб-клон Flappy Bird, ключевым решением стало разделение логики. В итоге получилось, что вместо одного большого файла у нас каждый модуль живёт своей жизнью. Хотя для проектов это уже стандарт, который обеспечивает правильность кода.

Три кита архитектуры

Три кита архитектуры

Почему это важно? В теории это ни на что сильно не влияет, но для понимания и лёгкого редактирования это неизбежный и важный момент. Так вот, движок ничего не знает о том, как выглядит птица, он оперирует голыми координатами {x, y}, радиусом r и скоростью vy. Художник (Renderer) же не знает правил игры — он просто получает текущее состояние и рисует кадр.

1.2 Главный цикл и смысл всех ретроигр — цена 60 (или меньше) кадров в секунду

animationLoop() {
    this.gameUpdate();  // Шаг физики и логики
    this.render();      // Отрисовка
    requestAnimationFrame(() => this.animationLoop());
}

requestAnimationFrame(rAF) — это встроенный в браузер метод JavaScript, предназначенный для создания плавных анимаций. В отличие от setInterval или setTimeout, он синхронизирован с вертикальной развёрткой монитора (vsync). При 60 Hz мы получаем ровно 60 вызовов в секунду (взял определение из интернета).

Если говорить проще, то каждый такой вызов — это один игровой кадр, благодаря которому игра выглядит плавно и отзывчиво. Весь код, связанный с обновлением логики и отрисовкой, должен уложиться в промежуток между двумя кадрами. При 60 кадрах в секунду этот промежуток составляет примерно 16.6 миллисекунды (1 тыс. мс / 60 = 16.66 мс).

На современном железе это ~2–3 мс. Запас есть, но на слабых мобильных устройствах с экранами 30 Hz могут быть проблемы.


Часть 2. Физика под микроскопом: формулы и их значение

2.1 Гравитация и прыжок: почему именно эти числа?

const GRAVITY = 0.2;        // пиксель/кадр²
const JUMP_POWER = -5.4;    // пиксель/кадр (отрицательная скорость — вверх)
const MAX_VELOCITY = 9;     // терминальная скорость

updateBird() {
    this.bird.vy += GRAVITY;              // v = v₀ + a·t
    if (this.bird.vy > MAX_VELOCITY)
        this.bird.vy = MAX_VELOCITY;      // ограничение скорости падения
    this.bird.y += this.bird.vy;          // Δy = v·Δt (Δt = 1 кадр)
}

jump() {
    this.bird.vy = JUMP_POWER;            // мгновенный импульс (замена скорости)
}

2.1.1 Ускорение свободного падения в игре

Константа GRAVITY = 0.2 означает, что за каждый кадр вертикальная скорость птицы увеличивается на 0.2 пикселя/кадр. Это покадровое ускорение, и чтобы получить привычное физическое ускорение в единицах «пиксель/секунда²», нужно умножить на количество кадров в секунде (60 кадров/сек):

a = 0.2 (пиксель/кадр²) * 60 (кадр/сек) = 12 (пиксель/сек²)

2.1.2 Здесь может возникнуть вопрос: почему GRAVITY = 0.2 или откуда это число?

Так вот, значение подобрано геймдизайнером (мной) экспериментально ради баланса игровых ощущений:

Изначально я взял GRAVITY = 1.6, чтобы приблизиться к реальному ускорению 9.8 м/с² (при 1 пиксель ≈ 1 см и 60 кадрах/сек получается ≈ 9.6 м/с² — почти точно). Но при такой гравитации игра оказалась слишком медленной, птица вяло поднималась и долго падала, трудно было создать напряжение. Тогда я решил повысить показатель и путём подбора увеличил гравитацию до 0.2. Это сделало падение быстрее, что меня устраивало как игрока, когда я тестировал игру.

Таким образом, 0.2 — это не физическая константа, а игровая. И её можно спокойно увеличивать или уменьшать, т. к. она рождалась из ощущений, а не из расчётов.

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

В реальной физике прыжок добавляет импульс к текущей скорости:

v(после) = v(до) + u(импульс)

Если птица падает (v_до положительная), прыжок (u_отрицательный) лишь уменьшит скорость падения, но не гарантирует подъёма.

В игре же прыжок полностью заменяет скорость:

jump() {
    this.bird.vy = JUMP_POWER;  
}

Это сделано намеренно, и главная причина — это предсказуемость для игрока, то есть высота прыжка всегда одинакова, независимо от того, падала птица или поднималась.

2.1.4 Траектория полёта

Если построить график y(t) при последовательных прыжках, получится пилообразная кривая, и из-за ограничения максимальной скорости график падения становится линейным, а не параболическим, как должно быть в реальном мире, но в игре это как раз то, что добавляет предсказуемости в прыжках.

Итог: вся механика — осознанное упрощение физики ради динамичного и понятного геймплея. Цифры рождены не из учебника или формул, а из игровых тестов самого пользователя (пока это был только я).

2.2 Генерация труб: процедурный хаос

randomPipeHeight() {
    const max = CONFIG.H - CONFIG.PIPE_GAP - 50;  // 600 - 160 - 50 = 390
    const min = 50;
    return Math.floor(Math.random() * (max - min + 1) + min);
}

2.2.1 Почему просвет именно 160 пикселей?

Высота птицы — 28 пикселей. Просвет в 160 пикселей составляет:

Просвет

Относительный размер

Результат

200 px

7.1 высота птицы

Игра слишком лёгкая (на 20% больше пространства для ошибки)

160 px

5.7 высота птицы

Оптимально — по ощущениям лучший вариант

120 px

4.3 высота птицы

Почти непроходимо, игра превращается в лотерею

Почему не больше и не меньше?

Значение 160 подобрано для более простой игры, но при тестировании мне больше понравилось 120. Также если посмотреть расстояние между трубами, то можно понять, что каждые 1.8 секунды игрок получает новое препятствие (трубу), при просвете 160 игра более спокойна, чтобы расслабиться. Но когда поставил 120, я немного почувствовал старые воспоминания, поэтому здесь нужно будет добавить два режима в будущем.

2.3 Математика бесконечной генерации

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

  1. Когда труба уходит за левый край экрана (x + width < 0), она удаляется из массива.

  2. Новая труба создаётся, когда последняя труба приближается к правому краю на расстояние меньше PIPE_SPACING.

  3. Позиция новой трубы: x = canvas.width, а просвет (дырка) генерируется случайно в допустимых пределах.

2.4 Коллизии: геометрия немного нестандартная

checkCircleRectCollision(cx, cy, r, rx, ry, rw, rh) {
    let closestX = Math.max(rx, Math.min(cx, rx + rw));
    let closestY = Math.max(ry, Math.min(cy, ry + rh));
    let dx = cx - closestX;
    let dy = cy - closestY;
    return (dx * dx + dy * dy) < r * r;
}

Возможно, возникает вопрос: а почему не AABB (прямоугольник — прямоугольник)?

AABB

AABB

Так вот, если бы мы проверяли столкновение по прямоугольному bounding box птицы (28×28 пикселей), то:

  • Углы труб имели бы пустые зоны (визуально птица ещё не коснулась трубы).

  • Игрок бы получал «фантомные» смерти.

Математика метода «круг — прямоугольник»

  1. Находим ближайшую точку прямоугольника к центру круга — через проекцию центра на оси прямоугольника с последующим ограничением (clamp).

  2. Вычисляем евклидово расстояние от центра круга до этой точки.

  3. Если расстояние меньше радиуса, коллизия есть.

Важный нюанс, что порядок проверки происходит после обновления позиции. Если за один кадр птица «перелетела» сквозь трубу (была слева, а стала справа, ни разу не задев стенки), мы всё равно засчитываем успех.


Часть 3. Визитка движка самой игры

3.1 Отрисовка птицы

В классическом Flappy Bird птица была квадратной — 8-битная эстетика рулила. В моей версии птица круглая и немного напоминает Angry Birds, но дух игры сохраняется. Вот как рисуется тело птицы из файла skinManager.js:

ctx.beginPath();                                   // начинаем рисовать
ctx.ellipse(0, -1, config.BIRD_RADIUS - 1, config.BIRD_RADIUS, 0, 0, Math.PI * 2);  // тело
ctx.fillStyle = skin.bodyColor1;                   // цвет тела
ctx.fill();                                        // закрашиваем

Почему именно так?

1. Честные коллизии.
Птица — круг радиусом config.BIRD_RADIUS. Отрисовка строго соответствует геометрии, проверяемой в checkCollision(). Игрок видит ровно ту форму, которая участвует в столкновениях.

2. Простота кастомных скинов.
Система скинов (skins объект) подменяет только цвета. Форма остаётся неизменной, поэтому любой скин автоматически корректен с точки зрения коллизий и физики.

3.2. Анимация крыльев, или, по-другому, конечный автомат в действии

Вот сердце анимации — функция updateWingAngle():

function updateWingAngle() {
    if (isFlapping) {
        wingAngle = Math.sin(flapTimer * 0.03) * 1.4;
        flapTimer++;
        if (flapTimer > 55) {
            isFlapping = false;
            flapTimer = 0;
        }
    } else {
        wingAngle += 0.025 * wingDirection;
        if (wingAngle > 0.5) {
            wingAngle = 0.5;
            wingDirection = -1;
        } else if (wingAngle < -0.5) {
            wingAngle = -0.5;
            wingDirection = 1;
        }
    }
}

Математика плавного движения

В состоянии покоя крыло движется линейно. Код прибавляет +0.025 или -0.025 к wingAngle на каждом кадре в зависимости от wingDirection. При достижении границ ±0.5 направление меняется на противоположное.

Математика резкого взмаха

Когда игрок нажимает пробел или тап, вызывается triggerWingFlap(), которая устанавливает:

isFlapping = true;    // включаем режим прыжка
flapTimer = 0;        // сбрасываем таймер
wingAngle = 0.9;      // мгновенный стар

Дальше каждый кадр вычисляется по формуле:

wingAngle = sin(flapTimer * 0.03) * 1.4


Часть 4. Минусы нашего клона (в коде)

4.1 Проблема №1: Нет предсказания ввода (Input Prediction)

В нативных играх есть понятие input prediction. Если игра видит, что игрок нажал на экран за 5 мс до столкновения, она может «простить» ошибку, откатив время на 1 кадр.

У нас строгая проверка, что если коллизия есть, то Game Over.

4.2 Проблема №2: Звук или его отсутствие

В игре нет вообще звуков. В оригинале есть щелчок при прыжке или «бдыщь» при столкновении — это создаёт почти 50% ощущений. Без аудиообратной связи игра кажется «плоской».

Почему не добавить? Так вот, в Web Audio API требует запроса разрешения от пользователя (из-за автополитики браузеров), без жеста пользователя звук не воспроизвести, поэтому довольно часто этот пункт пропускают при веб-разработке.


Часть 5. Логические проблемы: почему игра не вызывает азарт

Технические недостатки — это полбеды. Главная проблема моего клона в том, что он не вызывает зависимости. Разберём почему.

5.1 Проблема №7: Нет «эффекта потока»

Оригинал был удалён, потому что вызывал зависимость. Там был идеальный цикл, который в психологии называется «поток».

Критерий потока

Оригинал

Низкий порог входа

Один клик — и ты в игре

Быстрая обратная связь

Смерть за 1-2 секунды

Мгновенный рестарт

Повторный клик по экрану

Отсутствие барьеров

Нет загрузок, нет меню

Психологический механизм: Человек тратит 2 секунды на попытку, 0.1 секунды на осознание ошибки и 0.2 секунды на рестарт. Цикл занимает 2.3 секунды. В моём клоне: 2 секунды на попытку, 1 секунда на появление меню, 1 секунда на рестарт — цикл в 2 раза длиннее.

5.2 Проблема №8: Нет стиля и «души» как в Mario

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

5.3 Проблема №9: Неправильный темп игры (Tempo Mismatch)

В оригинальной Flappy Bird расстояние между трубами и скорость птицы подобраны так, чтобы игрок постоянно находился в состоянии лёгкого стресса.

У меня же игра превращается в плавный полёт бумажного самолётика. Игрок кинул, посмотрел, надоело, ушёл. Нет того самого «ещё одну попытку».

5.4 Проблема №10: Нет прогрессии сложности

В оригинале после 10, 20, 30 очков игра не менялась. Это было гениально, т. к. игрок знал, что если он прошёл 5 труб, то 10-ю пройдёт по той же формуле. Сложность не растёт — растёт только давление от счёта.

В моём клоне тоже нет прогрессии. Но почему это проблема, если в оригинале его тоже не было? Так вот, ответ следует из 9 проблемы, а конкретно в скорость, если у тебя спокойная игра, то игрок быстро находит «золотую середину» и играет на автомате, и так как игра плавная, то не ощущения собственного прогресса: «Раньше я умирал на 5 очках, а теперь на 20!».

5.5 Проблема №11: Нет социального доказательства или ажиотажа толпы и видео

Flappy Bird взлетела из-за вирусного эффекта. Люди постили скриншоты со счётом 2, 5, 10 — и смеялись над собой. Было стыдно за низкий счёт, но это рождало соревнование.

5.6 Проблема №12: Наказание слишком жестокое

В оригинале после смерти игра мгновенно перезапускалась. Игрок не успевал расстроиться — его палец уже снова жал на экран.

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


6. Итоги

6.1 Что получилось в итоге.

Игра (свой скин)

Игра (свой скин)

Меню

Меню

6.2 Заключение

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

Flappy Bird — идеальный пример того, как маленькие детали создают великую игру. Не физика и не графика, а психология: правильный баланс между сложностью и напряжением, мгновенная обратная связь, отсутствие барьеров для повторной попытки.

Наш код рабочий, играбельный и даже интересный. Но чтобы повторить успех оригинала, нужно копать глубже — на уровень психологии.


Поиграть в мою версию «Flappy Bird» можно здесь.

Исходный код открыт на GitHub — используйте его как основу для своих творческих экспериментов.

Если у вас есть идеи по улучшению проекта, вы нашли баг или просто хотите вспомнить, как впервые набили 10 очков в «Flappy Bird», пишите в комментариях

© 2026 ООО «МТ ФИНАНС»