Базовый курс С++

Материал из Викиверситета

Добро пожаловать на Базовый курс программирования на С++, созданный программистом Александром Семенко, который уже более 20 лет работает программистом на различных проектах. Этот курс рассчитан на новичка, который делает свои первые шаги в программировании. Здесь мы изучаем на практике как писать программу на языке С++ в операционной системе Windows, используя среду программирования Visual Studio.

На этом курсе мы не просто изучаем особенности языка - мы активно применяем их для того, чтобы написать простую компьютерную игру. Такая игра нам нужна в качестве точки приложения наших знаний, а так же как источник множества практических заданий, решая которые вы сможете научиться программировать гораздо эффективнее, чем просто изучая особенности языка.

Весь курс состоит из 95 частей, по окончании которого вы сможете считать себя C++ Junior-Developer, т.е. начинающим С++ разработчиком (но только, если вы сами следовали по курсу и писали код сами).

Оглавление

Часть #01. О чём и для кого этот курс (Базовый курс программирования на С++).[править]

Все части в плейлисте: https://www.youtube.com/playlist?list=PLlheCABZdy3OZoIh9VwlYUbTSCWwDUee6

Часть #02. Инструменты разработчика[править]

Устанавливаем инструменты разработчика - программы, которые помогают нам писать программы.
00:00 - Инструменты разработчика
02:02 - Устанавливаем Хром (Google Chrome)
02:43 - Устанавливаем Double Commander
04:03 - Создаём каталог Downloads
04:26 - Что такое "Среда Разработки"
05:44 - Устанавливаем Visual Studio
08:24 - Логинимся в Visual Studio
09:09 - Настраиваем Visual Studio
11:02 - Установка видеоплейера VLC
11:36 - Устанавливаем Meld
12:54 - Устанавливаем DOSBox
13:36 - Устанавливаем ActivePresenter

Часть #03. Double Commander[править]

Как работать с файловым менеджером Double Commander.
00:00 - Double Commander
03:31 - Double Commander
04:18 - Панели
04:35 - Переходы по каталогам
05:47 - F3 - просмотр файла
06:23 - F4 - редактирование файла
06:42 - F7 - создание каталога
07:21 - F8 - удаление файлов и каталогов
09:06 - Shift+F4 - создание файла
09:35 - F5 - копирование файлов и каталогов
10:34 - F6 - перемещение и переименование
11:14 - Выделение группы файлов
12:36 - Поле ввода команд
12:58 - Запуск программы с параметром
14:31 - Сортировка файлов
15:13 - Вкладки

Часть #04. Выбор проекта для обучения[править]

Как выбрать свой первый проект.
Ссылка на файл popcorn.zip: https://drive.google.com/file/d/1rTZy2xEh8GaSqGUSjwHhNSROV2ken4p7

Часть #05. Механики и Творцы[править]

Как работают программисты.

Часть #06. Прототип 01[править]

Начинаем разработку прототипа нашей игры.
Знакомство с Visual Studio и справочником MSDN.
00:00 - Прототип 01
02:33 - Создаём проект в Visual Studio
04:52 - Компилируем проект (клавиша F7)
06:06 - Запуск программы (клавиша F5)
06:52 - Обзор шаблонного проекта
07:46 - Оконная процедура
08:22 - Сообщения Windows
10:47 - Что такое программа
11:50 - Структура нашего проекта
14:45 - Настройка эмулятора DOSBox
18:28 - Захват видео программой ActivePresenter
25:21 - Получаем кадр из видео
28:47 - Начинаем делать прототип игры
29:24 - Задаём цвет фона окна
32:52 - Ищем описание функции CreateSolidBrush() в справочнике MSDN
36:30 - Макрос RGB
37:05 - Тип данных HBRUSH
37:53 - Типы данных и архитектура Windows
42:30 - Задаём размер окна
46:42 - Корректируем размеры клиентской области функцией AdjustWindowRect()
1:00:20 - Добавляем функцию Draw_Frame() для отрисовки кадра игры
1:04:20 - Стиль оформления нашего кода
1:06:56 - Как мы пишем комментарии
1:08:04 - Как сохранять файл с не-английским содержимым
1:10:08 - Как скачать архив с проектом: https://drive.google.com/open?id=1U5b...
1:10:17 - Как распаковать архив с проектом
1:11:34 - Как открыть наш проект в Visual Studio
Popcorn_Project.zip - https://drive.google.com/file/d/1U5bdRcv8k1gmgKTpa40FSpxWXY0V2m0R

Часть #07. Прототип 02[править]

00:00 - Прототип 02
02:23 - Два вида графики - растровая и векторная
03:57 - Вывод прямоугольника функцией Rectangle()
10:05 - Call Stack в Visual Studio
13:21 - Функция Rectangle() в справочнике MSDN
16:30 - Создаём карандаш функцией CreatePen()
20:49 - Выбираем карандаш функцией SelectObject()
21:29 - Создаём кисть функцией CreateSolidBrush()
23:10 - Выводим второй кирпич
26:27 - Выносим повторяющийся код в функцию Draw_Brick()
32:09 - Рефакторинг функции Draw_Brick()
35:09 - Вводим именованные константы
38:02 - Выводим в цикле 2 ряда кирпичей
41:43 - Заполняем всё игровое поле кирпичами
46:35 - Создаём текущий "срез" проекта в каталоге Backup
51:58 - Сравнение и слияние (мердж) двух версий проекта
Popcorn_01.zip - https://drive.google.com/file/d/1cJvmN3isnNvf9BuRfgg1t09p1cvILofG

Часть #08. Прототип 03[править]

00:00 - Прототип 03
01:17 - Объявляем массив для описания уровня
06:14 - Выводим кирпичи уровня
08:25 - Исправляем функцию Draw_Brick() для вывода 3-х типов кирпичей
09:39 - Используем оператор switch/case вместо if
12:25 - "Magic numbers" ("магические" числа) в программе
13:01 - Создаём перечисление (enum) для типа кирпичей
19:35 - Рисуем прямоугольник со скруглёнными углами функцией RoundRect()
24:53 - Выносим код рисования кирпичей в функцию Draw_Level()
25:57 - Выносим код создания карандашей и кистей в функцию инициализации Init()
30:05 - Меняем цвет фона и кирпичей
33:03 - Ваша очередь писать код
33:40 - Как сравнить ваш код с очередным "срезом" проекта
37:07 - Popcorn_02.zip: делаем паузу, пишем код
37:48 - Предварительное объявление функций
39:20 - Рефакторим исходный файл, чтобы вынести код в другой файл
40:07 - Создаём фильтр Engine, а в нём - пару файлов Engine.cpp и Engine.h
43:02 - Переносим код из Main.cpp в Engine.cpp
47:25 - Popcorn_03.zip: делаем паузу, пишем код
47:41 - Как делать срез проекта
49:12 - Откатываю изменения проекта на предыдущий шаг
50:11 - Мерджим код (как сравнить ваш код с моим)
Popcorn_02.zip - https://drive.google.com/file/d/1AY-FVwkB3UUcfRlh9RWmk6Ea9QfOIMvC
Popcorn_03.zip - https://drive.google.com/file/d/12M3WEMnorPuuESBvx8q8u1Bs_KNrUxSV

Часть #09. Прототип 04[править]

00:00 - Прототип 04
02:27 - Рисуем платформу
04:22 - Рисуем шарик платформы функцией Ellipse()
10:08 - Рисуем второй шарик платформы
13:23 - Рисуем среднюю часть платформы
20:12 - Выносим код рисования в функцию Draw_Platform()
23:28 - Меняем цвета платформы
25:05 - Создаём кисти и карандаши для платформы
28:30 - Выносим код в функцию Create_Pen_Brush()
31:54 - Возврат множества значений из функции
36:25 - Рисуем блик на шарике функцией Arc()
45:25 - Создаём карандаш для рисования блика
48:31 - Popcorn_04.zip: пишем код

Popcorn_04.zip - https://drive.google.com/file/d/1qwtbHfbDWL4cS2CyD684ziVmjaxhrhAf

Часть #10. Прототип 05[править]

00:00 - Прототип 05
00:30 - Анализируем анимацию элемента игры
03:48 - Делаю раскадровку анимации
17:22 - Выводим падающую букву
21:13 - Трансформации и структура XFORM
22:36 - Примеры трансформаций
24:05 - Функция SetWorldTransform()
25:46 - Применяем трансформацию для поворота пространства
31:11 - Выносим код в функцию Draw_Brick_Letter()
32:15 - Поворачиваем пространство на произвольный угол
34:28 - Применяем функцию косинуса cos()
36:35 - Применяем функцию синуса sin()
38:47 - Поворачиваем букву разные углы
43:19 - Выводим каждый кадр кирпича отдельно
44:51 - Модифицируем матрицу поворота
47:02 - Смещаем центр поворота
50:46 - Рисуем обратную сторону буквы
53:29 - Динамически меняем толщину обратной стороны
57:37 - Функции для получения абсолютных значений abs() и fabs()
59:23 - Функция округления round()
01:01:19 - Рефакторим код для повышения читабельности
01:04:32 - Выводим букву в положении плашмя
01:11:27 - Popcorn_05.zip: пишем код
01:11:49 - Добавим тип кирпича, из которого выпала буква
01:14:14 - Добавим передний и задний фон буквы
01:18:45 - Выносим копипаст в функцию Set_Brick_Letter_Colors()
01:24:30 - Исправляем угол поворота буквы
01:30:16 - Рисуем саму букву
01:36:27 - Вводим тип буквы ELetter_Type
Popcorn_05.zip - https://drive.google.com/file/d/1dXRMhJe2dmKM-QB6issaOfcRoo5VkDak
Popcorn_06.zip - https://drive.google.com/file/d/1eEpEE2fPB0BTGsE-8vcjAu61PQz0xl-N

Часть #11. Прототип 06[править]

00:00 - Прототип 06
01:06 - Обработка нажатий клавиш
02:31 - Сообщение WM_KEYDOWN
05:34 - Пишем код обработки трёх клавиш
08:22 - Создаём функцию On_Key_Down()
10:00 - Вводим тип EKey_Type
13:47 - Обрабатываем нажатия клавиш
20:29 - Функция для перерисовки области окна InvalidateRect()
23:22 - Передаём хэндл окна через функцию Init_Engine()
25:18 - Пишем код функции Redraw_Platform()
30:04 - Очищаем область окна перед выводом платформы
31:18 - Создаём фоновую кисть и карандаш
34:15 - Описываем текущую и предыдущую области платформы
40:05 - Задаём минимальную позицию платформы
42:05 - Перерисовка той части окна, которая изменилась
43:05 - Функция BeginPaint()
44:19 - Передаём описание области рисования в функцию Draw_Frame()
45:37 - Функция IntersectRect()
48:47 - Выводим уровень только если нужно
54:14 - Popcorn_07.zip: пишем код
Popcorn_07.zip - https://drive.google.com/file/d/11IjaxpdDvmhNoiEEag0yoixBobxRZTAm

Часть #12. Прототип 07[править]

00:00 - Прототип 07
00:58 - Установка таймера функцией SetTimer()
03:08 - Константа WM_USER
05:36 - Обработка сообщения WM_TIMER
07:48 - Добавляем функцию On_Timer()
11:55 - Рисуем шарик, разбивающий кирпичи
23:16 - Выносим код в функцию Draw_Ball()
24:00 - "Симметрия кода"
25:07 - Добавляем описание области шарика Ball_Rect
27:19 - Добавляем функцию Move_Ball()
30:26 - Исправляем движение шарика
34:12 - Задаём движение шарика углом и скоростью
36:15 - Что такое синус и косинус
39:08 - Применяем синус и косинус для движения шарика
40:53 - Делаем отражение шарика от стенок уровня
58:48 - Popcorn_08.zip: пишем код
Popcorn_08.zip - https://drive.google.com/file/d/1ArJbECLdT4k1D3kYZ4EuMhfagdVOLIpl

Часть #13. Прототип 08[править]

00:00 - Прототип 08
01:12 - Рисуем рамку вокруг уровня
02:32 - Декомпозируем рамку на элементы
06:20 - Выводим элемент рамки
16:02 - Выводим левую сторону рамки
18:53 - Выводим правую сторону рамки
21:10 - Выносим код в функцию Draw_Bounds()
22:56 - Выводим верхнюю сторону рамки
28:40 - Ограничиваем рамкой перемещение платформы
35:44 - Epic Fail программиста
37:36 - Корректируем ограничение движения платформы
40:17 - Исправляем позиции отражения шарика
46:11 - Делаем отражение шарика от платформы
49:58 - Отражение шарика от кирпичей
54:16 - Исключение (exception) при выходе за пределы массива
55:20 - Выносим код в функцию Check_Level_Brick_Hit()
57:32 - Popcorn_09.zip: пишем код
Popcorn_09.zip - https://drive.google.com/file/d/1kbA3do6LRgXI9eQv3-TbdA2x015Gee-6

Часть #14. Классы[править]

00:00 - Классы в С++
01:27 - Обновляем Visual Studio
03:18 - Начинаем изучать классы в С++
04:38 - Глобальные функции и данные
05:29 - Создаём класс как коллекцию функций и данных
07:33 - Класс как сущность
11:17 - Класс как тип
12:12 - Экземпляры класса (объекты)
12:50 - Методы класса
15:03 - Что такое класс
16:02 - Особенность классов: представление данных и функций
17:52 - Особенность классов: объявление методов (функций класса)
19:05 - Особенность классов: секции доступа
19:34 - Публичная секция (public)
20:49 - Приватная секция (private)
24:04 - Зачем нужен прототип проекта
25:24 - Создаём класс нашего игрового движка - Engine
28:30 - Преобразовываем глобальные функции в методы класса
41:27 - Переносим прочие типы в заголовочный файл
43:06 - Переносим переменные и константы в класс
44:31 - Делаем константы статическими
46:52 - Статические члены данных
50:39 - Конструктор класса
52:05 - Переносим инициализацию переменных в конструктор
52:52 - Список инициализации
55:48 - Popcorn_10.zip: пишем код
Popcorn_10.zip - https://drive.google.com/file/d/1VcucM1FN4EISGNZx5LNwQgTm58obUYzQ

Часть #15. Рефакторинг сложного класса[править]

00:00 - Рефакторинг сложного класса
01:42 - Форматирование ссылок и указателей
03:58 - Настройка выравнивания кода в Visual Studio
06:13 - Выносим сущность шарика в класс ABall
10:50 - Исправляем ошибки, разделяя сущности движка и шарика
29:15 - Указатель на себя this
33:44 - Переносим часть членов класса ABall в приватную секцию
35:53 - Popcorn_11.zip: пишем код
36:22 - Выносим сущность уровня в класс ALevel
55:07 - Рефакторим метод Init_Engine()
01:02:11 - Статический метод
01:05:21 - Popcorn_12.zip: пишем код
01:05:39 - Создаём функцию инициализации ABall::Init()
01:07:57 - Выносим сущность платформы в класс AsPlatform
01:29:33 - Рефакторим имена членов данных платформы
01:32:38 - Popcorn_13.zip: пишем код
01:32:50 - Выносим сущность рамки в класс AsBorder
01:40:26 - Рефакторим имена методов
01:43:00 - Подводим итог рефакторинга класса движка
01:43:46 - Popcorn_14.zip: пишем код
Popcorn_11.zip https://drive.google.com/file/d/1eXtYryEBoauBjJXsL-tgI3G92smqqwzS
Popcorn_12.zip https://drive.google.com/file/d/1txBSML3A1G6My_xdoqaYK6I7uEIBuAsF
Popcorn_13.zip https://drive.google.com/file/d/1UfHvgeoV8hvOQfehlK0VU4mG56oMnWaV
Popcorn_14.zip https://drive.google.com/file/d/17k7rLh-yEYs8HCAzkHAZSQOuS-iVRUfg

Часть #16. Выносим классы в отдельные файлы[править]

00:00 - Выносим классы в отдельные файлы
02:30 - Выносим класс AsBorder в отдельную пару файлов
02:54 - Создаём фильтр для пары файлов
05:02 - Добавляем пару файлов Border.cpp и Border.h
06:06 - Переносим код класса AsBorder в новую пару файлов
07:57 - Как получается циклическая зависимость файлов
10:27 - Избавляемся от циклической зависимости: заменяем типы
13:00 - Избавляемся от циклической зависимости: выносим члены в общий класс
16:27 - Переносим статический метод в другой класс
19:48 - Исправляем предупреждения (warnings)
33:05 - Popcorn_15.zip: пишем код
33:28 - Выносим класс ALevel в свою пару файлов
39:03 - Выносим класс AsConfig в свою пару файлов
41:21 - Зачем нужна директива #pragma once
43:51 - Popcorn_16.zip: пишем код
44:04 - Выносим класс ABall в свою пару файлов
50:13 - Переносим общие константы из классов в AsConfig
59:47 - Popcorn_17.zip: пишем код
59:59 - Выносим класс AsPlatform в свою пару файлов
01:04:22 - Проверяем порядок следования методов в *.h и *.cpp
01:07:31 - Сохраняем все исходные файлы в формате Юникода
01:10:41 - Popcorn_18.zip: пишем код
Popcorn_15.zip https://drive.google.com/file/d/1wv-FJwiWcKF_mhlRi8ACt5AwkL7vUB7Z
Popcorn_16.zip https://drive.google.com/file/d/1Wbc2ai6o9cusZ3k9iQdYjye1APbY5xAH
Popcorn_17.zip https://drive.google.com/file/d/1x6UM5NEjoQ4ZLxUiBszeji6QCW3aIBDI
Popcorn_18.zip https://drive.google.com/file/d/1NZMXjG1-nsejkMcymrRRzQYqAHnyfjHh

Часть #17. Кодстайл AStyle[править]

Все части в плейлисте : https://www.youtube.com/playlist?list...

Часть #18. Анимация активного кирпича, этап 1[править]

00:00 - Анимация активного кирпича, этап 1
00:34 - Исправляем форматирование в Main.cpp
02:59 - Исправляем параметры функции wWinMain()
04:43 - Корректируем функцию WndProc()
05:50 - Переносим массив Level_01 в класс ALevel
07:17 - Исправляем имя константы Y_Pos
08:45 - Переносим в приватную секцию члены данных класса AsEngine и ABall
10:27 - Инкапсуляция, как один из трёх главных принципов ООП
12:45 - Определение инкапсуляции
13:51 - Выбираем в оригинальной игре следующую фичу, которую мы воспроизведём
17:49 - Начинаем рисовать полутоновый кирпич
21:41 - Создаём кисть и карандаш
24:49 - Создаём формулу изменения оттенка
26:50 - Особенности целочисленной арифметики
28:25 - Создаём новый оттенок на каждый кадр
32:44 - Выносим код рисования угасающего кирпича в метод нового класса AActive_Brick
38:46 - Привязываем анимацию к таймеру
45:12 - Ограничиваем шаги угасания
46:25 - Вводим константу FPS
48:08 - Выносим класс AActive_Brick в отдельную пару файлов
52:23 - Popcorn_19.zip: пишем код
Popcorn_19.zip - https://drive.google.com/file/d/1eDopDzN2IU9C2taxV31tk2ORVK05QttI

Часть #19. Анимация активного кирпича, этап 2[править]

00:00 - Анимация активного кирпича, этап 2
01:52 - Выносим компоненты цвета в конфиг
04:23 - Инкапсулируем компоненты цвета в класс AColor
11:07 - Передаём ссылку на объект цвета вместо компонентов R,G,B
12:57 - Перегруженные функции
15:23 - Используем цвет AColor для создания полутоновых оттенков
18:01 - Делаем выбор полутоновых кистей и карандашей из массива
23:06 - Создаём статические массивы карандашей и кистей
26:24 - Задаём полутоновые цвета в методе Setup_Colors()
30:55 - Добавляем тип активного кирпича
32:43 - Выводим угасание активного кирпича
35:05 - Исправляем угасание - не до чёрного, а до фонового цвета
37:12 - Добавляем константу фонового цвета BG_Color
39:51 - Исправляем вывод последнего шага угасания
41:27 - Ставим условную контрольную точку для отладки угасания
46:22 - Создаём метод AsConfig::Setup_Colors()
48:47 - Выносим фоновую кисть и карандаш в конфиг
50:20 - Убираем лишние параметры из методов Draw()
54:46 - Выносим код в метод Get_Fading_Channel()
58:59 - Выносим код в метод Get_Fading_Color()
01:05:28 - Popcorn_20.zip: пишем код
Popcorn_20.zip - https://drive.google.com/file/d/14TPFdy97yT4uA2AL7humSCQllozNQH5m

Часть #20. Пишем план разработки программы[править]

00:00 - Пишем план разработки программы
00:41 - Два подхода к разработке программ: любительский и профессиональный
02:22 - Что такое план разработки
03:00 - 2 начальника в команде
04:24 - Для кого мы пишем код
05:34 - Начинаем писать наш план, анализируя оригинальную игру
26:03 - Выбираем способ получения информации о кирпичах и уровнях
27:18 - Смотрим документацию по игре в интернете
29:33 - Запускаем игру в DOSBox
32:05 - Делаем видеозахват 4-го уровня
36:33 - Записываем демо 6-го и 22-го уровней
38:24 - Ищем потерянный видеофайл
40:22 - Записываем демо 7-го уровня
42:44 - Записываем демо 3-го уровня
44:10 - Записываем демо 9-го и 10-го уровней
45:35 - Записываем демо 5-го уровня
47:17 - Записываем демо 8-го уровня
49:14 - Дорабатываем план, детализируя описания кирпичей
55:38 - Описываем падающие буквы
01:01:07 - Записываем анимацию расширяющейся платформы
01:04:28 - Продолжаем описывать падающие буквы
01:12:47 - Составляем список монстров
01:15:27 - Подводим итог составления плана
01:17:45 - Исправляем грамматические ошибки
01:20:30 - Создаём архив
01:21:40 - Popcorn_21.zip: пишем план
Popcorn_21.zip - https://drive.google.com/file/d/1XX25pJjlDCasUYsnSby0N328M4APCLpw

Часть #21. Анимация расплавления платформы[править]

00:00 - Анимация расплавления платформы
00:42 - Делаем раскадровку анимации расплавления из оригинальной игры
03:27 - Анализируем анимацию и решаем, как мы будем её воспроизводить
06:02 - Вводим состояния платформы EPlatform_State
09:30 - Добавляем метод Act() для перерисовки платформы
12:56 - Рефакторим метод Draw() для рисования разных состояний платформы
16:59 - Смещаем область платформы вниз
23:12 - Ищем в MSDN функции для чтения и установки пикселей
26:41 - Анимация платформы в первом приближении
35:08 - Стираем фоном красные следы
51:46 - Смещаем столбцы платформы на случайное расстояние
57:27 - Выносим код в AsConfig::Rand()
01:00:58 - Убираем оставшиеся точки
01:02:34 - Замедляем анимацию, добавив подсчёт кадров в Current_Timer_Tick
01:07:35 - Сохраняем позицию смещения каждого столбца платформы
01:12:42 - Оператор sizeof
01:19:24 - Popcorn_22.zip: пишем код
Popcorn_22.zip - https://drive.google.com/file/d/1yO7NoAbFN3ssJTjqxzNX8GdEiJv3d4rg

Часть #22. Анимация выкатывающейся платформы[править]

00:00 - Анимация выкатывающейся платформы
00:28 - Включаю панель Solution Explorer
01:44 - Исправляю угасание активного кирпича
03:58 - Исправляем отражение мячика от нижней грани уровня
06:51 - Исправляем формулу отражения от нижней грани
07:40 - Начинаем делать анимацию выкатывающейся платформы
08:40 - Делаю раскадровку оригинальной анимации
13:36 - Добавим новое состояние платформы - EPS_Roll_In
14:01 - Выносим код установки состояния в метод Set_State()
17:37 - Добавляем функцию Draw_Roll_In_State()
20:32 - Рисуем разделительную линию
25:18 - Рисуем блик
29:08 - Поворачиваем разделительную линию
39:36 - Анимируем поворот шарика платформы
42:51 - Очищаем фоном предыдущее место платформы
44:44 - Корректируем размер Platform_Rect для шарообразной платформы
46:35 - Зацикливаем шаг поворота платформы
48:01 - Переносим стартовую позицию платформы на правый край уровня
49:02 - Останавливаем мячик посреди уровня
56:10 - Корректируем стартовую позицию платформы
57:41 - Корректируем начальный угол поворота
59:07 - Замедляем движение платформы в 3 раза
01:01:48 - Особенности графического режима GM_ADVANCED
01:05:04 - Рисуем расширяющуюся платформу
01:09:38 - Корректируем область перерисовки платформы
01:11:10 - Делаем плавное расширение платформы
01:16:17 - Переносим Hwnd из движка в конфиг
01:20:29 - Центрируем платформу
01:23:15 - Сохраняем срез архива под новым именем
01:25:14 - Popcorn_part_22.zip: пишем код
Popcorn_part_22.zip - https://drive.google.com/file/d/1tAWNJq6-0YPujZ-u31mjmaHmsM-4Dw7p

Часть #23. Состояния игры и мячика[править]

00:00 - Состояния игры и мячика
00:36 - Увеличиваем количество оборотов шарика платформы с 8 до 16
02:27 - Описываем состояния игры
07:16 - Зависимость действий по таймеру от текущего состояния игры
09:20 - Описываем различные состояния мячика
11:20 - Ставим мячик на платформу
17:33 - Исправляем отражение мячика от нижней грани уровня
19:30 - Делаем потерю мячика при выходе за пределы уровня
25:12 - Заменяем координаты мячика с целого на вещественный тип
31:15 - Меняем состояние мячика и платформы в зависимости от состояния игры
34:29 - Обрабатываем состояние игры при утрате мячика
38:54 - Переводим растаявшую платформу в состояние EPS_Missing
43:21 - Обрабатываем состояние игры EGS_Restart_Level
49:02 - Обрабатываем нажатие пробела
53:18 - Добавим метод Set_State() для установки состояния мячика
56:56 - Добавим метод Get_State()
58:28 - Добавим метод Redraw_Ball()
01:00:51 - Поставим платформу в состояние EPS_Ready
01:02:32 - Popcorn_part_23.zip: пишем код
Popcorn_part_23.zip - https://drive.google.com/file/d/1JIXBbJ5gc6LLaFrlzQ_W0gncDj_3bhdW

Часть #24. Правильное движение мячика[править]

00:00 - Правильное движение мячика
01:38 - Переходим к представлению мячика как окружности с центром и радиусом
06:18 - Делаем серию телепортаций мячика вместо одной
09:17 - Корректируем координаты взаимодействий с препятствиями
11:42 - Заменяем коррекцию пути на регистрацию столкновений
16:21 - Выносим код в метод Check_Border_Hit()
18:59 - Переносим переменную Has_Floor из уровня в конфиг
20:42 - Переносим метод Check_Border_Hit() из класса мячика в класс рамки
26:52 - Наследуем класс рамки от интерфейса AHit_Checker
28:50 - Базовый и производный классы
29:29 - Виртуальный метод
30:57 - Абстрактный метод, абстрактный класс или интерфейс
33:11 - 2-й принцип ООП: наследование
33:36 - 3-й принцип ООП: полиморфизм
34:14 - Заменяем указатель на рамку указателем на интерфейс
40:03 - Накапливаем оставшееся расстояние в переменной Rest_Distance
42:20 - Popcorn_part_24_1.zip: пишем код
42:40 - Обработка попаданий по кирпичам через интерфейс AHit_Checker
47:40 - Устраняем циклическую зависимость мячика и уровня
48:48 - Заменяем указатель на ALevel на указатель типа AHit_Checker
51:11 - Отключаем warning C26451 через набор правил компиляции
57:59 - Обработка попаданий по кирпичам
59:09 - Накапливаем булевые результаты в одной переменной got_hit
01:01:40 - Делаем столкновение с платформой через интерфейс AHit_Checker
01:06:36 - Добавим вызов обработки столкновения с платформой
01:09:21 - Переносим указатели в массив Hit_Checkers
01:14:44 - Добавляем хит-чекеры через метод Add_Hit_Checker()
01:19:43 - Popcorn_part_24_2.zip: пишем код
Popcorn_part_24_1.zip - https://drive.google.com/file/d/1eW71Y2o_rNbpCFTAOkHGwziVtYqyfvm1
Popcorn_part_24_2.zip - https://drive.google.com/file/d/1JYA_l8W6Nsdicl33-DfA16HNTc_LQ58q

Часть #25. Столкновение с кирпичами, этап 1[править]

00:00 - Столкновение с кирпичами, этап 1
00:24 - Устанавливаем правильное направление движения мячика
01:04 - Исправляем отражение от нижней грани кирпича
04:42 - Убираем константу AsConfig::Ball_Size
05:45 - Отключаем warning C26451
07:35 - Строим модель процесса отражения мячика от кирпича
12:54 - Ищем уравнение окружности
14:26 - Переписываем уравнение окружности относительно X и Y
18:42 - Применяем уравнение окружности к нашей модели отражения
26:36 - Выносим проверку столкновения в функцию Hit_Circle_On_Line()
33:41 - Делаем отражение от верхней грани кирпича
36:52 - Проверяем угол падения перед проверкой столкновения
39:56 - Исправляем позицию отражения в brick_top_y
41:30 - Разбираемся, почему мячик летит сквозь кирпичи
44:01 - Создаём свойство для Ball_Direction
51:28 - Добавим метод ABall::Reflect()
55:31 - Popcorn_part_25.zip: пишем код
Popcorn_part_25.zip - https://drive.google.com/file/d/1sbyUZ-FDf6o-B8TPKsQTZuybkkoLg_5N

Часть #26. Столкновение с кирпичами, этап 2[править]

00:00 - Столкновение с кирпичами, этап 2
00:16 - Делаем отражение от боковых стенок кирпича
05:02 - Убираем кирпичи уровня, чтобы проверить отражения
10:25 - Выбираем, от какой грани следует отражаться в первую очередь
14:19 - Переносим код в функции Check_Vertical_Hit() и Check_Horizontal_Hit()
19:35 - Запрещаем отражение, если ему мешает кирпич
26:24 - Переносим параметры текущего кирпича в члены класса
31:09 - Выносим код в метод Is_Check_Horizontal_First()
33:43 - Исключаем из проверки лишние кирпичи
42:52 - Добавляем тестовый уровень
44:40 - Создаём текущий уровень Current_Level
46:18 - Очищаем массив функцией memset()
48:17 - Создаём метод для установки текущего уровня Set_Current_Level()
52:11 - Добавим состояние тестирования движка EGS_Test_Ball
54:29 - Добавим в класс ABall методы Set_For_Test() и Is_Test_Finished()
01:02:49 - Исправляем движение мячика в тестовом режиме
01:06:12 - Добавим в метод Set_State() параметр y_pos со значением по умолчанию
01:10:57 - Подбираем координаты мячика для начала теста
01:13:02 - Ищем причину неправильного отражения мячика
01:14:59 - Строим другую модель отражения мячика
01:16:15 - Применяем обновлённую модель отражения
01:19:03 - Исправляем метод Check_Hit()
01:23:41 - Проводим тест ещё раз
01:26:46 - Ещё одна ошибка при отражении
01:31:12 - Проводим последнюю серию тестов
01:33:54 - Убираем лишний код
01:37:19 - Popcorn_part_26.zip: пишем код
Popcorn_part_26.zip - https://drive.google.com/file/d/1eiuAhUkvt2-5yeLWuhJHZvLk2pzWn8Rc

Часть #27. Отражение мячика от платформы, этап 1[править]

02:20 - Исправляем ошибку отражения мячика от пустоты
05:14 - Выясняем причину залипания мячика возле платформы
08:01 - Ищем подходящую задачу в плане разработки
10:17 - Декомпозируем задачу отражения мячика от платформы на подзадачи
12:02 - Пишем код отражения от центральной части платформы
14:34 - Применяем код отражения от кирпичей из Check_Vertical_Hit()
16:30 - Описываем отрезок верхней части платформы
22:08 - Добавим метод Is_Moving_Up()
25:07 - Применяем метод Is_Moving_Up() в классе ALevel вместо условий
26:30 - Добавим по аналогии метод Is_Moving_Left()
29:36 - Переносим метод Hit_Circle_On_Line() в класс AHit_Checker
36:33 - Делаем отражение от нижней грани
38:24 - Замедляем мячик для анализа стирания при отражении
40:46 - Меняем порядок вывода мячика в кадре
41:52 - Popcorn_part_27.zip: пишем код
Popcorn_part_27.zip - https://drive.google.com/file/d/1Otx3DofFgh8qePuSRUQM8SctwsCHA9h2

Часть #28. Отражение мячика от платформы, этап 2[править]

00:00 - Отражение мячика от платформы, этап 2
00:17 - Строим модель отражения двух окружностей
18:41 - Вычисляем расстояние между центрами окружностей
26:06 - Выносим в конфиг константу Moving_Step_Size
28:47 - Вычисляем арктангенс функцией atan2()
32:20 - Пишем код для углов альфа и гамма, и назначаем мячику новый угол
36:51 - Воспроизводим в тестовом режиме ошибку отражения
44:39 - Строим модель для определения "улетающего" мячика
48:16 - Добавим проверку диапазона относительного угла отражения
51:12 - Тестируем отражение в другом направлении
54:55 - Проверяем эффект скольжения мячика вдоль платформы
58:22 - Тестируем отражение при вертикальном смещении стартовой точки
01:01:15 - Проводим финальную серию тестов
01:02:07 - Выносим код в метод Reflect_On_Circle()
01:09:06 - Тестируем отражение от правого шарика платформы
01:10:31 - Рефакторим код отражения от центральной части платформы
01:12:41 - Проверяем отражение мячика от правой грани
01:14:20 - Уменьшаем радиус мячика на 1/6 пикселя
01:17:14 - Popcorn_part_28.zip: пишем код
Popcorn_part_28.zip - https://drive.google.com/file/d/1TUQEpT532JksMpQUlSaiImRFIfzjn9CP

Часть #29. Применяем активные кирпичи[править]

00:00 - Применяем активные кирпичи
01:28 - Составляем список активных кирпичей в нашей игре
07:00 - Описываем задачу анимации активных кирпичей
08:48 - Добавляем функцию Add_Active_Brick()
10:41 - Создаём новый объект активного кирпича оператором new
14:16 - Определяем тип кирпича, в который попал мячик
15:49 - Создаём массив активных кирпичей
17:16 - Заполнение массива активными кирпичами
20:00 - Ограничиваем создание кирпичей только для синих и красных
22:14 - Выводим все активные кирпичи уровня
23:22 - Анимируем все активные кирпичи уровня
26:10 - Добавим активному кирпичу его координаты в уровне
28:56 - Обнуляем массив Active_Bricks при инициализации уровня
30:03 - Добавляем новый активный кирпич на первое свободное место в массиве
34:49 - Удаляем активные кирпичи оператором delete
37:55 - Добавляем метод Is_Finished()
42:00 - Popcorn_part_29.zip: пишем код
Popcorn_part_29.zip - https://drive.google.com/file/d/1u8t_IM0JWzd44cROBz2f0CpsR8Xz6B49

Часть #30. Выпадение буквы из кирпича[править]

00:00 - Выпадение буквы из кирпича
00:17 - Исправляем циклы вывода и анимации активных кирпичей
02:07 - Анализируем сценарии, при которых буквы выпадают из кирпичей
03:11 - Добавляем метод On_Hit()
06:31 - Выбираем случайным образом действие при разбитии кирпича
11:10 - Создаём класс падающей буквы AFalling_Letter
14:42 - Создаём массив падающих букв Falling_Letters
16:33 - Добавляем очередную падающую букву в массив
20:57 - Выносим код в метод Add_Falling_Letter()
24:34 - Переносим метод Draw_Brick_Letter() из класса ALevel в AFalling_Letter
26:43 - Выносим в конфиг кисти и карандаши для рисования кирпичей
32:22 - Popcorn_part_30_1.zip: пишем код
32:36 - Копируем методы Act(), Draw() и Is_Finished() из класса активного кирпича в класс падающей буквы
36:13 - Описываем прямоугольником Letter_Cell область падающей буквы
39:25 - Дорабатываем метод Draw() падающей буквы
44:44 - Исправляем создание падающей буквы в методе Add_Falling_Letter()
47:11 - Обнуляем массив Falling_Letters в методе Init()
47:44 - Выводим все падающие буквы
48:42 - Вызываем анимацию всех падающих букв
52:08 - Анимируем падение буквы
53:40 - Очищаем фон под падающей буквой
59:01 - Исправляем баг с остановкой падающей буквы
01:02:37 - Исправляем баг с белыми точками после мячика
01:13:35 - Анимируем вращение буквы
01:16:06 - Замедляем вращение буквы в 4 раза
01:19:03 - Смещаем начало анимации на 2 кадра вперёд
01:21:27 - Дописываем метод Is_Finished()
01:23:59 - Рисуем фоном отсутствующие кирпичи в Draw_Brick()
01:25:32 - Стираем из уровня разбитый кирпич
01:27:06 - Popcorn_part_30_2.zip: пишем код
Popcorn_part_30_1.zip - https://drive.google.com/file/d/1YHvjwFpuXNfMmhlhSdPZMHavxuaNst0o
Popcorn_part_30_2.zip - https://drive.google.com/file/d/17Lra3Vz9bHqlgRcCArEMzFCV-KOdnFqI

Часть #31. Новое расплавление платформы[править]

00:00 - Новое расплавление платформы
00:19 - Анализируем текущее решение для расплавления платформы
01:51 - Замеряем время, затрачиваемое на расплавление
06:41 - Изменяем время более точно
08:38 - Рассматриваем модель вывода платформы линиями
11:40 - Пишем тест для замера производительности
15:06 - Измеряем время для нового алгоритма
18:57 - Сохраняем исходное изображение платформы
21:05 - Создаём массив в куче
26:45 - Сохраняем пиксели изображения в массив
29:13 - Добавим деструктор класса для удаления массива из кучи
33:27 - Пишем алгоритм, рисующий платформу цветными штрихами
38:38 - Пишем метод Get_Platform_Image_Stroke_Color()
48:07 - Определяем карандаш, соответствующий цвету штриха
51:44 - Добавим 3 переменных для описания цвета карандашей платформы
55:04 - Добавим метод Get_RGB()
57:56 - Инициализируем значения цветов платформы
59:41 - Проверяем в отладчике, как снимается образ платформы
01:02:24 - Смотрим в отладчике на содержимое области памяти
01:06:39 - Отлаживаем вывод цветных штрихов
01:12:25 - Замедляем анимацию расплавления, чтобы проверить правильность работы алгоритма
01:14:44 - Убираем оставшиеся красные следы
01:16:37 - Исправляем подпрыгивание платформы на первом кадре
01:18:56 - Убираем "пиксельный мусор"
01:20:25 - Убираем лишние комментарии и добавляем нужные
01:22:03 - Создаём кисти и карандаши платформы через цвет AColor
01:23:59 - Popcorn_part_31.zip: пишем код
Popcorn_part_31.zip - https://drive.google.com/file/d/1gZzmOuQ44cBBIcDHIRllq5s3yQdjcH9m

Часть #32. Оптимизация кирпичей[править]

00:00 - Оптимизация кирпичей
00:35 - Рисуем только те кирпичи, которые надо перерисовать
04:10 - Передаём в Draw_Brick() ссылку на RECT вместо пары координат
06:00 - Выносим метод Draw() в новый базовый класс AGraphics_Object
10:04 - Наследуем класс падающей буквы от AGraphics_Object
11:02 - Выносим код в метод Draw_Objects()
17:37 - Добавим в класс AGraphics_Object методы Act() и Is_Finished()
20:12 - Выношу код из Act() в Act_Objects()
23:30 - Исправляем координаты вывода кирпичей
25:05 - Исправляем анимацию тающих кирпичей
28:11 - Задаём последовательность случайных чисел функцией srand()
31:01 - Функции Windows для получения и преобразования системного времени
34:55 - Задаём значение псевдослучайной последовательности в методе Init_Engine()
38:51 - Ускоряем угасание кирпича до 1 секунды
40:18 - Popcorn_part_32_1.zip: пишем код
40:35 - Переносим код из On_Key_Down() в новый метод Move()
45:37 - Выносим класс падающей буквы в отдельную пару файлов
48:38 - Popcorn_part_32_2.zip: пишем код
Popcorn_part_32_1.zip - https://drive.google.com/file/d/1xZXEO9ZOxaFApzdyb2o_R60kmTCuXZEY
Popcorn_part_32_2.zip - https://drive.google.com/file/d/1myCK06OaPJLUI8bd2hhBniZqPFePpEWp

Часть #33. Ловим буквы платформой[править]

00:00 - Ловим буквы платформой
01:13 - Отмечаем в плане сделанные задачи
04:02 - Рассматриваем модель столкновения букв с платформой
05:14 - Описываем способ реализации такого столкновения
07:38 - Выносим код из On_Timer() в Act()
08:36 - Пишем "каркас" алгоритма обработки столкновений
11:24 - Добавим метод On_Falling_Letter()
12:19 - Добавим метод Get_Next_Falling_Letter()
18:40 - Добавим метод Hit_By()
21:02 - Добавим метод Get_Letter_Cell();
25:22 - Проверяем в отладчике работу алгоритма
29:53 - Добавим метод Finalize()
31:41 - Финализируем падающую букву в методе рисования
32:33 - Корректируем методы Is_Finished() и Act()
34:52 - Предотвращаем смещение платформы в момент расплавления
36:14 - Описание состояний падающей буквы в EFalling_Letter_State
38:56 - Заменяем использование Got_Hit и Finished на состояние
41:03 - Исправляем имя класса ALevel на AsLevel
42:58 - Popcorn_part_33.zip: пишем код
Popcorn_part_33.zip - https://drive.google.com/file/d/16Nu84JOOwbLqSa4mzECwE5hmkxTR3LyM

Часть #34. Падающие буквы, этап 1[править]

00:00 - Падающие буквы, этап 1
00:19 - Возвращаем анимацию выкатывающейся платформы
03:37 - Исправляем ошибку маленького размера расплавляющейся платформы
07:57 - Исправляем размер разделителя шарика платформы
10:10 - Отмечаем сделанные задачи в плане разработки
11:14 - Строим в перечислении список всех падающих букв
14:57 - Выводим раскадровку всех ракурсов падающей буквы
23:08 - Исправим позицию вывода падающих букв
29:56 - Рисуем букву "И"
35:32 - Исправляем матрицу поворота буквы
45:37 - Рефакторим метод Add_Falling_Letter()
47:24 - Выводим букву "Ж"
55:33 - Добавим метод Draw_Line()
58:40 - Применим Draw_Line() для рисования буквы "Ж"
01:00:46 - Рисуем букву "И" методом Draw_Line()
01:02:21 - Выносим константу Brick_Half_Height
01:03:34 - Popcorn_part_34.zip: пишем код
Popcorn_part_34.zip - https://drive.google.com/file/d/10D0wnGdptlPkrkrZ2Z87_CnxNjqEoV1H

Часть #35. Падающие буквы, этап 2[править]

00:00 - Падающие буквы, этап 2
00:26 - Рисуем букву "М"
03:30 - Добавим метод Draw_Line_To()
04:19 - Применим Draw_Line_To() для рисования букв "М" и "И"
06:04 - Рисуем букву "С" методом Draw_C()
15:12 - Рисуем букву "К"
17:15 - Рисуем букву "Ш"
19:13 - Рисуем букву "П"
20:33 - Рисуем букву "Л"
23:05 - Рисуем букву "Т"
24:29 - Рисуем символ "+"
26:28 - Делаем случайную генерацию типа падающей буквы
30:59 - Неравномерное распределение вероятности
36:18 - Вычисляем сумму всех "весов" падающих букв в методе Init()
38:54 - Добавим метод Get_Random_Letter_Type()
43:50 - Проверяем генерацию падающих букв в отладчике
48:09 - Popcorn_part_35.zip: пишем код
Popcorn_part_35.zip - https://drive.google.com/file/d/15ZOkvf_NXxSC4Z30jYdNMudaOFxEmI13

Часть #36. Неразбиваемый кирпич, этап 1[править]

00:00 - Неразбиваемый кирпич, этап 1
00:41 - Делаем раскадровку анимации неразбиваемого кирпича
04:31 - Добавим код анимации в метод Act() активного кирпича
07:20 - Создаём копипастом класс AActive_Brick_Unbreakable
10:55 - Делим функциональность активных кирпичей на 2 класса
17:16 - Выносим общую функциональность в класс AActive_Brick
19:16 - Переносим данные в секцию класса protected
20:54 - Добавим вызовы конструктора базового класса AActive_Brick
23:13 - Переносим конструктор AActive_Brick в защищённую секцию
25:01 - Удаляем объекты производных классов через указатель на базовый
28:48 - Смотрим в отладчике, как происходит удаление объекта
31:25 - Смотрим то же действие, но в машинных кодах
36:37 - Добавим деструктор AGraphics_Object
39:35 - Делаем деструктор AGraphics_Object виртуальным
45:45 - Создаём неразбиваемый кирпич в Add_Active_Brick()
48:06 - Исключения (exceptions) в С++
50:10 - Смотрим, как генерируется исключение оператором throw
52:06 - Перехватываем исключение операторами try/catch
55:27 - Как нужно использовать исключения
58:55 - Выбросим исключение в конструкторе AActive_Brick_Red_Blue
01:01:13 - Фабрика объектов
01:02:21 - Рисуем неразбиваемый кирпич в методе Draw_Brick()
01:05:43 - Popcorn_part_36.zip: пишем код
Popcorn_part_36.zip - https://drive.google.com/file/d/1WPKdOsrrZolssOlK9HnHDLIN83L48JI3

Часть #37. Рефакторинг цвета[править]

00:00 - Рефакторинг цвета
00:17 - Переносим выбор кисти и карандаша в метод AColor::Select()
04:14 - Инкапсулируем кисть и карандаш в класс AColor
07:02 - Переименуем White_Brick_Color в White_Color
08:08 - Применяем выбор цвета в методе Draw_Brick()
09:54 - Убираем кисти и карандаши из конфига
11:17 - Исправляем выбор фонового цвета
13:41 - Исправляем метод Set_Brick_Letters_Color()
19:18 - Исправляем использование фоновой кисти и карандаша
19:53 - Исправляем массивы оттенков цвета для угасающих кирпичей
21:46 - Добавляем конструктор по умолчанию для AColor
23:33 - Исправляем метод AActive_Brick_Red_Blue::Draw()
24:53 - Исправляем методы Get_Fading_Color() и Setup_Colors()
27:08 - Уберём методы Create_Pen_Brush()
27:35 - Исправляем использование цвета для мячика ABall
29:40 - Исправляем использование цвета для рамки AsBorder
32:29 - Исправляем использование цвета для платформы AsPlatform
39:16 - Удаляем из конфига карандаш Letter_Pen
40:28 - Удаляем метод конфига Setup_Colors()
41:07 - Проверяем порядок инициализации статических констант
43:49 - Меняем имена констант на Red_Color и Blue_Color
45:40 - Popcorn_part_37.zip: пишем код
Popcorn_part_37.zip - https://drive.google.com/file/d/11a--J1Zaia6i5kD1YiSJomVHNhAypQQp/view

Часть #38. Неразбиваемый кирпич, этап 2[править]

00:00 - Неразбиваемый кирпич, этап 2
00:28 - Начинаем рисовать анимацию неразбиваемого кирпича
01:51 - Выносим вызов функции RoundRect() в метод Round_Rect()
06:47 - Применяем Round_Rect() при выводе платформы
13:35 - Анализируем изображение блика на кирпиче
16:02 - Рисуем красный блик четырьмя линиями
21:28 - Ищем в MSDN функцию FloodFill()
23:22 - Применяем функцию FloodFill() для заливки области
25:40 - Рисуем блик одной толстой линией
32:39 - Регионы Windows
37:44 - Обрезаем регионом вывод блика на кирпиче
45:25 - Добавим толстый карандаш в класс AColor
50:33 - Добавим метод Select_Pen() для выбора толстого карандаша
53:26 - Добавим метод Get_Brush()
54:54 - Переносим регион в приватную секцию
56:35 - Делаем анимацию движения блика
01:01:18 - Переносим рисование кирпича в метод Draw_In_Level()
01:09:13 - Popcorn_part_38.zip: пишем код
Popcorn_part_38.zip - https://drive.google.com/file/d/1nPmrNqb14YAnaN4igdsttw2Gnft-0wKi

Часть #39. Многоразовый кирпич[править]

00:00 - Многоразовый кирпич
00:26 - Готовим изображения кирпичей на разных стадиях
06:39 - Создаём класс AActive_Brick_Multihit
11:09 - Переносим throw 13 в метод AsConfig::Throw()
15:09 - Вызываем рисование многоразовых кирпичей в методе Draw_Brick()
16:42 - Рисуем многоразовый кирпич в методе Draw_In_Level()
25:16 - Выносим рисование стадии в метод Draw_Stage()
27:55 - Рисуем стадии 2, 3 и 4
32:11 - Добавим активный многоразовый кирпич в Add_Active_Brick()
35:22 - Добавим метод Redraw_Brick()
39:32 - Анализируем анимацию вращающейся сотни
41:22 - Рисуем очередной кадр анимации "100" в методе Draw()
47:07 - Добавим цвет Letter_Color()
49:29 - Корректируем размер цифр в "100"
52:39 - Делаем вращение сотни
01:06:03 - Убираем разбитый кирпич из уровня
01:06:51 - Тестируем в движке попадание мячика по кирпичам
01:08:48 - Popcorn_part_39.zip: пишем код
Popcorn_part_39.zip - https://drive.google.com/file/d/1UzirYJTC9znxDigZjUSk59A79TYnPw9J

Часть #40. Мячик на парашюте, этап 1[править]

00:00 - Мячик на парашюте, этап 1
00:28 - Анализируем движение мячика на парашюте
02:49 - Выбираем архитектурное решение для рисования свёрнутого парашюта
06:32 - Анализируем оригинальное изображение свёрнутого парашюта
09:00 - Рисуем чертёж нашей версии парашюта
10:26 - Рисуем фрагмент парашюта в методе Draw_Parachute_In_Level()
12:51 - Создаём двухцветный цвет в классе AColor
16:46 - Рисуем пару сегментов
22:09 - Выносим код в метод Draw_Parachute_Part()
25:54 - "Ставим" мячик на парашют в AsLevel::On_Hit()
28:51 - Добавим метод Set_On_Parachute()
31:51 - Добавим состояние EBS_On_Parachute
32:59 - Добавим метод Draw_Parachute()
34:17 - Анализируем оригинальное изображение парашюта
35:29 - Добавим описание области парашюта Parachute_Rect
38:41 - Popcorn_part_40.zip: пишем код
Popcorn_part_40.zip - https://drive.google.com/file/d/1RLkZXpl1GbrQ3OonkuaZgAxJYZqNL28l

Часть #41. Мячик на парашюте, этап 2[править]

00:00 - Мячик на парашюте, этап 2
00:25 - Анализируем изображение парашюта
01:50 - Смотрим в MSDN на функцию Pie()
02:56 - Смотрим в MSDN на функцию Chord()
04:14 - Рисуем купол функцией Pie()
11:02 - Рисуем купол функцией Chord()
12:19 - Рисуем левую арку под куполом
18:12 - Рисуем остальные 2 арки
24:03 - Рисуем левую стропу
29:50 - Рисуем остальные стропы
34:25 - Делаем движение мячика на парашюте
41:40 - Отключаем парашют при попадании в платформу
44:59 - Добавим обработку EBS_Off_Parachute в ABall::Set_State()
45:44 - Добавим метод Redraw_Parachute()
47:05 - Снимаем состояние EBS_Off_Parachute в ABall::Draw()
48:35 - Добавим метод Clear_Parachute()
50:14 - Запретим устанавливать состояние EBS_On_Parachute в методе ABall::Set_State()
52:58 - Валидация переключения других состояний в ABall::Set_State()
54:40 - Делаем потерю мячика на парашюте
59:08 - Popcorn_part_41.zip: пишем код
Popcorn_part_41.zip - https://drive.google.com/file/d/1NiaKvFyc6G6RULIAieE8EHEUJX9tXkly

Часть #42. Телепорт, этап 1[править]

00:00 - Телепорт, этап 1
00:24 - Снимаем раскадровку анимации телепортации
03:24 - Создаём копипастом класс AActive_Brick_Teleport
06:33 - Рисуем кирпич телепортации на уровне в AsLevel::Draw_Brick()
08:28 - Анализируем изображение телепорта
09:25 - Рисуем кирпич телепортации в Draw_In_Level()
13:19 - Переносим Portal_Color в конфиг
16:05 - Переносим в конфиг цвета Blue_Highlight и Red_Highlight
18:53 - Корректируем размер и позицию портала
21:44 - Добавим активный кирпич в Add_Active_Brick()
23:17 - Добавим состояния мячика EBS_Teleporting
25:26 - Ставим мячик в центр кирпича
28:45 - Добавим обработку телепортации в ABall::Set_State()
31:31 - Устраняем лишнюю перерисовку мячика в ABall::Move()
33:05 - Рисуем анимацию телепорта
37:11 - Подключаем Ball.h к активным кирпичам
40:43 - Сплющиваем мячик в портале через Draw_Teleporting()
44:29 - Убираем лишнее рисование мячика в портале
46:26 - Стираем мячик, когда он переходит в режим телепортации
48:57 - Popcorn_part_42.zip: пишем код
Popcorn_part_42.zip - https://drive.google.com/file/d/1B9CPlqEztYCEZEkjZd9mQHfA_hG_SZIB

Часть #43. Телепорт, этап 2[править]

00:00 - Телепорт, этап 2
00:35 - Добавим указатель на телепорт назначения
02:54 - Выбираем координаты телепорта назначения и выносим код в метод Select_Destination_Teleport()
05:05 - Подсчитываем все телепорты на уровне
08:29 - Добавим структуру SPoint
10:05 - Создаём динамический массив позиций телепортов
13:19 - Добавляем в массив позиции телепортов
14:35 - Удаляем массив в деструкторе ~AsLevel()
15:59 - Проверяем выбор из массива координат телепорта назначения
21:19 - Выносим код в метод Add_New_Active_Brick()
24:43 - Добавим состояния телепорта ETeleport_State
26:46 - Делаем переход по состояниям в методе Act()
28:39 - Передаём мячик телепорту назначения
31:41 - Рисуем расширяющийся мячик в AActive_Brick_Teleport::Draw()
33:20 - Переносим код в метод Set_Ball()
36:38 - Добавим в активный кирпич Level_X и Level_Y
37:58 - Проверяем работу телепортации
44:40 - Выпускаем мячик из телепорта назначения и добавляем метод Get_Center()
47:14 - Исправляем состояние мячика в методе AActive_Brick_Teleport::Draw()
49:17 - Popcorn_part_43.zip: пишем код
Popcorn_part_43.zip - https://drive.google.com/file/d/1A0tchJQUkmyuNKUiirPw6cAd9I0dfuDk

Часть #44. Телепорт, этап 3[править]

00:00 - Телепорт, этап 3
00:21 - Выбираем телепорт назначения случайным образом
05:53 - Выносим код в Add_Active_Brick_Teleport()
09:24 - Определяем направление, с которого прилетел мячик
16:06 - Добавим описание направления в EDirection_Type
17:53 - Определяем направление, в котором должен улететь мячик
25:04 - Добавим в телепорт направление выпуска Release_Direction
26:48 - Выпускаем мячик в правильном направлении в AActive_Brick_Teleport::Act()
30:06 - Выносим код в Get_Brick_X_Pos() и Get_Brick_Y_Pos()
36:57 - Пробрасываем тип удара в метод Add_Active_Brick_Teleport()
38:15 - Проверяем выбор направления
40:24 - Получаем координаты телепорта назначения через Get_Level_Pos()
44:10 - Проверяем передачу мячика между телепортами
48:42 - Убираем мячик из телепорта
50:20 - Проверяем выход из телепорта
53:00 - Исправляем удаление графических объектов в Act_Objects()
55:23 - Исправляем метод AActive_Brick_Teleport::Is_Finished()
58:20 - Анализируем причину, по которой мячик сам возвращается в нижний телепорт
01:03:30 - Не отражаем мячик для телепортов
01:06:50 - Поворачиваем угол вместе с поворотом направления
01:10:32 - Проверим выбор направления выпуска мячика
01:12:03 - Popcorn_part_44.zip: пишем код
Popcorn_part_44.zip - https://drive.google.com/file/d/1unJ46fUz1jQtzXH1ewSeYJV9dxJdeToO

Часть #45. Реклама, этап 1[править]

00:00 - Реклама, этап 1
00:37 - Смотрим на баг, когда падающие буквы перетирают кирпичи
01:32 - Ставим мячик поближе на платформу
02:38 - Стираем все движущиеся объекты методом Clear_Objects()
04:17 - Добавим метод Clear() в класс AGraphics_Object
09:32 - Анализируем оригинальные кирпичи рекламы
11:41 - Создаём копипастом класс AActive_Brick_Ad
13:35 - Рисуем кирпич рекламы на уровне
16:54 - Рисуем блик на шарике
21:33 - Рисуем второй шарик кирпича
23:01 - Создаём активный кирпич в методе Create_Active_Brick()
24:46 - Добавим валидацию пределов уровня
28:52 - Снимаем раскадровку анимации прыгающего шарика
32:17 - Декомпозируем анимацию на элементы
36:35 - Создаём класс AAdvertisement, описывающий рекламу
42:44 - Добавим объект рекламы в уровень
46:58 - Popcorn_part_45_1.zip: пишем код
47:18 - Начинаем рисовать кадр анимации и задаём область рекламы
50:16 - Рисуем красный шарик
52:26 - Рисуем стол
57:32 - Заливаем стол белым цветом
01:00:17 - Рисуем борт стола
01:04:51 - Рисуем тень под шариком
01:08:28 - Меняем цвет каймы стола
01:12:06 - Корректируем размер стола
01:13:11 - Popcorn_part_45_2.zip: пишем код
Popcorn_part_45_1.zip - https://drive.google.com/file/d/1PpdzlRQjC0q8Eh0o79kmGclMQMlExf1i
Popcorn_part_45_2.zip - https://drive.google.com/file/d/1LPMSq3TPgh6SIRzJutHUGmAGDCY6585M

Часть #46. Реклама, этап 2[править]

00:00 - Реклама, этап 2
00:21 - Перенесём рекламу под кирпичи
03:57 - Заказываем перерисовку для кадра анимации
05:17 - Добавим указатель на рекламу в кирпич рекламы
07:53 - Добавим метод Show_Under_Brick()
09:00 - Удаляем разбитые кирпичи рекламы
11:59 - Добавим метод Has_Brick_At()
13:37 - Исправим вызов метода Draw_Brick()
15:50 - Стираем разбитый кирпич рекламы
17:42 - Заменяем разбитые кирпичи рекламы невидимыми - EBT_Invisible
21:12 - Создаём маску кирпичей, чтобы не рисовать рекламу, которую не видно
25:16 - Заполняем маску при разбивании кирпичей
28:06 - Заказываем перерисовку областей под маской
32:27 - Исправим метод AActive_Brick_Ad::Draw()
34:09 - Popcorn_part_46.zip: пишем код
Popcorn_part_46.zip - https://drive.google.com/file/d/1rGkFwMTPLkgCBKzI3VVBhvS3nBnZ8Gzd

Часть #47. Реклама, этап 3[править]

00:00 - Реклама, этап 3
00:17 - Используем регион обрезки для вывода рекламы
02:47 - Смотрим описания регионов в MSDN
07:26 - Добавим пустой регион Empty_Region
08:59 - Переделаем маску на массив регионов Brick_Regions
11:43 - Создаём регионы кирпичей в методе Show_Under_Brick()
14:50 - Удаляем регионы в деструкторе рекламы
16:27 - Смотрим в отладчике, как создаются регионы
18:51 - Переносим код очистки рекламы в метод Draw()
22:04 - Исследуем причину, по которой стол не залился белым цветом
23:36 - Смотрим в MSDN на функцию Polygon
25:02 - Рисуем стол функцией Polygon()
28:27 - Включаем видимость всей рекламы для отладки
30:46 - Рисуем блик на шарике
38:19 - Рисуем рамку вокруг рекламы
43:50 - Корректируем размер борта стола
45:59 - Popcorn_part_47.zip: пишем код
Popcorn_part_47.zip - https://drive.google.com/file/d/1P_ZvN0Cjnn4KtJgfkNxjwuTU6OecAw9G

Часть #48. Реклама, этап 4[править]

00:00 - Реклама, этап 4
00:17 - Переведём представление позиции шарика к X, Y и размерам
07:27 - Добавим вертикальное смещение шарика
10:14 - Ограничим движение шарика порогами
13:41 - Настраиваем пороги
16:41 - Ищем формулу для тела, брошенного вверх
20:02 - Применяем формулу при анимации шарика
25:06 - Плющим шарик при ударе
32:08 - Делаем движение тени под шариком
36:42 - Отключаем видимость всей рекламы при отладке
37:32 - Уменьшаем на 1 пиксель шарики рекламных кирпичей
40:35 - Popcorn_part_48.zip: пишем код
Popcorn_part_48.zip - https://drive.google.com/file/d/1RTSDqcTKPDkXvXyV9RRsD6fZJ7BrdYF7

Часть #49. Множество мячиков на уровне[править]

00:00 - Множество мячиков на уровне
02:12 - Смотрим анимацию растроения мячика
03:01 - Добавим массив мячиков вместо одного объекта
10:50 - Добавим новое состояние мячика EBS_Disabled
15:56 - Исправляем аргументы метода Set_State()
20:02 - Перенесём константу Start_Ball_Y_Pos в конфиг
21:47 - Сделаем обработку падающих букв в зависимости от их типа
23:50 - Ловим букву "Т"
25:45 - Popcorn_part_49_1.zip: пишем код
26:06 - Ставим на платформу первых 3 мячика
28:13 - Выносим код в метод Restart_Level()
29:32 - Выносим код в метод Play_Level()
30:31 - Переводим состояние игры в EGS_Lost_Ball только при утрате всех мячиков
34:47 - Останавливаем уровень методом AsLevel::Stop()
37:43 - Удаляем объекты в методе Draw(), когда нужно отменить всю анимацию
40:10 - Добавим метод Cancel_All_Activity()
41:14 - Добавим метод Delete_Objects()
43:47 - Popcorn_part_49_2.zip: пишем код
Popcorn_part_49_1.zip - https://drive.google.com/file/d/15rjfxeUMrmGZWycf2bAiOHcnhlvmaC8b
Popcorn_part_49_2.zip - https://drive.google.com/file/d/1sdiVcqYOuqCG3NGS7KvhG0QkCaNkq20F

Часть #50. Рефакторинг движения, этап 1[править]

00:00 - Рефакторинг движения, этап 1
00:35 - Воспроизводим ошибку с пролётом мячика через платформу
08:15 - Два вида событий, приводящих к смещению объектов в нашей игре
10:09 - Анализируем текущий способ обработки нажатия клавиш
13:00 - Делаем обработку события отжатия клавиш WM_KEYUP
14:53 - Рефакторим метод On_Key_Down()
16:40 - Добавим перечисление EPlatform_Moving_State
18:54 - Добавим в метод AsPlatform::Move() поддержку состояния движения платформы
21:18 - Добавим переменную для скорости платформы
23:27 - Переносим переменную X_Step в константы
24:18 - Смещаем платформу в методе Play_Level()
28:04 - Добавим в платформу метод Advance()
33:13 - Проверяем смещение платформы
36:18 - Добавим метод Get_Middle_Pos()
40:05 - Убираем красные следы, появившиеся из-за ошибки округления
45:13 - Выносим код в метод Get_Normal_Platform_Image()
51:21 - Проверяем попадание мячиков внутрь платформы
53:02 - Вынесем в конфиг константу D_Global_Scale
55:49 - Popcorn_part_50.zip: пишем код
Popcorn_part_50.zip - https://drive.google.com/file/d/1sqSHkNhW1tNpZXq75cwHLrbpVXJAt995

Часть #51. Рефакторинг движения, этап 2[править]

00:00 - Рефакторинг движения, этап 2
00:17 - Создаём класс AMover
03:54 - Наследуем класс платформы от AMover
05:40 - Добавим в движок массив Movers
07:49 - Смещаем все муверы в методе Play_Level()
11:00 - Добавим класс-контейнер для мячиков AsBall_Set
13:12 - Добавим метод AsBall_Set::Draw()
14:47 - Добавим метод Release_From_Platform()
17:34 - Добавим метод Set_On_Platform()
19:19 - Удалим метод Restart_Level()
20:22 - Вынесем код в метод Advance_Mover()
21:45 - Вынесем код в метод All_Balls_Are_Lost()
25:13 - Сделаем смещение всех мячиков в методе Play_Level()
26:58 - Popcorn_part_51_1.zip: пишем код
27:19 - Отнаследуем класс AsBall_Set от AMover
28:59 - Преобразуем метод Move() мячика в метод Advance()
36:39 - Добавим метод Get_Speed() в платформу
38:47 - Добавим метод Get_Speed() в набор мячиков
42:06 - Исправляем ошибку, при которой мячики не рисуются
44:44 - Добавим методы Begin_Movement() и Finish_Movement() в платформу
46:12 - Добавим методы Begin_Movement() и Finish_Movement() в набор мячиков
46:59 - Отнаследуем класс мячика от AMover
49:39 - Переносим класс AMover в файл Ball.h
52:29 - Начинаем и заканчиваем движения для кадра в Advance_Movers()
53:31 - Проверяем пересечение мячиков и платформы
01:03:49 - Снимаем видео с нашей игры
01:08:55 - Исправим X_Step платформы
01:09:32 - Добавим в набор мячиков метод Set_For_Test()
01:10:57 - Добавим в метод Is_Test_Finished()
01:12:17 - Перенесём переменную Rest_Distance в класс
01:14:15 - Popcorn_part_51_2.zip: пишем код
Popcorn_part_51_1.zip - https://drive.google.com/file/d/1aodlF-q0fpaHWCglgZp9I75rbN2EVrks
Popcorn_part_51_2.zip - https://drive.google.com/file/d/1ZO5kXc6DdfNTfoUPQOsvuMnhh7jkktw3

Часть #52. Буквы Т, И, С и Ж[править]

00:00 - Буквы Т, И, С и Ж
00:25 - Исправляем имя метода на Advance_Movers()
00:54 - Добавим метод Triple_Balls()
05:22 - Выбираем самый дальний мячик
09:36 - Размножаем самый дальний мячик
12:49 - Разводим боковые мячики в стороны
16:26 - Проверяем работу алгоритма растроения
27:11 - Мячики перестали растраиваться - исправляем ошибку
30:55 - Popcorn_part_52_1.zip: пишем код
31:05 - Добавим обработку буквы "И" методом Inverse_Balls()
36:02 - Popcorn_part_52_2.zip: пишем код
36:14 - Выносим набор мячиков AsBall_Set в свою пару файлов
39:39 - Popcorn_part_52_3.zip: пишем код
39:45 - Исследуем баг с уезжающей платформой и её остатком после расплавления
42:46 - Добавим новое состояние платформы - EPS_Pre_Meltdown
45:20 - Смотрим, как замедляется мячик в оригинальной игре
46:57 - Ускоряем все мячики в методе Accelerate()
50:56 - Настраиваем ускорение мячиков
54:23 - Сбрасываем скорость всех мячиков в методе Reset_Speed()
59:38 - Ловим букву "Ж" в методе On_Falling_Letter()
01:03:39 - Popcorn_part_52_4.zip: пишем код
Popcorn_part_52_1.zip - https://drive.google.com/file/d/1oeQfyKmDvooUmRATzDfX59kLZuDDRYoG
Popcorn_part_52_2.zip - https://drive.google.com/file/d/1X2IGFTDPYeg63V6eAf16lA-Rm7bZPnpQ
Popcorn_part_52_3.zip - https://drive.google.com/file/d/1k2akUA0LpQVD-YsO0EOGVm4Ig_5tfsmA
Popcorn_part_52_4.zip - https://drive.google.com/file/d/1kMcYqceh1txIAMafcAfldHXS0-qd-k0P

Часть #53. Буква П (пол)[править]

00:00 - Буква П (пол)
01:13 - Ловим букву "П" в методе On_Falling_Letter()
02:12 - Смотрим, как включается пол в оригинальной игре
05:08 - Добавим метод Redraw_Floor()
08:55 - Рисуем пол в методе AsBorder::Draw()
13:33 - Добавим проверку пересечения с областью рисования для элементов рамки
16:52 - Падающие буквы и мячик оставляют следы на полу
17:51 - Рисуем линию пола пунктиром
23:00 - Выносим код в метод Draw_Floor()
24:25 - Рисуем серию штрихов
27:29 - Настраиваем длину штриха
30:29 - Штрихи не доходят до конца рамки
33:09 - Исправляем "отгрызание" мячиком куска пола в методе AsBorder::Check_Hit()
38:05 - Проверяем рисование пола и попадание в него мячика
40:55 - Popcorn_part_53.zip: пишем код
Popcorn_part_53.zip - https://drive.google.com/file/d/1kASCpBIAuXuRekFYC5vNrM6uRMuV_iVu

Часть #54. Рефакторинг графических объектов[править]

00:00 - Рефакторинг графических объектов
00:27 - Анализируем архитектуру объектов, имеющих метод рисования Draw()
03:57 - Наследуем класс мячика от графического объекта
10:08 - Наследуем AsBall_Set от графического объекта
12:24 - Наследуем AsBorder от графического объекта
14:32 - Наследуем AsLevel от графического объекта
17:12 - Наследуем AsPlatform от графического объекта
19:58 - Представим главные объекты движка как его модули
23:20 - Делаем инициализацию и отрисовку модулей движка
25:50 - Выносим код очистки графических объектов в методы Clear()
27:08 - AsLevel::Clear()
27:48 - AsBorder::Clear()
30:09 - AsPlatform::Clear()
32:54 - AsBall_Set::Clear()
33:33 - ABall::Clear()
34:52 - Платформа ощутимо мерцает в состоянии покоя
37:27 - Ищем причину мерцания платформы при её движении
42:04 - Исследуем "инерцию" платформы
45:43 - Рефакторим метод платформы Advance()
46:47 - Исправляем "инерцию" в методе Move() платформы
52:54 - Popcorn_part_54.zip: пишем код
Popcorn_part_54.zip - https://drive.google.com/file/d/1Ziu1KGjB3c5BdW080s6l_O9Eq9ybMaU0

Часть #55. Платформа с клеем, этап 1[править]

00:00 - Платформа с клеем, этап 1
00:29 - Смотрим на анимацию оригинальной игры
01:54 - Снимаем раскадровку анимации растекающегося клея
04:57 - Анализируем элементы анимации
05:58 - Описываем 3 новых состояния платформы
08:13 - Добавим метод Draw_Glue_State()
10:16 - Рисуем пятно клея
17:39 - Переводим платформу в состояние растекающегося клея
18:33 - Добавим фоновую кайму
19:29 - Корректируем позицию и размер капли клея
21:52 - Выносим код в метод Draw_Glue_Spot()
23:03 - Добавим в Draw_Glue_Spot() параметры для вывода различных пятен
25:41 - Выведем правое пятно
27:45 - Выведем среднее пятно
28:30 - Выводим общую каёмку для всех капель
30:18 - Корректируем размер левой капли
31:22 - Делаем анимацию растекающегося клея в методе Act()
36:47 - Ограничим регионом вывод пятен
41:32 - Зададим начальную высоту пятен в Set_State()
42:57 - Удалим регион в Draw_Glue_State()
44:24 - Добавим заглушки в Set_State() для обработки двух других состояний
45:49 - Делаем движение платформы с клеем
48:13 - Добавим стирание платформы в AsPlatform::Clear()
49:57 - Переводим платформу в состояние EPS_Glue
51:37 - При движении платформа с клеем стирается не полностью
53:11 - Смотрим, как вызывается метод Redraw_Platform()
56:20 - Добавим в Redraw_Platform() параметр для обновления прямоугольников
59:59 - Popcorn_part_55.zip: пишем код
Popcorn_part_55.zip - https://drive.google.com/file/d/1pTGDIvGRVCXMfqme1As8smS2GmY33kOL

Часть #56. Платформа с клеем, этап 2[править]

00:00 - Платформа с клеем, этап 2
00:28 - Ловим букву "К" в методе On_Falling_Letter()
01:37 - Убираем клей при старте игры и ловим букву "К"
02:23 - Ставим мячик на платформу в методе Check_Hit()
05:03 - Мячик остановился слишком высоко
06:50 - Выносим код в метод On_Space_Key()
09:16 - Добавим в платформу метод Init(), чтобы передать ей указатель на набор мячиков
11:31 - Отпустим следующий приклеенный мячик в On_Space_Key()
12:38 - Добавим метод Release_Next_Ball()
14:47 - Вызываем метод платформы Init()
16:15 - Смещаем приклеенные мячики в методе платформы Advance()
19:14 - Добавим метод On_Platform_Advance()
21:25 - Добавим метод Forced_Advance()
26:04 - Проверяем, как смещаются и отпускаются несколько мячиков
27:44 - Предотвращаем повторное растекание клея в Set_State()
29:27 - Баг: игра зависла из-за постоянного переотражения мячика
32:06 - Добавим принудительный поворот направления при зависании
34:58 - Баг: мячики на платформе смещаются в методе Advance()
38:33 - Баг: мячики перестали стартовать с платформы
41:17 - Сохраним направление и скорость мячика при постановке на платформу
42:48 - Добавим мячику метод Release()
46:13 - Исправим реакцию на букву "К" в Set_State() платформы
48:08 - Баг: мячики смещаются на платформе, если есть мячик, быстрее, чем платформа
53:45 - Добавим генерацию падающей буквы "О" в Add_Falling_Letter()
54:53 - Отменяем состояние клея в методе On_Falling_Letter()
55:32 - Делаем обработку оставшихся состояний клея в методе Set_State() платформы
57:29 - Добавим обработку состояния финализации клея в Act() платформы
58:56 - Баг: мячик остался на платформе после отмены клея
59:43 - Отпускаем очередной мячик в методе платформы Set_State()
01:01:57 - Добавим мячику время старта с платформы
01:04:18 - Отпускаем мячики в методе AsBall_Set::Act()
01:07:11 - Баг: мячики не стартуют сами с платформы в начале игры
01:09:07 - Баг: мячик стартует вправо
01:11:18 - Баг: платформа не двигается после автостарта мячиков
01:16:20 - Popcorn_part_56.zip: пишем код
Popcorn_part_56.zip - https://drive.google.com/file/d/1lFC5G0CWWAv-gLdC10WURMKhmfFHx8HX

Часть #57. Рефакторинг состояния клея платформы[править]

00:00 - Рефакторинг состояния клея платформы
00:30 - Выносим в отдельный метод вызов InvalidateRect()
06:31 - Выносим шаг коэффициента растекания клея в именованную константу
07:55 - Баг: другая буква, кроме "О" не отменяет клей
09:53 - Переводим платформу в нормальное состояние в On_Falling_Letter()
12:04 - Добавим установку состояния EPS_Normal в методе платформы Set_State()
15:00 - Баг: мячик летает близко к горизонтальному направлению
16:20 - Баг: растекание клея прерывается, если поймать другую букву
18:11 - Выносим состояния клея платформы в отдельное перечисление
22:18 - Заменяем состояния платформы на новые
27:32 - Исправляем приклеивание мячиков на платформу в Check_Hit()
28:35 - Корректируем смещение мячиков на платформе в Advance()
30:35 - Исправляем анимацию в методе платформы Act()
32:28 - Баг: буква "Т" не отключает клей и не растраивает мячик
34:39 - Баг: буква "Т" так и не растроила мячик
35:44 - Выносим угол коррекции в константу Min_Ball_Angle
37:52 - Корректируем угол мячика при приближении к горизонтали
44:07 - Popcorn_part_57.zip: пишем код
Popcorn_part_57.zip - https://drive.google.com/file/d/12G-jY6kFBgz6blVAhMqh8TS3DU2Z3s4s

Часть #58. Оптимизация состояний[править]

00:00 - Оптимизация состояний
02:07 - Выносим состояния расплавления в EPlatform_Substate_Meltdown
04:37 - Выносим код Act_For_Glue_State()
06:08 - Добавим метод Act_For_Meltdown_State()
07:52 - Уберём очистку при расплавлении из метода Clear()
08:48 - Исправим метод Draw() при рисовании расплавления
09:36 - Исправим метод Set_State() для расплавления
10:18 - Исправим метод Act_For_Meltdown_State()
12:22 - Popcorn_part_58_1.zip: пишем код
12:37 - Выносим состояния выкатывания в EPlatform_Substate_Rolling
14:37 - Исправим метод Act() для выкатывающейся платформы
16:11 - Добавим метод Act_For_Rolling_State()
17:42 - Добавим метод Draw_Rolling_State()
19:40 - Исправим методы Set_State(), Redraw_Platform(), Draw_Roll_In_State() и On_Timer()
21:58 - Переносим код из рисующих методов в Act_For_Rolling_State()
24:10 - Удаляем метод Draw_Expanding_Roll_In_State()
25:10 - Исправим метод Act_For_Rolling_State()
25:51 - Выносим обычные состояния платформы в EPlatform_Substate_Regular
26:50 - Пересечение констант EPSR_Unknown
28:54 - Используем новый вид перечислений - enum class
33:31 - Добавим переменную типа EPlatform_Substate_Regular
35:26 - Исправим методы Advance(), Clear() и Draw()
38:40 - Добавим перегруженный метод Set_State()
42:05 - Добавим метод Has_State()
44:07 - Исправим методы Move() и On_Space_Key()
46:18 - Исправим Act_For_Rolling_State(), Act_For_Glue_State(), Draw_Normal_State() и Draw_Meltdown_State()
47:37 - Исправим методы On_Timer(), Act() и On_Falling_Letter()
49:17 - Popcorn_part_58_2.zip: пишем код
49:36 - Сделаем enum class для EPlatform_Substate_Meltdown
51:38 - Сделаем enum class для EPlatform_Substate_Rolling
53:07 - Сделаем enum class для EPlatform_Substate_Glue
54:25 - Сделаем enum class для EPlatform_State
56:58 - Сделаем enum class для EPlatform_Moving_State
58:12 - Переносим все состояния платформы в класс AsPlatform_State
01:01:41 - Переносим инициализацию состояний в конструктор AsPlatform_State
01:03:49 - Исправляем использование переменных состояния
01:05:38 - Добавим оператор преобразования типа
01:09:17 - Добавим оператор присваивания
01:12:30 - Баг: платформа с клеем не двигается
01:14:19 - Подводим итог рефакторинга перечислений
01:15:13 - Popcorn_part_58_3.zip: пишем код
Popcorn_part_58_1.zip - https://drive.google.com/file/d/126D9yJTZJdh1wU27z18i1sKHb18kSBKz
Popcorn_part_58_2.zip - https://drive.google.com/file/d/1Wgkgp3-w6XAlZ1B4gI4LmWRr5TvxN7Ie
Popcorn_part_58_3.zip - https://drive.google.com/file/d/18BIx5G0OxXUm9S92fVXPn0fzqidlpCi1

Часть #59. Расширяющаяся платформа, этап 1[править]

00:00 - Расширяющаяся платформа, этап 1
00:29 - Смотрим анимацию в оригинальной игре
01:11 - Делаем раскадровку анимации расширения платформы
02:59 - Анализируем полученные кадры
03:30 - Добавим новое состояние платформы EPlatform_Substate_Expanding
04:41 - Добавим метод Draw_Expanding_State()
05:41 - Рисуем в Paint-е расширенную платформу
11:47 - Декомпозируем платформу на компоненты
15:33 - Начинаем рисовать расширяющуюся платформу
16:49 - Рисуем среднюю часть
19:13 - Добавим установку расширяющегося состояния в Set_State()
21:28 - Позиционируем среднюю часть по середине платформы
24:02 - Позиционируем правый шарик платформы
25:53 - Вырезаем дырку под дугой
28:40 - Рисуем дугу
33:34 - Рисуем линии фермы
40:55 - Корректируем дугу
45:05 - Рисуем вторую пару линий фермы
47:27 - Вырезаем дырку под фермой
48:38 - Добавим прямоугольник над шариком платформы
50:33 - Дополним комментарии для рисования платформы
52:22 - Переносим код рисования ферм в Draw_Expanding_Truss()
54:42 - Переносим код рисования шарика в Draw_Expanding_Platform_Ball()
57:39 - Рисуем правый шарик
01:06:14 - Рисуем правые фермы
01:09:14 - Popcorn_part_59.zip: пишем код
Popcorn_part_59.zip - https://drive.google.com/file/d/1ZGNP2c9zC25mvBqSClFmo3RzqwJzj5P8

Часть #60. Расширяющаяся платформа, этап 2[править]

00:00 - Расширяющаяся платформа, этап 2
00:31 - Обрабатываем столкновение с буквой "Ш" в On_Falling_Letter()
02:20 - Создадим копипастом метод Act_For_Expanding_State()
07:36 - Добавим стирание платформы в методе Clear()
08:44 - Корректируем ширину платформы в Redraw_Platform()
10:48 - Исправляем заказ перерисовки платформы в Act_For_Expanding_State()
11:40 - Замедляем анимацию в 10 раз
12:27 - Введём коэффициент расширения платформы в Draw_Expanding_Truss()
16:50 - Добавим смещение ферм по X и проверим анимацию
19:45 - Сделаем расширение относительно центра платформы в Act_For_Expanding_State()
21:16 - Исправляем отрисовку правого края платформы
23:33 - Popcorn_part_60_1.zip: пишем код
23:45 - Добавим условие в метод Move() для смещения расширенной платформы
26:19 - Обновляем предыдущий прямоугольник платформы 1 раз на кадр в Redraw_Platform()
31:02 - Проверяем расширение платформы с клеем
31:48 - Баги: платформа заезжает за правый край уровня и мячик пролетает сквозь правый край платформы
32:26 - Смотрим использование ширины платформы
34:19 - Выносим код в метод Get_Current_Width()
36:54 - Применяем метод Get_Current_Width() вместо обращения к переменной Width
38:47 - Проверяем расширение платформы
39:46 - Применяем коррекцию из метода Advance() в Correct_Platform_Pos()
45:02 - Баг: платформа не переходит из одного анимированного состояния в другое
46:46 - Сохраним следующее состояние платформы в Set_State()
48:44 - Добавим метод Set_Next_State()
55:37 - Применяем переход в отложенное состояние в Set_State()
01:02:20 - Добавим в Set_State() обработку расширенной платформы
01:04:23 - Проверяем переходы между анимированными состояниями
01:07:08 - Переносим код отпускания всех мячиков из Set_State() в Act_For_Glue_State()
01:10:29 - Выносим код в метод Set_Next_Or_Regular_State()
01:13:20 - Popcorn_part_60_2.zip: пишем код
Popcorn_part_60_1.zip - https://drive.google.com/file/d/147UkixUPdGmd0cTVWdfvHt0jocGD2jd0
Popcorn_part_60_2.zip - https://drive.google.com/file/d/11HvlAYXiUpbTORSNEGfn5aKDYHJBRsnH

Часть #61. Лазерная платформа, этап 1[править]

00:00 - Лазерная платформа, этап 1
00:20 - Исправим ошибку в имени extension_ratio
00:51 - Смотрим анимацию лазерной платформы
02:01 - Снимаем раскадровку оригинальной анимации
04:14 - Анализируем раскадровку
06:23 - Добавим enum EPlatform_Substate_Laser
07:16 - Добавим поддержку лазерного состояния в Set_Next_State()
08:31 - Добавим поддержку лазерного состояния в Act(), Clear() и Draw()
09:13 - Добавим установку лазерного состояния в Set_State()
10:43 - Добавим поддержку лазера в Move() и On_Space_Key()
11:53 - Добавим копипастом метод Act_For_Laser_State()
15:13 - Допишем установку начального шага трансформации в Set_State()
15:54 - Добавим пустой метод Draw_Laser_State()
17:59 - Готовим прообраз изображения платформы
21:47 - Декомпозируем исходное изображение на графические примитивы
25:17 - Добавим регион обрезки для лазерной платформы
26:53 - Рисуем левое крыло
31:28 - Рисуем перемычку левого крыла
33:38 - Рисуем копипастом правое крыло и перемычку
37:13 - Делаем правое крыло зеркальным отражением левого
39:29 - Ищем способ совмещения чертежа с результатом отрисовки
43:02 - Корректируем высоту крыльев и перемычек
45:13 - Рисуем прямоугольники "ног" платформы
49:38 - Рисуем кабину
54:25 - Рисуем левую "ногу" многоугольником
59:52 - Рисуем правую "ногу" многоугольником
01:01:03 - Рисуем среднюю часть кабины
01:02:42 - Рисуем пушки
01:07:59 - Рисуем хвост
01:11:47 - Заливаем платформу цветом
01:12:42 - Выносим код в метод Draw_Laser_Wing()
01:14:56 - Совмещаем в одном методе рисование левого и правого крыла
01:19:39 - Выносим код в метод Draw_Laser_Leg()
01:20:50 - Совмещаем в одном методе рисование двух "ног"
01:23:26 - Выносим код в метод Draw_Laser_Cabin()
01:24:55 - Popcorn_part_61.zip: пишем код
Popcorn_part_61.zip - https://drive.google.com/file/d/1vvAySUvf_YkHCEhQusKN2YXvy3DByKIz

Часть #62. Лазерная платформа, этап 2[править]

00:00 - Лазерная платформа, этап 2
00:31 - Добавим обработку буквы "Л" в On_Falling_Letter()
02:09 - Ускоряем для отладки переход в лазерное состояние
03:06 - Разбираемся, как мы будем делать анимацию платформы
04:57 - Как мы сделаем анимацию крыла
06:53 - Добавим коэффициент трансформации ratio в Draw_Laser_Wing()
08:34 - Меняем высоту эллипса от 7 до 12 игровых пикселей
11:14 - Отключим рисование остальных элементов платформы
12:23 - Настраиваем рисование перемычки
13:48 - Баг: платформа анимируется не по середине уровня
15:35 - Исправляем позиционирование в Act_For_Rolling_Step()
16:42 - Анализируем анимацию перемычки
20:24 - Рисуем начальное и конечное состояние перемычки
24:45 - Добавим метод Draw_Expanding_Rectangle()
34:38 - Преобразуем Draw_Expanding_Rectangle() в Draw_Expanding_Figure()
36:22 - Применяем Draw_Expanding_Figure() для рисования крыла
41:19 - Анимируем правое крыло
46:14 - Анимируем пушку
54:13 - Анимируем хвост
56:31 - Заменяем тип аргументов в Draw_Expanding_Figure() с int на double
58:25 - Вынесем код в Get_Expanding_Value()
01:01:34 - Корректируем рост хвоста
01:04:47 - Анимируем внутреннюю часть кабины
01:09:50 - Анимируем среднее кольцо кабины
01:16:57 - Анимируем внешнюю часть кабины
01:19:37 - Смотрим анимацию всей платформы
01:21:55 - Анализируем анимацию ног и средней части
01:23:39 - Анимируем рост "ног" платформы
01:26:03 - Анимируем уменьшение средней части платформы
01:28:47 - Выносим код в Draw_Laser_Inner_Part()
01:31:28 - Проверяем анимацию платформы, залитой цветом
01:32:42 - Добавим перечисление EFigure_Type и применим его в Draw_Expanding_Figure()
01:37:10 - Завершаем настройку анимации платформы
01:39:12 - Баг: платформа обрезана слева на 2 пикселя
01:45:22 - Смотрим, как платформа переходит между состояниями
01:47:04 - Popcorn_part_62.zip: пишем код
Popcorn_part_62.zip - https://drive.google.com/file/d/1Q6fRTvzDtXFQzE9zjKtPrpbzUUEICruB

Часть #63. Оптимизация платформы, этап 1[править]

00:00 - Оптимизация платформы, этап 1
00:38 - Признак необходимости рефакторинга большого класса
02:06 - Обобщим 3 перечисления в одно - EPlatform_Transformation
04:53 - Выносим код в Set_Transformation_State()
10:04 - Отрефакторим метод Set_State() для "регулярного" состояния
12:42 - Исправляем константы в методах анимации
14:34 - Проверяем работу программы
16:26 - Убираем лишние комментарии
17:36 - Описываем иерархию классов платформы
23:15 - Виртуальное наследование
27:01 - Рассматриваем альтернативу наследованию - агрегацию
29:07 - Выносим код в класс AsPlatform_Glue
33:06 - Выносим вызов Redraw_Platform() за пределы Act_For_Glue_State()
34:29 - Передаём в Act_For_Glue_State() указатель на набор мячиков
35:26 - Смотрим, как можно отрефакторить метод Set_State()
37:45 - Рефакторим метод Set_Next_Or_Regular_State()
41:33 - Переносим метод Set_State() в класс AsPlatform_State
43:03 - Добавим снова метод Set_State() в платформу, но уже с другой логикой
47:01 - Передадим ссылку на состояния платформы в AsPlatform_Glue()
48:06 - Вернём следующее состояние из Act_For_Glue_State()
50:04 - Рефакторим метод Draw_Glue_State()
53:23 - Рефакторим метод Draw_Glue_Spot()
54:07 - Исправляем инициализацию данных платформы
55:32 - Переносим код в метод Reset()
57:51 - Исправляем имена методов класса AsPlatform_Glue
58:46 - Убираем лишние комментарии
01:00:32 - Popcorn_part_63.zip: пишем код
Popcorn_part_63.zip - https://drive.google.com/file/d/1R7ZXPGEAtwK3MO1BrhdRfHpIvhJg_mQG

Часть #64. Оптимизация платформы, этап 2[править]

00:00 - Оптимизация платформы, этап 2
00:19 - Выносим функциональность расширяющейся платформы в свой класс
02:54 - Добавим конструктор, принимающий ссылку на состояние платформы
04:29 - Рефакторим Act_For_Expanding_State()
05:49 - Исправим AsPlatform_Glue::Act()
06:35 - Передаём ссылки на позицию и следующее состояние в Act_For_Expanding_State()
08:03 - Возвращаем необходимость коррекции позиции через параметр
09:21 - Исправляем вызов Act_For_Expanding_State()
11:07 - Рефакторим Draw_Expanding_State()
13:42 - Рефакторим Draw_Expanding_Platform_Ball()
15:45 - Делаем отложенную инициализацию платформы
19:24 - Добавим переменную Inner_Color
21:22 - Добавим переменную Circle_Color
22:57 - Переносим метод Draw_Circle_Highlight()
26:15 - Переносим константы и исправляем инициализацию данных платформы
27:38 - Добавим метод AsPlatform_Expanding::Reset()
29:58 - Исправим имена методов и уберём лишние комментарии
31:11 - Popcorn_part_64_1.zip: пишем код
31:25 - Выносим функциональность лазерной платформы в свой класс
33:48 - Добавим конструктор AsPlatform_Laser
34:53 - Рефакторим Act_For_Laser_State()
36:24 - Рефакторим Draw_Laser_State()
37:19 - Рефакторим константы для лазерной платформы
39:05 - Рефакторим цвет и добавляем метод Init()
42:32 - Рефакторим Draw_Laser_Inner_Part()
43:59 - Перенесём Draw_Expanding_Figure() и Get_Expanding_Value()
45:33 - Исправляем инициализацию платформы
46:16 - Исправляем вызов Act_For_Laser_State()
47:09 - Добавим метод AsPlatform_Laser::Reset()
48:18 - Исправляем инициализацию платформы
49:30 - Исправим имена методов и уберём лишние комментарии
52:11 - Popcorn_part_64_2.zip: пишем код
Popcorn_part_64_1.zip - https://drive.google.com/file/d/1_TS3VLfhI4caus6uOMc46ZiYpeQIxbxZ
Popcorn_part_64_2.zip - https://drive.google.com/file/d/1uNyMCtzh3Rft3rdXXUmTsBJ4kkcWjKBX

Часть #65. Стрельба лазерными лучами, часть 1[править]

00:00 - Стрельба лазерными лучами, часть 1
00:30 - Смотрим оригинальную анимацию лазерных лучей
01:00 - Делаем обработку нажатия пробела в методе Fire()
03:31 - Добавим в лазерную платформу массив лазерных лучей
05:13 - Ищем свободные лазерные лучи
09:02 - "Ставим" лучи в начальную позицию в методе Set_At()
11:40 - Планируем архитектуру классов для множества лазерных лучей
12:48 - Создадим копипастом классы ALaser_Beam и AsLaser_Beam_Set
13:54 - Переносим набор лучей из платформы в движок
17:55 - Добавим прототипы методов для ALaser_Beam и AsLaser_Beam_Set
20:27 - Переносим метод Fire() в класс AsLaser_Beam_Set
22:29 - Добавим в ALaser_Beam поле Is_Active и метод Set_At()
25:48 - Пишем копипастом методы класса AsLaser_Beam_Set
29:28 - Пишем код для ALaser_Beam::Draw()
30:40 - Пишем код для ALaser_Beam::Set_At()
34:56 - Рисуем луч
37:32 - Проверяем рисование луча
39:18 - Popcorn_part_65.zip: пишем код
Popcorn_part_65.zip - https://drive.google.com/file/d/1GiOyT6CzFJJxi9pU_Cjusw3S9yjnykV8

Часть #66. Стрельба лазерными лучами, часть 2[править]

00:00 - Стрельба лазерными лучами, часть 2
00:25 - Исправим ошибку с двумя плюсами
00:46 - Выносим код в метод Get_Gun_Pos()
04:35 - Отрефакторим методы Fire() у лазерной платформы и набора лучей
06:14 - Корректируем позицию центра пушек
13:34 - Ставим луч в стартовую позицию в ALaser_Beam::Set_At()
15:05 - Делаем метод Advance() лазерного луча
15:51 - Выносим код в метод Redraw_Beam()
18:27 - Заглушим метод Begin_Movement()
19:15 - Поправим методы Get_Speed(), Act() и Is_Finished()
19:56 - Проверяем первый запуск лучей
20:26 - Делаем метод ALaser_Beam::Clear()
22:32 - Удаляем точечные следы от лучей
25:24 - Деактивация лучей при достижении края уровня
27:47 - Анализируем поведение луча возле границы уровня
30:18 - Добавим состояния луча в ELaser_Beam_State
32:57 - Применим методы лазерного луча
38:52 - Добавим "режим" стрельбы вместо выстрела по нажатию пробела
40:57 - Перенесём код стрельбы из Fire() в Act()
43:21 - Делаем автоматическую стрельбу через интервал времени
46:58 - Исправим реакцию на отжатие пробела в On_Space_Key()
48:39 - Проверяем столкновение луча с кирпичами в методе Advance()
51:21 - Добавим копипастом массив хит-чекеров для лучей
52:08 - Добавим перегруженный метод Check_Hit() в уровень
58:26 - Исправим On_Hit() уровня для нулевого указателя на мячик
01:03:17 - Добавим перегруженный метод Check_Hit() в Hit_Checker
01:06:23 - Добавим копипастом метод Add_Hit_Checker()
01:08:28 - Баг: лучи пропадают перед кирпичами
01:12:42 - Проверяем переход платформы через разные состояния
01:13:53 - Баг: лазер стреляет сам при переходе платформы в лазерное состояние
01:16:37 - Popcorn_part_66.zip: пишем код
Popcorn_part_66.zip - https://drive.google.com/file/d/1IZyD1ih6x4WXd4CQnhRRY-uWDTaPSl4F

Часть #67. Рефакторинг платформы[править]

00:00 - Рефакторинг платформы
01:26 - Выносим класс ALaser_Beam в свою пару файлов
05:31 - Вынесем "инструменты" в пару файлов Tools
08:38 - Переносим класс AColor в Tools
11:49 - Выносим класс AsLaser_Beam_Set в свою пару файлов
14:08 - Выносим класс AsPlatform_State в свою пару файлов
16:36 - Выносим класс AsPlatform_Glue
24:46 - Выносим класс AsPlatform_Expanding
26:31 - Выносим класс AsPlatform_Laser
29:31 - Добавим класс AHit_Checker_List
35:13 - Добавим в AHit_Checker_List метод Check_Hit()
36:45 - Применим AHit_Checker_List для лазерного луча
39:27 - Применим AHit_Checker_List для мячика
44:08 - Popcorn_part_67.zip: пишем код
Popcorn_part_67.zip - https://drive.google.com/file/d/1TuB_vLGuQMLaT4FRVpENrngUJApPiido

Часть #68. Гейты, этап 1[править]

00:00 - Гейты, этап 1
00:52 - Анализируем оригинальную анимацию
02:14 - Снимаем раскадровку анимации гейта
03:40 - Анализируем этапы анимации
05:41 - Создаём класс AGate
08:15 - Добавим заготовки методов гейта
09:40 - Вызовем рисование гейта в методе Draw() рамки
10:16 - Поставим гейт в правильную позицию
12:53 - Декомпозируем гейт на элементы
14:52 - Отключим рисование рамки справа
15:29 - Рисуем основу верхней чаши
22:59 - Добавим регион обрезки
27:27 - Рисуем блик на чаше снизу
30:12 - Рисуем "заплатку" в правом нижнем углу
31:37 - Рисуем перфорацию
32:31 - Добавим в конфиг метод Rect()
34:55 - Добавим в конфиг второй перегруженный метод Rect()
38:53 - Отрефакторим рисование чаши
40:17 - Рисуем перемычку перед чашей
41:08 - Вынесем код в метод Draw_Cup()
43:03 - Применим матрицу преобразования пространства для чаши
49:25 - Выводим отражённую чашу
53:36 - Выводим обе чаши
57:58 - Добавим метод Draw_Edge() для рисования ребра
01:01:33 - Рисуем рёбра под чашей
01:05:50 - Выносим код в Draw_Edges()
01:07:05 - Рисуем полный гейт
01:08:11 - Выводим все 8 гейтов
01:15:14 - Очищаем область под гейтом
01:20:04 - Добавим цвет Gate_Color
01:21:10 - Баг: платформа наезжает на гейты
01:22:49 - Popcorn_part_68.zip: пишем код
Popcorn_part_68.zip - https://drive.google.com/file/d/1KH_8VC59arQ0mU9Dzg6PzpvAdmzK5NOW

Часть #69. Гейты, этап 2[править]

00:00 - Гейты, этап 2
00:26 - Анализируем состояния анимации гейтов
02:21 - Добавим перечисление EGate_State
03:51 - Добавим перечисление EGate_Transformation
05:36 - Добавим метод Open_Gate() в гейт
07:39 - Добавим метод Open_Gate() в класс рамки
12:16 - Добавим Act() для рамки
14:08 - Исправим Clear() для рамки
15:13 - Добавим Act() для гейта
16:43 - Добавим копипастом метод Act_For_Short_Open()
24:23 - Анимируем короткое открытие в Draw_Edges()
27:58 - Баг: не работает анимация гейта
30:33 - Добавим метод Redraw_Gate()
31:32 - Баг: открытый гейт не закрывается
34:11 - Popcorn_part_69_1.zip: пишем код
35:34 - Исправим порядок открытия и выкатывания платформы в Restart_Level()
37:23 - Сделаем открытие гейта в AsEngine::Act()
39:22 - Добавим метод Is_Gate_Opened()
40:32 - Добавим метод Is_Opened()
42:04 - Ускорим анимацию короткого открытия
42:56 - Добавим копипастом метод Act_For_Long_Open()
51:30 - Добавим смещение чаш вверх
53:25 - Добавим смещение нижней чаши вниз
56:44 - Анализируем множественные баги анимации гейта
58:11 - Исправляем анимацию 7-го гейта
59:15 - Исправляем однопиксельные погрешности округления
01:05:31 - Баг: рамка не перерисовывается после задвигающихся чаш
01:07:40 - Баг: рамка мигает под гейтом
01:12:21 - Совместим 2 метода в один Act_For_Open()
01:18:21 - Анализируем оригинальную анимацию длинного раскрытия гейта
01:19:53 - Разделим Draw_Edges() на два метода
01:21:11 - Добавим метод Draw_Long_Opening_Edges()
01:25:01 - Добавим метод Draw_Red_Edge()
01:26:55 - Выведем красные рёбра
01:30:30 - Анализируем оригинальную анимацию разряда между чашами
01:31:19 - Рисуем разряд в AGate::Draw()
01:32:47 - Добавим метод Draw_Charge()
01:41:36 - Popcorn_part_69_2.zip: пишем код
Popcorn_part_69_1.zip - https://drive.google.com/file/d/1xVkc8NUvIo6yQGtACwzPgpDdC2L83zoT
Popcorn_part_69_2.zip - https://drive.google.com/file/d/1kAzPkz2V6ghvHoxaejxsvV9LLPbZoZCA

Часть #70. Рефакторинг enum и игровые объекты[править]

00:00 - Рефакторинг enum и игровые объекты
00:26 - О том, что не надо смешивать enum и enum class
01:40 - Переводим EBrick_Type в enum class
06:18 - Исправим условия в Open_Gate() и Is_Gate_Opened()
09:34 - Переводим EDirection_Type в enum class
12:00 - Переводим ETeleport_State в enum class
14:58 - Переводим EBall_State в enum class
16:17 - Переводим EKey_Type в enum class
17:10 - Переводим EGame_State в enum class
18:10 - Переводим ELetter_Type в enum class
20:46 - Переводим EFalling_Letter_State в enum class
21:48 - Группируем фильтры в #Ball
24:09 - Группируем фильтры в #Laser_Beam
25:19 - Группируем фильтры в #Platform
26:30 - Группируем фильтры в #Tools
27:42 - Вынесем класс AGate в свою пару файлов
30:24 - Группируем фильтры в #Border
31:14 - О замене вызова Rectangle() на AsConfig::Rect()
35:41 - Переименуем файлы Tools в Common
39:19 - Переносим методы конфига в класс AsTools
41:38 - Исправляем вызовы методов конфига
43:24 - Проверяем результат замены вызова Rectangle() на AsConfig::Rect()
45:04 - Заменяем вызов Rectangle() в AsBorder::Clear()
46:30 - Заменяем вызов Rectangle() в AsBorder::Draw_Element()
50:46 - Заменяем вызов Rectangle() в AFalling_Letter::Clear()
52:35 - Заменяем вызов Rectangle() в ALaser_Beam::Clear()
53:33 - Заменяем вызов Rectangle() в AsPlatform::Clear()
54:09 - Заменяем вызов Rectangle() в AsPlatform_Expanding::Draw_State()
55:35 - Добавим метод AsTools::Ellipse()
57:36 - Заменяем вызов Ellipse() в ABall::Clear()
58:39 - Заменяем вызов Ellipse() в ABall::Draw()
59:20 - Заменяем вызов Ellipse() в ABall::Draw_Parachute()
01:00:34 - Заменяем вызов Ellipse() в AsPlatform::Draw_Normal_State() и Draw_Expanding_Platform_Ball()
01:02:57 - Popcorn_part_70_1.zip: пишем код
01:04:00 - Создадим копипастом класс AsGame_Objects_Set
01:06:09 - Анализируем задачу представления разных игровых объектов в одном классе
01:08:37 - Пытаемся в конструкторе создать массив общих объектов
01:11:57 - Приведение объектов к другому типу на примере Begin_Movement()
01:13:36 - Приведение типов через оператор dynamic_cast
01:16:39 - Объединим классы AGraphics_Object и AMover в один - AGame_Object
01:19:44 - Сделаем доступ к игровым объектам через итератор
01:24:25 - Добавим метод AsBall_Set::Get_Next_Game_Object()
01:28:22 - Добавим копипастом интерфейсные методы в AsGame_Objects_Set
01:35:01 - Отнаследуем AsLaser_Beam_Set от AsGame_Objects_Set
01:37:46 - Представим модули и муверы в движке как игровые объекты
01:44:22 - Добавим в движок метод Add_Next_Module()
01:48:20 - Исправим стирание гейтов в AsBorder::Clear()
01:49:05 - Popcorn_part_70_2.zip: пишем код
Popcorn_part_70_1.zip - https://drive.google.com/file/d/1sCPm6a4Vrk6mtYGRCAyG9m72Q8rm1Zsk
Popcorn_part_70_2.zip - https://drive.google.com/file/d/1kK2sLU9c0rUfBFx4ni7T2LgRoFB2cmzZ

Часть #71. Монстр "Глаз"[править]

00:00 - Монстр "Глаз"
00:34 - Добавим копипастом класс AsMonster_Set
04:02 - Добавим класс AMonster
06:34 - Добавим набор монстров в движок
08:56 - Добавим метод Emit_At_Gate()
14:41 - Добавим инициализацию набора монстров
17:40 - Активация монстра возле указанного гейта
20:40 - Добавим в рамку метод Get_Gate_Pos()
22:09 - Добавим в гейт метод Get_Pos()
23:20 - Добавим монстру метод Activate() и конструктор
25:43 - Выбираем монстров и снимаем раскадровку анимации
30:33 - Декомпозируем анимацию глаза
32:16 - Рисуем основу монстра в AMonster::Draw()
36:09 - Добавим эллиптический регион обрезки
37:36 - Подбираем цвет "затенённой" части монстра
41:08 - Рисуем второй круг монстра
45:12 - Рисуем роговицу
51:30 - Рисуем радужку
53:11 - Рисуем зрачок
55:53 - Делаем переменный размер роговицы
01:00:48 - Обрезаем вывод по размеру роговицы
01:02:47 - Обводим роговицу эллиптической дугой
01:09:35 - Popcorn_part_71_1.zip: пишем код
01:09:49 - Декомпозируем анимацию мигания глаза
01:11:31 - Описываем состояния мигания в EEye_State
01:13:29 - Описываем расписание мигания всех глаз
01:17:29 - Задаём расписание мигания конкретного глаза
01:22:10 - Вычислим текущий кадр анимации по расписанию
01:25:51 - Вычисляем текущую высоту роговицы
01:31:27 - Подкорректируем расписание мигания
01:33:38 - Popcorn_part_71_2.zip: пишем код
Popcorn_part_71_1.zip - https://drive.google.com/file/d/1vceTA2TzeVOQOZE4FRjrKM7G0ttHhuXS
Popcorn_part_71_2.zip - https://drive.google.com/file/d/1N-DuljL07dpaLf_rb8YHbKT5XLDphMaY

Часть #72. Взрыв монстра[править]

00:00 - Взрыв монстра
00:50 - Баг: в гейте остаётся кусок платформы
05:09 - Анализируем анимацию взрыва монстра
06:45 - Добавим состояние монстра EMonster_State
10:38 - Добавим метод Destroy()
11:35 - Добавим в монстра массив взрывающихся шариков
13:07 - Добавим класс AExplosive_Ball
14:02 - Добавим объявление метода Explode()
15:02 - Добавим прототипы методов класса AExplosive_Ball
16:47 - Добавим параметры в метод Explode()
19:08 - Отрефакторим метод AMonster::Draw()
21:34 - Добавим метод Draw_Destroing()
23:02 - Рисуем шарик взрыва
24:46 - Добавим состояние взрыва EExplosive_Ball_State
27:28 - Делаем анимацию в AExplosive_Ball::Act()
31:48 - Добавим метод Redraw_Ball()
33:01 - Баг: метод Act() не вызывается
35:08 - Добавим метод Act_Destroing()
36:24 - Исправим заказ перерисовки
39:26 - Делаем анимацию угасания шарика
42:41 - Рисуем угасающий шарик в Draw()
45:33 - Добавим оттенки угасания копипастом из активного кирпича
49:13 - Перенесём методы получения оттенков в класс AsTools
52:07 - Рисуем очередной оттенок угасающего шарика
54:32 - Отладим угасание шарика
59:38 - Сделаем стирание монстра
01:01:14 - Взрываем 2 шарика
01:02:12 - Добавим смещение взрыва во времени
01:04:14 - Добавим состояние взрыва Charging
01:07:18 - Добавим белую обводку шарика взрыва
01:10:03 - Эмулируем множество шариков
01:11:25 - Взрываем все шарики
01:13:35 - Разнесём шарики в пространстве
01:17:08 - Исправим вычисление смещения шариков от центра
01:26:12 - Добавим синие шарики взрывов
01:31:36 - Popcorn_part_72.zip: пишем код
Popcorn_part_72.zip - https://drive.google.com/file/d/18G4tTgwAO0-BGGftWnDZyEzgZg-0OoeL

Часть #73. Движение монстра[править]

00:00 - Движение монстра
00:32 - Смещение монстра в AsMonster::Advance()
05:39 - Добавим метод Redraw_Monster()
08:38 - Закажем перерисовку монстра в его методе Finish_Movement()
11:00 - Добавим направление движения монстра
12:13 - Задаём направление при активации монстра
14:05 - Вычисляем направление выпуска монстра в Emit_At_Gate()
17:06 - Вычислим позицию монстра в зависимости от гейта
20:29 - Применяем направление для движения монстра
24:50 - Делаем случайный выбор направления в Act_Alive()
30:01 - Стираем предыдущее изображение монстра
31:50 - Сделаем случайный выбор скорости для монстра
33:44 - Добавим собственный метод Act() в набор монстров
37:20 - Переименуем класс AsGame_Objects_Set
38:17 - Добавим состояния набора монстров EMonster_Set_State
40:39 - Действия набора монстров в Act() в зависимости от состояний
45:02 - Добавим метод активации монстров Activate()
46:52 - Добавим в рамку метод Is_Gate_Closed()
49:00 - Добавим в гейт метод Is_Closed()
51:32 - Добавим максимальное количество живых монстров
52:35 - Подсчёт живых монстров перед выпуском нового
55:30 - Баг: монстр выходит из гейта выше, чем надо
56:44 - Ускорим открытие и закрытие гейта в 2 раза
57:32 - Баг: гейт, закрываясь, не становится в свою исходную позицию
01:01:19 - Ограничиваем перемещение монстра рамкой уровня
01:04:13 - Баг: монстра "выстреливает" из гейта на уровень
01:11:00 - Добавим гейту позицию кирпича
01:14:34 - Задаём позиции кирпичей для всех гейтов
01:16:45 - Добавим метод Long_Open_Gate()
01:24:32 - Добавим метод Has_Brick_At()
01:32:55 - Popcorn_part_73.zip: пишем код
Popcorn_part_73.zip - https://drive.google.com/file/d/1p2UXPtxSdOV1Ph6-RjSbQN5CuR86lkrH

Часть #74. Движение монстра, этап 2[править]

00:00 - Движение монстра, этап 2
00:39 - Выносим класс AExplosive_Ball в свою пару файлов
03:47 - Выносим класс AMonster в свою пару файлов
05:55 - Popcorn_part_74_1.zip: пишем код
06:10 - Корректируем следующую позицию монстра в Advance()
09:46 - Выносим код в Get_Monster_Rect()
13:59 - Добавим в уровень метод Has_Brick_At()
25:34 - Выбираем свободное направление для монстра в Advance()
32:54 - Баг: платформа не двигается после автостарта мячика
36:10 - Исправляем постановку мячика на платформу в Set_On_Platform()
39:01 - Добавим столкновения с монстрами в Init_Engine()
40:25 - Наследуем AsMonster_Set от AHit_Checker
43:19 - Наследуем AMonster от AHit_Checker
46:13 - Перенесём метод Reflect_On_Circle() в класс AsTools
50:20 - Исправим вызов Reflect_On_Circle() в Check_Hit() платформы
52:55 - Почему не стоит подключать лишний заголовочный файл в *.cpp
56:37 - Добавим интерфейсный класс ABall_Object
01:00:47 - Перенесём константу Radius из мячика в конфиг
01:03:58 - Исправим Check_Hit() у монстра
01:05:06 - Заменяем использование ABall на ABall_Object
01:11:57 - Переносим в интерфейс все нужные методы
01:17:00 - Убираем лишние подключения файла Ball.h
01:20:23 - Баг: добавление 4-го хит-чекера игнорируется
01:24:21 - Баг: шарики взрывов рисуются для движущегося монстра
01:27:05 - Переводим состояние монстра в Missing в Act_Destroing()
01:31:40 - Размещаем методы ABall в правильном порядке
01:33:33 - Popcorn_part_74_2.zip: пишем код
Popcorn_part_74_1.zip - https://drive.google.com/file/d/1LXsbZa_xODWoONrkb28rqmdZELfWdalI
Popcorn_part_74_2.zip - https://drive.google.com/file/d/1FmiR_cgRjo7o6RRRr-naata04YbMe_n4

Часть #75. Движение монстра, этап 3[править]

00:00 - Движение монстра, этап 3
00:17 - Уничтожаем монстра в AMonster::Check_Hit()
04:50 - Баг: монстры залазят за линию пола
06:15 - Баги: монстры остаются после утраты мячика и мячик отражается от взрывов
07:52 - Убираем отражение от взрывов в AMonster::Check_Hit()
08:59 - Баг: остаётся кусочек платформы при растаивании
09:20 - Баг: двойное выкатывание платформы
09:44 - Баг: остаются красные пиксели от растаявшей платформы
10:38 - Воспроизводим баг с красными пикселями
13:36 - Закажем перерисовку в Act_For_Meltdown_State()
17:31 - Запретим выход монстров в 7-м гейте в Long_Open_Gate()
20:00 - Делаем корректную остановку уровня в Play_Level()
21:52 - Добавим метод Destroy_All()
23:02 - Добавим метод Disable_All()
25:15 - Добавим в платформу список хит-чекеров
27:42 - Добавим обработку хит-чекеров в методе Advance() платформы
30:17 - Добавим третий перегруженный метод Check_Hit()
33:03 - Проверяем столкновение прямоугольников в AMonster::Check_Hit()
36:32 - Исправим баг с остатком платформы при растаивании
38:21 - Popcorn_part_75.zip: пишем код
Popcorn_part_75.zip - https://drive.google.com/file/d/1xCYxI-kgzOvoW0ZujMj3gaBz3dh0t-kO

Часть #76. Двойная буферизация[править]

00:00 - Двойная буферизация
00:19 - Исправим мерцание мячика
01:56 - Анализируем причину мерцания изображения
03:11 - Что такое "двойная буферизация"
05:02 - Буфер и контекст устройства
06:29 - Смотрим в MSDN как создавать контекст устройства
18:52 - Создаём контекст и используем его при обработке WM_PAINT
24:51 - Вынесем код в метод On_Paint()
26:59 - Отрефакторим On_Paint(), чтобы не создавать контекст на каждый кадр
32:25 - Выносим функциональность контекста в класс AsFrame_DC
39:38 - Добавим деструктор ~AsFrame_DC()
41:18 - Зальём контекст фоновым цветом и поправим имена в AsFrame_DC
43:18 - Добавим класс AsMain_Window и метод Main()
47:03 - Перенесём методы главного окна в класс AsMain_Window
52:10 - Перенесём глобальные данные в класс AsMain_Window
55:23 - Добавим статический указатель на главное окно
58:31 - Отрефакторим аргументы методов класса AsMain_Window
01:05:39 - Popcorn_part_76.zip: пишем код
Popcorn_part_76.zip - https://drive.google.com/file/d/1BowISKSQIQLW_cFx1a_u7hpUodez5aF_

Часть #77. Монстр Комета[править]

00:00 - Монстр "Комета"
00:17 - Выбираем лучший способ реализации второго монстра
03:46 - Добавим пару классов AMonster_Eye и AMonster_Comet
05:50 - Сделаем виртуальным метод Draw_Alive()
08:23 - Сделаем виртуальным метод Act_Alive()
09:15 - Добавим пустые методы классов монстра и кометы
10:49 - Переносим код методов из AMonster в AMonster_Eye
11:34 - Разделим данные между AMonster и AMonster_Eye
16:31 - Вынесем код в метод On_Activation()
20:00 - Заменим экземпляры монстров на указатели в наборе монстров
22:25 - Добавим проверку на нулевой указатель
27:22 - Баг: монстры не появляются из гейта
29:59 - Удаляем монстров в методе Act()
32:04 - Добавим метод Is_Finished() и уберём Is_Active()
34:52 - Баг: монстр повторно взрывается при утрате мячика
37:46 - Вынсем код в метод Change_Direction()
40:27 - Popcorn_part_77_1.zip: пишем код
40:53 - Сделаем копипастом поворот пространства для Draw_Alive()
47:05 - Готовим чертёж монстра-кометы
51:43 - Рисуем фон и шарик кометы
58:14 - Рисуем шлейф кометы
59:33 - Добавим цвет Monster_Comet_Tail
01:00:58 - Рисуем дугу шлейфа
01:06:47 - Рисуем вторую дугу
01:10:48 - Рисуем вторую комету
01:14:41 - Делаем анимацию вращения комет
01:22:05 - Добавим монстру-комете свой метод Clear()
01:24:37 - Зададим случайную скорость вращения
01:27:19 - Добавим случайный выбор монстра
01:28:36 - Баг: монстры не анимируются в гейте
01:30:44 - Popcorn_part_77_2.zip: пишем код
Popcorn_part_77_1.zip - https://drive.google.com/file/d/1-SrmeGtujMEjIRSPrJVZM30uLmVkr15V
Popcorn_part_77_2.zip - https://drive.google.com/file/d/1e-SUuW89ixLFyjgBrAGjduV32RmuchcI

Часть #78. Логотип игры[править]

00:00 - Логотип игры
02:02 - Перенесём в движок константу Timer_ID
03:39 - Добавим класс AsInfo_Panel
07:04 - Добавим в движок модуль Info_Panel
07:28 - Добавим заготовки методов инфо-панели
08:50 - Определяем позиции и размеры верхней и нижней плашки
13:49 - Рисуем прямоугольники плашек
18:14 - Уберём чёрную однопиксельную полоску
19:38 - Решаем, как нам лучше вывести надписи
21:38 - Смотрим в MSDN описание функции TextOut()
23:55 - Выводим строку логотипа в методе Draw()
29:52 - Ищем в MSDN пример создания логического шрифта
32:57 - Добавим выбор шрифта в нашу программу
36:40 - Выбираем подходящие шрифты
41:03 - Создадим логический шрифт для Arial
49:38 - Вынесем код в метод Choose_Font()
50:33 - Ищем в MSDN функции для цвета текста
53:05 - Применяем функции для установки цвета текста
55:01 - Выводим тень под буквами
01:00:57 - Сделаем шрифт Logo_Pop_Font
01:07:22 - Popcorn_part_78.zip: пишем код
Popcorn_part_78.zip - https://drive.google.com/file/d/1XVJklxjd2jlo1yvvfjhvyzSNFhwpfdez

Часть #79. Информационная панель[править]

00:00 - Информационная панель
00:19 - Определяем, как рисовать рамку
02:10 - Рисуем белую линию
05:14 - Выносим значения координат в переменные
09:00 - Добавим цвет и отрисуем фоновую линию
11:22 - Перенесём цвета рамки в класс AsInfo_Panel и проинициализируем их в методе Init()
15:31 - Добавим деструктор класса AsInfo_Panel
16:00 - Перенесём код создания шрифтов в Init()
16:53 - Вызовем Init() в методе движка Init_Engine()
17:40 - Добавим деструктор цвета ~AColor()
19:30 - Подберём оттенок цвета для фона
21:08 - Добавим цвет фона Dark_Blue и применим его
23:57 - Повышаем эффективность отрисовки рамки
26:03 - Рисуем плашку под именем игрока
28:32 - Добавим цвет Dark_Red
30:22 - Добавим вторую плашку под счёт
31:32 - Добавим шрифт для имени игрока Name_Font
32:25 - Выводим имя игрока
33:34 - Вычисляем длину строки
35:29 - Увеличим шрифт и отцентрируем имя игрока
37:01 - Применим моноширинный шрифт
42:11 - Вычисляем длину строки
46:02 - Центрируем строку в плашке
48:33 - Вынесем координаты плашки в RECT
51:57 - Перенесём код в метод Draw_String()
53:54 - Выведем строку счёта
55:45 - Добавим тень под текстом
58:57 - Вынесем константы счёта в класс
01:01:07 - Добавим шрифт для счёта Score_Font
01:04:44 - Выведем счёт белым цветом
01:06:34 - Выведем падающую букву "П" для индикатора пола
01:10:51 - Добавим в падающую букву метод Show_Full_Size()
01:13:09 - Добавим буквы для жизней и монстров
01:15:59 - Рисуем индикатор пола
01:21:20 - Рисуем два оставшихся индикатора
01:23:03 - Выравниваем индикаторы и буквы
01:28:21 - Рисуем дополнительную жизнь
01:35:05 - Выносим код в метод Draw_Extra_Life()
01:36:56 - Выводим все дополнительные жизни
01:41:33 - Popcorn_part_79.zip: пишем код
Popcorn_part_79.zip - https://drive.google.com/file/d/1fi9zfvdCnyFSz_BOWySIfvR37hmiaTE1

Часть #80. Строки в информационной панели[править]

00:00 - Строки в информационной панели
00:19 - Выносим класс инфопанели в отдельную пару файлов
02:39 - Создадим фильтр #Screen
04:13 - Popcorn_part_80_1.zip: пишем код
04:32 - Про строки из стандартной библиотеки
05:56 - Краткий обзор стандартной библиотеки
09:49 - Обзор строкового класса std::string
17:15 - Добавим переменную Player_Name
19:52 - Добавим переменную Score
20:54 - Выведем текущий счёт игрока
22:54 - Про функцию itoa()
26:45 - Добавим к строке строковое представление числа
30:35 - Выведем каждый тик таймера
32:39 - Добавим наш строковый класс AString
35:36 - Добавим пару конструкторов в AString
37:17 - Будем передавать ссылку на наш AString вместо const wchar_t *
38:10 - Добавим метод Append()
40:17 - Добавим метод Get_Content()
41:46 - Добавим метод Get_Length()
43:31 - Про функцию sprintf()
46:04 - Про формат вывода функции sprintf()
48:05 - Применим sprintf() для вывода счёта
50:08 - Перенесём класс AString в Common.cpp/h
51:19 - Вынесем код в метод Show_Extra_Lives()
52:55 - Выведем количество оставшихся жизней
56:05 - Popcorn_part_80_2.zip: пишем код
Popcorn_part_80_1.zip - https://drive.google.com/file/d/1891W55S7eh8c-PesYwVDr1IBDf34IQDS
Popcorn_part_80_2.zip - https://drive.google.com/file/d/1PYdOBXqdyP67R3CicrcKwIaNsqqKUokF

Часть #81. Вектор std vector[править]

00:00 - Вектор std::vector
00:19 - Контейнеры стандартной библиотеки
03:33 - Пример применения вектора
11:10 - Как мы могли бы применить вектор в нашей программе
13:15 - Пример создания вектора произвольного размера
16:16 - Заменим в движке массив модулей на вектор
21:38 - Исправим добавление модуля в Add_Next_Module()
26:07 - Делаем range-based for цикл по вектору в Act()
32:03 - Исправим циклы в Draw_Frame()
33:28 - Исправим циклы в Advance_Movers()
36:02 - Какие массивы лучше не переводить в вектор
38:13 - Заменим массив падающих букв на вектор
41:14 - Исправим цикл в методе Act_Objects()
43:53 - Цикл по вектору через итератор
56:22 - Исправим цикл в Clear_Objects()
59:02 - Исправим цикл из Draw_Objects()
01:00:35 - Исправим Get_Next_Falling_Letter()
01:02:24 - Исправим Add_Falling_Letter()
01:04:16 - Исправим Delete_Objects()
01:07:32 - Заменим массив активных кирпичей на вектор
01:08:17 - Изменим тип элементов наших векторов
01:10:10 - Исправим Act_Objects(), Get_Next_Falling_Letter() и Create_Active_Brick()
01:11:29 - Исправим Add_New_Active_Brick()
01:12:36 - Удалим лишние методы Delete_Objects(), Act_Objects() и Draw_Objects()
01:14:22 - Popcorn_part_81.zip: пишем код
Popcorn_part_81.zip - https://drive.google.com/file/d/1RWS_nYyCKEsCrYtv0s76SFggri_t6A8T

Часть #82. Замена массивов на вектора[править]

00:00 - Замена массивов на вектора
01:20 - Выбираем массивы для замены на вектора
09:15 - Заменим массив мячиков на вектор
10:57 - Добавим конструктор AsBall_Set
12:58 - Исправим цикл в Act()
15:22 - Исправим все циклы по мячикам
20:25 - Исправим Get_Next_Game_Object()
21:19 - Заменим массив гейтов на вектор и исправим деструктор рамки
24:22 - Исправим конструктор рамки
25:20 - Исправим метод Open_Gate()
26:48 - Исправим метод Long_Open_Gate()
27:56 - Исправим остальные методы рамки
31:25 - Заменим массив хит-чекеров на вектор
34:33 - Заменим массив лазерных лучей на вектор
38:11 - Заменим массив взрывающихся шариков на вектор
41:32 - Заменим массив тиков моргания глаза на вектор
43:38 - Заменим массив монстров на вектор
51:10 - Исправим выпускание монстра в Emit_At_Gate()
54:06 - Исправим Destroy_All() и Get_Next_Game_Object()
56:27 - Переключим проект обратно в архитектуру x86
58:59 - Исправляем предупреждения компилятора
01:02:02 - Баг: шарики взрыва угасают не правильно
01:09:57 - Добавим operator = в класс AColor
01:13:07 - Заменим operator = на метод Set_As()
01:17:14 - Перенесём класс AColor в Common.cpp/h
01:18:46 - Assertion при растраивании кирпичей
01:23:15 - Баг: первый кирпич тает не до конца
01:27:05 - Баг: мячики не правильно растроились
01:30:52 - Вычислим разницу между текущим и новым направлением
01:33:43 - Пытаемся воспроизвести баг
01:37:05 - Схема того, как надо разводить мячики
01:38:55 - Исправляем поворот мячиков
01:40:23 - Вынесем код в метод Turn_Tripled_Ball()
01:45:48 - Popcorn_part_82.zip: пишем код
Popcorn_part_82.zip - https://drive.google.com/file/d/1MwvWew_3poNwRFJyblNZakN35PYptu4G

Часть #83. Подсчёт очков и убывающие индикаторы[править]

00:00 - Подсчёт очков и убывающие индикаторы
00:59 - Смотрим, сколько очков дают за действия в оригинальной игре
05:57 - Опишем типы игровых событий через EScore_Event_Type
07:33 - Добавим метод Update_Score()
10:07 - Подсчитываем очки при попадании в кирпич
11:48 - Подсчитываем очки при попадании в монстра
12:27 - Подсчитываем очки при ловле буквы
13:05 - Перенесём подсчёт очков и жизней в инфопанель
14:57 - Закажем перерисовку инфопанели в Update_Score()
18:23 - Разделим область инфопанели на две части
24:13 - Исправим метод Draw() рисования инфопанели
26:58 - Вынесем позиции логотипа в константы
29:30 - Смотрим, как угасают индикаторы в оригинальной игре
31:10 - Рисуем внутреннюю часть индикатора
33:16 - Добавим класс AIndicator
36:02 - Добавим два индикатора в инфопанель
38:05 - Добавим код рисования индикаторов в метод Draw()
41:22 - Включаем индикатор пола
45:25 - Рисуем убывающую шкалу индикатора
50:26 - Добавим анимацию убывания шкалы
55:04 - Отладим анимацию индикатора
01:00:05 - Popcorn_part_83.zip: пишем код
Popcorn_part_83.zip - https://drive.google.com/file/d/1YX58m7S80Kf0B5wjtFLjXNVJekLcmFqH

Часть #84. Передача и обработка сообщений индикаторов[править]

00:00 - Передача и обработка сообщений индикаторов
00:17 - Как сейчас работают наши индикаторы
03:04 - Делаем прототип передачи сообщения
05:35 - Делаем прототип приёма и обработки сообщения
08:57 - Добавим тип и класс сообщений
11:22 - Добавим методы для приёма и передачи сообщений
17:23 - Вынесем очередь сообщений и её методы в AsMessage_Manager
21:20 - Проверяем работу очереди сообщений
25:34 - Баг: отключённый пол не стирается
29:04 - Добавим тип сообщения в индикатор
31:28 - Выносим код обработки сообщения в Handle_Message()
33:13 - Добавим остановку монстров Freeze()
35:59 - Исправим Freeze() на Set_Freeze_State()
38:49 - Баг: замёрзший монстр выходит из гейта
42:59 - Баг: гейты открываются для замороженных монстров
44:35 - Исправляем баг с застрявшим в гейте монстром
49:25 - Добавим индикаторам метод сброса Reset()
52:09 - Исправим Message_Was_Sent на Neet_To_Notify
54:05 - Вынесем AIndicator в свою пару файлов
55:32 - Popcorn_part_84_1: пишем код
56:27 - Обновим план разработки
58:55 - Отобразим изменение дополнительных жизней на индикаторе
01:04:01 - Уменьшим количество жизней при утрате мячика
01:08:49 - Сделаем обработку отмены действий методом Cancel()
01:13:08 - Посмотрим, как отменяются состояния платформы
01:14:53 - Баг: лазерная платформа не отменяется
01:21:53 - Popcorn_part_84_2: пишем код
Popcorn_part_84_1.zip - https://drive.google.com/file/d/1mv0nvwEqUX1nUdQ_kWSYkKWLt37kU5ve
Popcorn_part_84_2.zip - https://drive.google.com/file/d/1W3DRQ4guI5XP8ynsDCGDD2Oazw2ZwTGx

Часть #85. Делаем первые 4 уровня игры[править]

00:00 - Делаем первые 4 уровня игры
00:43 - Как представлены уровни в нашей игре
01:43 - Исправим 1-й уровень
02:31 - Сделаем 2-й уровень
06:37 - Заменим массив Teleport_Brick_Pos на вектор
08:59 - Исправим сканирование телепортов в Set_Current_Level()
10:51 - Добавим конструкторы в структуру SPoint
15:23 - Продолжим исправление Set_Current_Level()
17:28 - Применим метод вектора emplace_back()
19:58 - Исправим использование размера вектора телепортов
22:06 - Вынесем данные уровня в класс ALevel_Data
27:11 - Исправим метод Set_Current_Level()
29:55 - Добавим вектор уровней
32:00 - Создадим уровни в методе уровня Init()
36:25 - Вынесем класс ALevel_Data в отдельную пару файлов
38:31 - Popcorn_part_85_1.zip: пишем код
38:46 - Добавим заготовки под остальные 8 уровней
40:26 - Сделаем 3-й уровень
43:03 - Баг: монстры застряют в гейтах
49:24 - Сделаем 4-й уровень
52:17 - Поднимем гейты 2 и 3 на 1 игровой пиксель
54:12 - Опустим монстра на полпикселя
57:11 - Баг: монстр цепляется за ячейку
01:08:32 - Баг: монстры ходят сквозь кирпичи
01:20:18 - Уменьшим шаг поворота угла для монстра
01:24:51 - Popcorn_part_85_2.zip: пишем код
Popcorn_part_85_1.zip - https://drive.google.com/file/d/1-yaNOLMRWwzPrZ0b1aC4c4Y2mPysEh9I
Popcorn_part_85_2.zip - https://drive.google.com/file/d/122t5JcSCJDheGp5y6dEWytsoCnCfo2Qn

Часть #86. Делаем остальные 6 уровней игры[править]

00:00 - Делаем остальные 6 уровней игры
00:22 - Добавим 5-й уровень
03:11 - Добавим 6-й уровень
06:17 - Мячик пролетел сквозь кирпичи
14:50 - Баг: мячик, приклеенный на платформу, не смещается
19:00 - Баг: монстры застряют в нижних гейтах
26:14 - Баг: монстры заходят на кирпичи
35:39 - Исправим период выбора направления движения монстров
38:01 - Добавим 7-й уровень
40:45 - Баг: мячик на парашюте улетает вверх
44:11 - Баг: мячик на парашюте стирает кирпичи
46:39 - Уберём отражение от кирпича для мячика на парашюте
50:39 - Баг: мячик летает почти вертикально
01:01:10 - Добавим 8-й уровень
01:05:38 - Поместим указатели на уровни в массив Levels_Array
01:09:04 - Добавим в описание уровня указатель на рекламу
01:14:36 - Добавим 9-й уровень
01:19:18 - Добавим 10-й уровень
01:23:45 - Исправим генерацию случайного числа в Rand()
01:26:02 - Popcorn_part_86.zip: пишем код
Popcorn_part_86.zip - https://drive.google.com/file/d/1KnYiIcOqBTG7ScIYEMb7JVC3isTGdt3y

Часть #87. Рисуем швабру, стирающую уровень[править]

00:00 - Рисуем швабру, стирающую уровень
00:19 - Обновим статус задач проекта
01:53 - Смотрим на оригинальную анимацию перехода на новый уровень
08:09 - Подбираем имя класса для швабры
12:28 - Создадим класс AsMop
15:16 - Рисуем стирающую часть швабры
20:13 - Рисуем индикатор швабры
30:44 - Рисуем синий индикатор
33:22 - Добавим класс AMop_Indicator
36:28 - Перенесём код рисования индикатора в его метод Draw()
39:42 - Добавим Indicator_Rect
43:12 - Создадим множество индикаторов
51:15 - Добавим угасающие оттенки цвета индикаторов
54:20 - Вынесем код в класс AColor_Fade
59:23 - Заменим массив оттенков на AColor_Fade для шариков взрыва монстра
01:03:53 - Исправим Get_Fading_Color()
01:11:40 - Применим AColor_Fade для красно-синих кирпичей и перенесём AColor_Fade в конфиг
01:15:47 - Применим AColor_Fade для индикаторов швабры
01:16:40 - Сделаем анимацию индикатора
01:26:39 - Добавим перегрузку метода Get_Fading_Color()
01:29:20 - Сделаем растяжку от синего к красному для индикаторов
01:31:29 - Добавим смещение по времени для индикаторов
01:33:02 - Настроим анимацию индикаторов
01:35:04 - Popcorn_part_87.zip: пишем код
Popcorn_part_87.zip - https://drive.google.com/file/d/1bF53twI8CbclhFAmqTNhwuSn00yvIoTn

Часть #88. Анимация швабры[править]

00:00 - Анимация швабры
00:49 - Добавим класс AMop_Cylinder
03:54 - Добавим первый цилиндр в швабру
08:26 - Рисуем основную часть цилиндра
12:20 - Рисуем крепление цилиндра
17:59 - Рисуем все цилиндры швабры
23:18 - Добавим очистку уровня в Erase_Level()
28:07 - Сделаем изменение позиции по Y для индикаторов и цилиндров
34:08 - Сделаем анимацию раздвигания цилиндров
38:11 - Добавим в швабру метод Set_Mop()
40:32 - Добавим в цилиндр метод Set_Height_For()
44:09 - Померяем высоту цилиндров
48:03 - Добавим перерисовку прямоугольника швабры
50:58 - Вычислим высоту всех цилиндров
54:15 - Добавим перерисовку предыдущего прямоугольника швабры
55:24 - Добавим стирание для швабры в методе Clear()
57:49 - Добавим сеттер для позиции по Y цилиндра
01:02:04 - Вычислим текущую высоту каждого цилиндра
01:05:47 - Добавим стирание цилиндра в методе Clear()
01:08:05 - Исправим высоту стирания цилиндра
01:09:51 - Исправим интерпретацию высоты цилиндра
01:18:18 - Исправим массив с высотой цилиндров
01:21:55 - Увеличим скорость анимации
01:23:17 - Вынесем класс AsMop в свою пару файлов
01:25:13 - Вынесем класс AMop_Indicator в свою пару файлов
01:26:25 - Вынесем класс AMop_Cylinder в свою пару файлов
01:28:15 - Popcorn_part_88.zip: пишем код
Popcorn_part_88.zip - https://drive.google.com/file/d/1yQ9kOMpbCZbxwoM4ZSO84nKdtxx068P_

Часть #89. Смена уровней[править]

00:00 - Смена уровней
00:31 - Стираем уровень под шваброй
02:22 - Добавим метод Clear_Area()
05:09 - Добавим новое игровое состояние Mop_Level
08:35 - Добавим в уровень метод Mop_Level()
11:05 - Добавим в уровень метод Is_Level_Mopping_Done()
12:19 - Добавим перечисление EMop_State
16:02 - Учитываем состояния швабры в методе анимации Act()
19:29 - Добавим геттер Get_Mop_State()
21:54 - Добавим метод Show_Level()
23:11 - Сведём методы Erase_Level() и Show_Level() в Activate()
25:51 - Добавим состояния швабры для подъёма и опускания
27:20 - Исправим использование состояний швабры
28:45 - Задаём стартовую позицию сложенной швабры
32:25 - Добавим метод Get_Cylinders_Height()
34:03 - Делаем анимацию подъёма швабры
39:12 - Переводим анимацию в расширение после подъёма
40:44 - Переводим анимацию в спуск после опускания
45:02 - Рефакторим машину состояний анимации швабры
48:30 - Вынесем код в Act_Lifting()
55:26 - Оптимизация машины состояний для стирания и показа уровня
59:52 - Финальный рефакторинг машины состояний
01:01:48 - Popcorn_part_89.zip: пишем код
Popcorn_part_89.zip - https://drive.google.com/file/d/1RuYrSe2bZqy1UcMDn748oQ4Y6eAIY0uo

Часть #90. Настройка перехода по уровням игры[править]

00:00 - Настройка перехода по уровням игры
00:20 - Обновим состояние плана разработки
01:13 - Исправим конструктор ALevel_Data
04:40 - Добавим метод Get_Available_Bricks_Count()
10:44 - Добавим учёт оставшихся кирпичей
15:26 - Отправим новое сообщение Level_Done
18:03 - Добавим обработку сообщения в Handle_Message()
20:10 - Добавим заглушку для Game_Won()
20:49 - Вынесем код в метод Stop_Play()
22:39 - Добавим метод Mop_Next_Level()
27:02 - Уменьшим количество учитываемых кирпичей до 3
28:06 - Добавим метод Is_Mopping_Done()
34:04 - Добавим метод Is_Cleaning_Done()
35:43 - Баг: множество сообщений об окончании уровня
38:01 - Добавим метод Disable_All_Balls()
42:35 - Добавим состояние игры Finish_Level
46:32 - Добавим метод Can_Mop_Next_Level()
48:13 - Добавим метод Are_All_Destroyed()
49:25 - Исправим удаление монстров
52:18 - Исправим стирание мячиков
54:59 - Баг: количество жизней уменьшается при успешном завершении уровня
57:23 - Баг: монстр всё время выходит из одного и того же гейта
58:38 - Баг: монстр опять застрял в гейте
01:06:20 - Обновим план разработки
01:07:30 - Popcorn_part_90.zip: пишем код
Popcorn_part_90.zip - https://drive.google.com/file/d/1u2DCLoA7Motz0bflCI48oV72AoWAvp9J

Часть #91. Контролы, виджеты и табличка уровня[править]

00:00 - Контролы, виджеты и табличка уровня
00:22 - Смотрим на табличку уровня в оригинальной игре
02:32 - Добавим в уровень объект Level_Title
04:13 - Добавим класс AsLevel_Title
07:30 - Применим переменную Is_Visible в методах
08:07 - Добавим вывод надписи "УРОВЕНЬ"
11:24 - Добавим методы Show_Title() и Hide_Title()
15:25 - Про контролы и виджеты
21:50 - Добавим класс ALabel
29:05 - Применим метку для вывода имени игрока
33:04 - Пытаемся задать фонт для метки
36:29 - Добавим класс AFont
40:22 - Применим AFont как шрифт для метки
42:39 - Использование ссылки на объект при инициализации
45:40 - Добавим фонту метод Select()
48:21 - Сделаем вывод счёта через метку
52:42 - Добавим в метку цвет
54:47 - Перенесём создание лого-фонтов в конфиг
57:08 - Перенесём цвета Dark_Red и Dark_Blue в конструктор
59:59 - Выводим табличку уровня двумя метками
01:06:46 - Добавим прямоугольник Title_Rect
01:08:48 - Выведем табличку после очистки уровня
01:10:24 - Добавим состояния таблички ELevel_Title_State
01:14:45 - Добавим количество знаков для строкового представления числа
01:21:23 - Баг: постоянная перерисовка инфопанели
01:23:37 - Проверяем переходы по уровням
01:25:28 - Исправим условия включения моппинга уровня
01:27:30 - Popcorn_part_91.zip: пишем код
Popcorn_part_91.zip - https://drive.google.com/file/d/1EfYL7B1NHpJbz40AzCoeXhKNFMi2m6mT

Часть #92. Финальные титры[править]

00:00 - Финальные титры
00:27 - Анализируем оригинальную анимацию финальных титров
03:55 - Добавим класс AFinal_Letter
11:29 - Добавим букву "К" в уровень
14:35 - Выберем шрифт "MV Boli"
19:05 - Выберем шрифт "Impact"
22:31 - Выберем шрифт "Comic Sans MS" с курсивом
24:33 - Добавим в класс AFont поддержку курсива
26:48 - Подберём размер символов
28:33 - Исправим рисование AFinal_Letter
33:01 - Добавим координаты для финальной буквы
37:37 - Добавим массив букв "КОНЕЦ ИГРЫ"
41:53 - Выравниваем буквы по горизонтали
49:50 - Вынесем функциональность титров в AsGame_Title
55:22 - Добавим в AsGame_Title метод Show()
59:11 - Добавим массив букв "ПОБЕДА!"
01:00:47 - Выравниваем буквы победы по горизонтали
01:06:10 - Смотрим оригинальную анимацию уничтожения букв
01:08:08 - Добавим состояния EGame_Title_State
01:10:36 - Добавим стирание финальных титров
01:16:51 - Включаем надпись при утрате мячика
01:19:46 - Выводим надпись под кирпичами
01:24:32 - Вынесем код в метод Is_Destroying_Complete()
01:28:59 - Делаем анимацию спуска надписи
01:40:56 - Баг: надпись не едет
01:44:01 - Popcorn_part_92.zip: пишем код
Popcorn_part_92.zip - https://drive.google.com/file/d/1ttnI6qumNVQLvHiz47kzXth4VunAzSuz

Часть #93. Анимация финальных титров[править]

00:00 - Анимация финальных титров
00:20 - Добавим состояние титров Game_Over_Show
05:47 - Добавим обработку состояния Game_Over_Destroy
10:06 - Вынесем логику взрывающихся шариков в класс AExplosion
15:23 - Переносим код начала взрывов в Start_Explosion()
20:33 - Перенесём метод Draw_Destroying() в Draw_Explosion()
22:33 - Вынесем анимацию в метод Act_On_Explosion()
27:34 - Popcorn_part_93_1.zip: пишем код
27:47 - Вынесем класс AExplosion в свою пару файлов
30:38 - Вынесем класс AsLevel_Title
31:55 - Вынесем класс ALabel
33:53 - Вынесем класс AFinal_Letter
35:17 - Вынесем класс AsGame_Title
39:07 - Popcorn_part_93_2.zip: пишем код
39:23 - Отнаследуем финальную букву от AExplosion
44:35 - Добавим анимацию и рисование взрывов
46:31 - Смотрим в отладчике, как уничтожаются буквы
49:19 - Исправим анимацию финальных титров
53:40 - Добавим код для Is_Finished()
55:00 - Баг: Шарики не рисуются
56:11 - Баг: Шарики анимируются слишком медленно
59:10 - Баг: Шарики не закрывают буквы полностью
01:00:44 - Вынесем код в метод Destroy_Letters()
01:05:40 - Вычислим размеры буквы
01:10:05 - Баг: остаются кусочки букв
01:21:10 - Добавим состояния финальной буквы
01:23:57 - Исправим отрисовку буквы по состояниям
01:30:20 - Вынесем код в Draw_Letter()
01:32:38 - Popcorn_part_93_3.zip: пишем код
Popcorn_part_93_1.zip - https://drive.google.com/file/d/1e5kY6JwZj3GCSkVjvqirEp44UgvnB1bv
Popcorn_part_93_2.zip - https://drive.google.com/file/d/1qdftfOYH46dGaK9sb2I5C259SGsiv5Xx
Popcorn_part_93_3.zip - https://drive.google.com/file/d/1geRzs_gZUhNqfvnmN5Cz6uNWUqs6Mb0b

Часть #94. Победа в игре и ввод имени игрока[править]

00:00 - Победа в игре и ввод имени игрока
00:19 - Отладим вывод надписи "Победа"
01:55 - Добавим анимацию букв "Победа"
04:05 - Добавим метод Animate_Game_Won()
07:00 - Добавим метод Set_Color()
10:46 - Баг: победа не едет
12:15 - Баг: буквы не перемигиваются
13:31 - Добавим состояние Color_Letter для финальной буквы
15:21 - Вынесем код в Setup_Letter_Rect()
17:24 - Включим "победу" при успешном завершении игры
19:36 - Добавим состояние игры Game_Won
23:09 - Баг: восклицательный знак закрашивается не до конца
24:52 - Баг: буквы "КОНЕЦ ИГРЫ" обрезаются при взрывах
28:29 - Баг: всё время находятся в состоянии конца анимации
31:55 - Popcorn_part_94_1.zip: пишем код
32:11 - Обновим план
33:30 - Добавим состояние для ввода имени игрока
36:37 - Смотрим в MSDN про сообщение WM_CHAR
39:14 - Добавим обработку WM_CHAR и метод On_Char()
40:50 - Добавим метод Edit_Player_Name()
44:43 - Добавим в строку перегруженный метод Append()
48:11 - Баг: буквы при вводе не отображаются
49:28 - Добавим в класс метки метод Append()
51:29 - Делаем отложенное вычисление длины строки
54:17 - Добавим метод Delete_Last_Symbol()
57:36 - Добавим удаление символов
01:00:05 - Смотрим символы на unicode-table.com
01:06:45 - Поднимаем регистр букв
01:12:29 - Добавим обработку Enter при вводе имени
01:17:00 - Добавим надпись "ВВЕДИТЕ ИМЯ"
01:19:44 - Сделаем мигающую надпись
01:24:41 - Баг: редактирование надписи "ВВЕДИТЕ ИМЯ"
01:27:58 - Popcorn_part_94_2.zip: пишем код
Popcorn_part_94_1.zip - https://drive.google.com/file/d/1goQKN_VTRBn6vW0skwKrscSgEBEGcDAF
Popcorn_part_94_2.zip - https://drive.google.com/file/d/1HZrpu87FfyJSLZNl4ysqhst7_ALEzWWo

Часть #95. Финальная настройка и завершение проекта[править]

00:00 - Финальная настройка и завершение проекта
00:19 - Обновим план
01:31 - Сохраним все исходные файлы в формате Юникода
04:31 - Уберём комментарии "//!!! Надо сделать!"
06:09 - Исправим наследование швабры AsMop
07:42 - Представим строками заголовок и класс окна
12:25 - Убираем закомментированный код
18:41 - Popcorn_part_95_1.zip: пишем код
19:10 - Исправим количество жизней
20:11 - Смотрим в оригинальной игре количество очков за кирпичи
26:50 - Исправим подсчёт очков в Update_Score()
30:26 - Исправим подсчёт очков в On_Hit()
32:35 - Проверим подсчёт очков
35:16 - Подсчитаем вероятность выпадения букв в оригинальной игре
39:21 - Установим вероятность выпадения буквы
40:00 - Установим лимит монстров
43:18 - Баг: чёрные кирпичи
45:29 - Перенесём оттенки угасающего цвета в конфиг
53:36 - Исправим Draw_Brick_Letter(), чтобы убрать точки на экране
57:32 - Баг: редактирование имени не прекратилось
59:13 - Поиграем в нашу игру
59:48 - Баг: активные кирпичи не заканчивают свою анимацию
01:03:21 - Играем 1-й уровень
01:05:03 - Баг: мячик зациклился при отражении от платформы
01:12:58 - Баг: гейты открываются, не выпуская монстров
01:19:46 - Баг: мячик на парашюте летит вверх
01:22:30 - Баг: мячик застрял на платформе
01:29:37 - Баг: 8-й уровень не закончился
01:35:20 - Вынесем код в метод Set_Floor_State()
01:37:52 - Увеличим максимальное количество активных кирпичей
01:39:07 - Исправим баг с отражением мячика на парашюте от пола
01:41:46 - Исправим баг с пикселями, остающимися от парашюта
01:46:02 - Воспроизведём зацикленность мячика на тестовом уровне
01:56:47 - Добавим метод Detect_Hits_Cycling()
01:59:50 - Добавим циклический буфер координат
02:05:06 - Сканируем массив окнами в поисках паттерна
02:11:13 - Добавим оператор сравнения в APoint
02:12:34 - Добавим метод Set_As() в APoint
02:13:55 - Проверяем алгоритм поиска паттерна
02:20:48 - Смотрим на стили окна в MSDN
02:23:05 - Уберём ресайз окна нашей программы
02:24:24 - Исправим ускорение мячика
02:25:12 - Popcorn_part_95_2.zip: пишем код
02:25:28 - Обновим план и скомпилируем игру в релизе
02:27:40 - Мы заканчиваем наш проект!
Popcorn_part_95_1.zip - https://drive.google.com/file/d/1hPpTb0keb1BjiKzaGM0KKouuFzc_1mAk
Popcorn_part_95_2.zip - https://drive.google.com/file/d/1QLKKoOv4_HTDWFeXg5o47EHpqXXs04I2