Логарифм и резинки
#код
Как-то в одном из чатов обронили фразу: «а когда вам последний раз был нужен логарифм?». Забавно, но мне он потребовался буквально на следующий день. Это ещё один маленький пост о том, какого рода математика нужна геймплейному программисту в повседневной жизни.
Часто нам нужно сглаживать какие-то процессы, у которых нет чёткого конца или он меняется во времени. Ну, например, у нас один объект — пусть это будет ручной дракончик — движется на воображаемой резинке за другим объектом, который тоже постоянно в движении: скажем, это будет персонаж игрока. Если игрок оказался далеко от дракончика (например, телепортировался), то дракончик сперва летит к нему очень быстро, но при приближении постепенно замедляется, чтобы это смотрелось хорошо.
Новички обычно просто домножают скорость дракончика на расстояние до игрока. Это простое решение, но ужасно плохое, поскольку время, за которое дракончик долетит до игрока, будет напрямую зависеть от FPS. При низком фпс он будет добираться до цели быстро, а при большом топтаться на месте неожиданно долго. А если FPS скачет, то и вовсе придётся лицезреть нечто рывкообразное.
Юнитологи классом повыше обычно используют небезызвестную функцию SmoothDamp. Внутри там скрывается мудрённое решение из книги Game Programming Gems 4. Вот только нам приходится где-то хранить текущую скорость для каждого процесса сглаживания, да и в целом довольно страшно выглядит. Нельзя ли как-то попроще сделать и без лишних переменных в местах вызова?
На самом деле если мы задумаемся, как будет выглядеть FPS-независимый способ приближения со сглаживанием, то быстро поймём, что нам надо проходить одинаковую долю расстояния за одинаковое время. Например, за первую секунду проходим половину пути, за вторую секунду половину от половины, то есть остаётся четверть, затем 1/8, 1/16 и так далее. И никогда мы по настоящему не достигаем цели, но нам это и не надо. При таком движении неважно в какой точке этого процесса мы оказались (на первой секунде, второй и т.п.), мы всегда знаем, как рассчитать движение дальше. От пути всегда остаётся лишь
Можно думать об этом числе, как о степени агрессивности нашего In в нашем FPS-независимом сглаживании (аналог InCubic, InQuad и т.д.). Формула продолжит работать.
Но для полного счастья нам не хватает настройки времени, за которое дракончик будет визуально догонять персонажа из любой точки. Конечно, полностью он догнать не может, но нам хватит преодоления, скажем, 98% пути:
По-моему, симпатично получилось. А вы что думаете?
#код
Как-то в одном из чатов обронили фразу: «а когда вам последний раз был нужен логарифм?». Забавно, но мне он потребовался буквально на следующий день. Это ещё один маленький пост о том, какого рода математика нужна геймплейному программисту в повседневной жизни.
Часто нам нужно сглаживать какие-то процессы, у которых нет чёткого конца или он меняется во времени. Ну, например, у нас один объект — пусть это будет ручной дракончик — движется на воображаемой резинке за другим объектом, который тоже постоянно в движении: скажем, это будет персонаж игрока. Если игрок оказался далеко от дракончика (например, телепортировался), то дракончик сперва летит к нему очень быстро, но при приближении постепенно замедляется, чтобы это смотрелось хорошо.
Новички обычно просто домножают скорость дракончика на расстояние до игрока. Это простое решение, но ужасно плохое, поскольку время, за которое дракончик долетит до игрока, будет напрямую зависеть от FPS. При низком фпс он будет добираться до цели быстро, а при большом топтаться на месте неожиданно долго. А если FPS скачет, то и вовсе придётся лицезреть нечто рывкообразное.
Юнитологи классом повыше обычно используют небезызвестную функцию SmoothDamp. Внутри там скрывается мудрённое решение из книги Game Programming Gems 4. Вот только нам приходится где-то хранить текущую скорость для каждого процесса сглаживания, да и в целом довольно страшно выглядит. Нельзя ли как-то попроще сделать и без лишних переменных в местах вызова?
На самом деле если мы задумаемся, как будет выглядеть FPS-независимый способ приближения со сглаживанием, то быстро поймём, что нам надо проходить одинаковую долю расстояния за одинаковое время. Например, за первую секунду проходим половину пути, за вторую секунду половину от половины, то есть остаётся четверть, затем 1/8, 1/16 и так далее. И никогда мы по настоящему не достигаем цели, но нам это и не надо. При таком движении неважно в какой точке этого процесса мы оказались (на первой секунде, второй и т.п.), мы всегда знаем, как рассчитать движение дальше. От пути всегда остаётся лишь
1 / 2^tА значит пройденное расстояние от времени вычисляется по формуле:
1 - 1 / 2^tДвойка здесь всего лишь указатель на то, что в качестве одинаковых промежутков мы выбрали половину расстояния. Мы можем подставить туда 3, чтобы получить треть, или любое другое число больше 1.
Но для полного счастья нам не хватает настройки времени, за которое дракончик будет визуально догонять персонажа из любой точки. Конечно, полностью он догнать не может, но нам хватит преодоления, скажем, 98% пути:
1 - 1 / base^t = 0,98Ну вот и всё. Теперь мы можем инициализировать этими параметрами нашу бесконечную резинку-пружинку, после чего ей можно будет скармливать deltaTime, а в ответ получать LerpK. Таким образом получилось простое FPS-независимое сглаживание для всего, что можно лерпать. Финальный класс можно видеть на скриншоте.
base^t = 1 / (1 - 0,98)
t = log(1 / (1 - 0,98), base)
По-моему, симпатично получилось. А вы что думаете?
Поправочка!
#код
Ох, что-то я написал этот пост и тут же понял, что лишнего наворотил. Движение, при котором мы за равные промежутки проходим половину, затем половину от остатка и так далее — это фактически тоже самое движение, когда мы за равные промежутки проходим треть, затем треть от остатка и так далее. Это просто свойство перевёрнутой экспоненты самой по себе. И неважно какое основание. А чтобы управлять агрессивностью In-прыжка, достаточно слегка менять threshold-порог. Чем выше выставить процент порога, тем агрессивнее будет прыжок в начале.
Правду говорят, что если хочешь в чём-то разобраться сам, то расскажи это другому. Вот и здесь так получилось.
#код
Ох, что-то я написал этот пост и тут же понял, что лишнего наворотил. Движение, при котором мы за равные промежутки проходим половину, затем половину от остатка и так далее — это фактически тоже самое движение, когда мы за равные промежутки проходим треть, затем треть от остатка и так далее. Это просто свойство перевёрнутой экспоненты самой по себе. И неважно какое основание. А чтобы управлять агрессивностью In-прыжка, достаточно слегка менять threshold-порог. Чем выше выставить процент порога, тем агрессивнее будет прыжок в начале.
Правду говорят, что если хочешь в чём-то разобраться сам, то расскажи это другому. Вот и здесь так получилось.
public struct EndlessSpring
{
private float TimeScale;
// time is amount of time that needeed to pass
// threshold-share of the distance (never pass 100%)
public EndlessSpring(float time, float threashold = 0.98f)
{
var expectedValue = 1 / (1 - threashold);
this.TimeScale = Mathf.Log(expectedValue, 2) / time;
}
public float GetLerpK(float deltaTime)
{
return 1 - 1 / Mathf.Pow(2, deltaTime * TimeScale);
}
}
По кровавым словам узнаете их
#лайт
Если бы IT было командой черепашек-ниндзя, то игровая индустрия была бы в ней Микеланджело. Ну а среди грызунов-спасателей геймдеву, конечно же, досталась бы роль Дейла. Мы всегда были эдаким младшим и немного придурковатым персонажем IT-мира. Самый весёлый, но наивный раздолбай в гавайской рубашке, обожающий пиццу, комиксы и попадать в дебильные ситуации — это всё мы. И безусловно, нам есть чему поучиться у более старших и серьёзных коллег: от дисциплины и оценки сроков до культуры софтскиллов и ведения документации. У нас всё ещё полно детских болячек отрасли, как прыщей у подростка. Но всё же это не значит, что какой-нибудь занудный Донателло сможет так же ловко управляться с нунчаками, как это делаем мы. Когда дело доходит до владения палками на цепи, стоит спросить совета у Микеланджело.
К сожалению, на фоне всех хороших трендов взросления индустрии, сближение с энтерпрайзом занесло в нашу песочницу и пару куличиков добротного дерьма. Речь идёт о стадах программистов и менеджеров, которые последние пару лет толпами мигрируют к нам из соседних областей, но при этом совершенно игнорируют специфику геймдева. По моему опыту людям из кровавого крайне трудно вкатиться в разработку игр: здесь другой темп написания кода; другие методы тестирования; другая, в конце концов, динамика изменения ТЗ от геймдизайнера. Но есть люди, которые понимают, что теперь работают с произведением искусства, и пытаются адаптироваться; а есть мудилы, которые этого не хотят. Последние — это, как ни парадоксально, люди поопытнее, сформировавшие уже не только собственные привычки, но и солидное ЧСВ. Это тот чувак у вас в команде, который типа прохавал всё IT, много говорит о софтскиллах и о том, как делать правильно, но в общей телеге его колесо всегда волочится по другой колее. Это он треплет нервы всем артистам бюрократией, и именно на его тасках всегда в итоге происходит фичекат.
Частные эманации этого ползучего зла могут быть самые разные, поэтому мне не удастся рассказать, как это выглядит изнутри в каждой конкретной студии, но я могу описать их внешние проявления: по терминологии узнаете их! Уже столько вокруг пролезло бездушного, что их язык стал проникать в нашу среду.
Начну с моего личного чемпиона по мерзости: «мы продуктовая игровая компания!». Если у вас ещё почему-то не задёргался глаз, то поясню. Дело в том, что у них там в большом IT компании сплошь оутсорсовые, консалтинговые и бог знает какие ещё (уж извините, не разбираюсь). Бывает, что люди там годами таски какие-то делают, а пощупать плоды труда нельзя. Поэтому слово «продуктовая» для гребцов в тугих галстуках — это реально порой единственный глоток неденежной мотивации. Но когда HR радостно сообщает мне о «продуктовой игровой компании», для меня это тавтология. Для любого разработчика игр понятие «игровая компания» по умолчанию подразумевает работу над продуктом. Мы пришли в профессию, чтобы делать игры, и в большинстве своём только в таких компаниях и работали. «Продуктовость» это не бонус для нас, а вещь сама собой разумеющаяся. Это вот если вдруг компания не делает собственные игры, а аутсорсит непонятно что — вот тогда уже надо уточнять. Поэтому если вам кто-то из разрабов презентует свою компанию, как «продуктовую», то бегите. Вы там вряд ли встретите крутых артистов, которые делают вещи, или идейных программеров. Если они там были, то разбежались. Судя по лексикону, скорее всего это просто студия-симулякр из энтерпрайзников, где думают не об игре и игроках, а об удовлетворении шизы какого-нибудь заказчика (совсем как в вэбе, да).
#лайт
Если бы IT было командой черепашек-ниндзя, то игровая индустрия была бы в ней Микеланджело. Ну а среди грызунов-спасателей геймдеву, конечно же, досталась бы роль Дейла. Мы всегда были эдаким младшим и немного придурковатым персонажем IT-мира. Самый весёлый, но наивный раздолбай в гавайской рубашке, обожающий пиццу, комиксы и попадать в дебильные ситуации — это всё мы. И безусловно, нам есть чему поучиться у более старших и серьёзных коллег: от дисциплины и оценки сроков до культуры софтскиллов и ведения документации. У нас всё ещё полно детских болячек отрасли, как прыщей у подростка. Но всё же это не значит, что какой-нибудь занудный Донателло сможет так же ловко управляться с нунчаками, как это делаем мы. Когда дело доходит до владения палками на цепи, стоит спросить совета у Микеланджело.
К сожалению, на фоне всех хороших трендов взросления индустрии, сближение с энтерпрайзом занесло в нашу песочницу и пару куличиков добротного дерьма. Речь идёт о стадах программистов и менеджеров, которые последние пару лет толпами мигрируют к нам из соседних областей, но при этом совершенно игнорируют специфику геймдева. По моему опыту людям из кровавого крайне трудно вкатиться в разработку игр: здесь другой темп написания кода; другие методы тестирования; другая, в конце концов, динамика изменения ТЗ от геймдизайнера. Но есть люди, которые понимают, что теперь работают с произведением искусства, и пытаются адаптироваться; а есть мудилы, которые этого не хотят. Последние — это, как ни парадоксально, люди поопытнее, сформировавшие уже не только собственные привычки, но и солидное ЧСВ. Это тот чувак у вас в команде, который типа прохавал всё IT, много говорит о софтскиллах и о том, как делать правильно, но в общей телеге его колесо всегда волочится по другой колее. Это он треплет нервы всем артистам бюрократией, и именно на его тасках всегда в итоге происходит фичекат.
Частные эманации этого ползучего зла могут быть самые разные, поэтому мне не удастся рассказать, как это выглядит изнутри в каждой конкретной студии, но я могу описать их внешние проявления: по терминологии узнаете их! Уже столько вокруг пролезло бездушного, что их язык стал проникать в нашу среду.
Начну с моего личного чемпиона по мерзости: «мы продуктовая игровая компания!». Если у вас ещё почему-то не задёргался глаз, то поясню. Дело в том, что у них там в большом IT компании сплошь оутсорсовые, консалтинговые и бог знает какие ещё (уж извините, не разбираюсь). Бывает, что люди там годами таски какие-то делают, а пощупать плоды труда нельзя. Поэтому слово «продуктовая» для гребцов в тугих галстуках — это реально порой единственный глоток неденежной мотивации. Но когда HR радостно сообщает мне о «продуктовой игровой компании», для меня это тавтология. Для любого разработчика игр понятие «игровая компания» по умолчанию подразумевает работу над продуктом. Мы пришли в профессию, чтобы делать игры, и в большинстве своём только в таких компаниях и работали. «Продуктовость» это не бонус для нас, а вещь сама собой разумеющаяся. Это вот если вдруг компания не делает собственные игры, а аутсорсит непонятно что — вот тогда уже надо уточнять. Поэтому если вам кто-то из разрабов презентует свою компанию, как «продуктовую», то бегите. Вы там вряд ли встретите крутых артистов, которые делают вещи, или идейных программеров. Если они там были, то разбежались. Судя по лексикону, скорее всего это просто студия-симулякр из энтерпрайзников, где думают не об игре и игроках, а об удовлетворении шизы какого-нибудь заказчика (совсем как в вэбе, да).
Нетерпеливый подписчик возразит мне, что отличный вокабуляр это вовсе не проблема и я зря драматизирую; но язык определяет сознание, и в случае геймдева неизбежно приводит к натягиванию совы на известно что. Приведу пример поубедительнее. Если вы загуглите уровень зарплат в геймдеве, вы скорее всего найдёте опросы, где разработчики разбиты на Frontend, Backend и Fullstack. Привычное разделение в IT, но которое совершенно бессмысленно в геймдеве. Тут сразу несколько проблем.
Прежде всего, поставьте себя на место разработчика игрового движка синглплеерной игры. С одной стороны сервера нет, так что он работает, получается, над клиентом. Записываем во Frontend. С другой стороны, он не пишет саму игру, а для скриптеров его API выступает вполне себе Backend’ом. Наконец, можно заключить, что есть элементы того и другого, и выбрать вариант Fullstack. С равной вероятностью можно занести себя куда угодно. Подобные неоднозначности возникают и со многими другими геймдев-специализациями, если задуматься. В итоге такой опрос не показывает ничего, кроме погоды в унитазе составителя.
Но даже если представить, что мы договорились, кого в какую группу относить, это всё равно бессмысленные надкатегории, которые ничего не значат. В вэбе и мобильных приложениях Front и Back это естественным образом выделившиеся специализации, но в геймдеве такого разделения не произошло. Generalist-программисты у нас разделяются на геймплейщиков, графических, движкописателей/туловиков, иногда отдельно интерфейсщиков. Из геймплейщиков можно отдельно выделять скриптеров; из графических тех.артистов; движковых можно дальше дробить на физику, звук, АИ, сеть и т.п., но это не принципиально. Я не претендую на полноту классификации, но главное, что за категориями «графический программист» или «программист UI» стоят реальные вакансии и карьеры людей, которые двигались по этому пути, а Frontend-программист в геймдеве — это просто искусственная категория. И игровой код, и ИИ, и физика могут выполняться как на клиенте, так и на сервере, но различать их по этому признаку плохая идея.
Это ведь нетрудно задуматься, что некоторые привычные процессы или подходы просто неприменимы в геймдеве; что существует специфика работы. Но львиную долю набежавших энтепрайзников не посещает такая мысль даже на уровне терминологии. Теперь представьте, насколько часто в ваших внутренних процессах происходят спотыкания о всевозможные нюансы, если ваш HR тратит время разработчика на рассказы про вашу продуктовость; PM не видит перед собой геймплейного и графического программиста, а оперирует в голове выдуманными категориями; а программист интерфейсов требует ТЗ за два месяца и по шаблону мобильных приложений. И это только внешние очевидные проявления, а дьявол в мелочах и повседневном опыте.
Если слышите, что кто-то бездумно тащит в наше ремесло чужую терминологию, гоните его, насмехайтесь, зовите земляным червяком.
Прежде всего, поставьте себя на место разработчика игрового движка синглплеерной игры. С одной стороны сервера нет, так что он работает, получается, над клиентом. Записываем во Frontend. С другой стороны, он не пишет саму игру, а для скриптеров его API выступает вполне себе Backend’ом. Наконец, можно заключить, что есть элементы того и другого, и выбрать вариант Fullstack. С равной вероятностью можно занести себя куда угодно. Подобные неоднозначности возникают и со многими другими геймдев-специализациями, если задуматься. В итоге такой опрос не показывает ничего, кроме погоды в унитазе составителя.
Но даже если представить, что мы договорились, кого в какую группу относить, это всё равно бессмысленные надкатегории, которые ничего не значат. В вэбе и мобильных приложениях Front и Back это естественным образом выделившиеся специализации, но в геймдеве такого разделения не произошло. Generalist-программисты у нас разделяются на геймплейщиков, графических, движкописателей/туловиков, иногда отдельно интерфейсщиков. Из геймплейщиков можно отдельно выделять скриптеров; из графических тех.артистов; движковых можно дальше дробить на физику, звук, АИ, сеть и т.п., но это не принципиально. Я не претендую на полноту классификации, но главное, что за категориями «графический программист» или «программист UI» стоят реальные вакансии и карьеры людей, которые двигались по этому пути, а Frontend-программист в геймдеве — это просто искусственная категория. И игровой код, и ИИ, и физика могут выполняться как на клиенте, так и на сервере, но различать их по этому признаку плохая идея.
Это ведь нетрудно задуматься, что некоторые привычные процессы или подходы просто неприменимы в геймдеве; что существует специфика работы. Но львиную долю набежавших энтепрайзников не посещает такая мысль даже на уровне терминологии. Теперь представьте, насколько часто в ваших внутренних процессах происходят спотыкания о всевозможные нюансы, если ваш HR тратит время разработчика на рассказы про вашу продуктовость; PM не видит перед собой геймплейного и графического программиста, а оперирует в голове выдуманными категориями; а программист интерфейсов требует ТЗ за два месяца и по шаблону мобильных приложений. И это только внешние очевидные проявления, а дьявол в мелочах и повседневном опыте.
Если слышите, что кто-то бездумно тащит в наше ремесло чужую терминологию, гоните его, насмехайтесь, зовите земляным червяком.
А давайте поболтаем?
#лайт
Знаете чего не хватает, когда все месяцами работают удалённо? Технической болтовни из-за мониторов. Нормальное гиковское чувство рассказать о том, какой крутой пейпер прочитал вчера ночью, и как здорово раст уделывает кресты на синтетических тестах. Не уныло бросить ссылку в чат, а голосом раскидать императивщику за тайп-дедукшен здорового человека.
Вы уже догадываетесь к чему я клоню? Если вы из тех, кого хайп вокруг трендов раздражает, как блики солнечного света на дисплее, вам впору зашторивать окна. Речь пойдёт о clubhouse.
Мне прислали инвайт пару дней назад и я немного помониторил, если там жизнь в плане игростроя. Оказалось, что есть! Днём, разумеется, никого нет, но по вечерам там появляется «геймдев курилка», в которой всегда на удивление много людей. Причём не левого народу: если вы в индустрии не первый год или тусили пару раз на конференциях, то без труда отыщите кучу знакомых лиц на аватарках. Но, к сожалению, тон разговоров в курилке, как это часто бывает, захватили основатели студий, инвесторы и менеджеры. Эдакий дискурс старого DTF (ещё до смены владельцев). Вклиниваться в эти высокие разговоры о free to play не очень интересно; да и это совсем не то, чего просит душа простого технаря. А запрос на свою комнату, мне кажется, есть.
Я предлагаю собраться втроём или вчетвером, каждому заранее прочесть/изучить по одной интересной статье или пейперу, и рассказать каким-нибудь воскресным вечером друг другу, почему этот подход классный (или отстой). Это может быть новый proposal в С++, какая-нибудь фича из kotlin, статья про новый метод рейтрейсинга или же и вовсе эзотерический язык программирования. Что угодно. Расширим кругозор друг друга, а может чего интересного узнаем от аудитории.
Чем это отличается от подкаста?
Я давно хотел попробовать провести что-то вроде подкаста, но не был уверен насчёт формата. В своё время, когда заводил блог, я выбрал телеграмм, потому что рассудил, что это наиболее вменяемая платформа для технических блогов в наше время. Никому мои посты не интересны настолько, чтобы заходить на какой-то мой личный сайт или мониторить на Medium. А когда это всё происходит внутри платформы Telegram, куда люди и так заходят время от времени, аудитория не пропадает, и мои посты кто-то видит и обсуждает, даже если я пишу раз в месяц. Так же и с clubhouse. Там уже есть какой-то движ и люди туда заходят каждый день, так что почему бы и не поболтать?
Кроме того, традиционный подкаст предполагает более строгое отношение к контенту, хотя бы потому, что сохраняется в вечность. Он нацелен на то, что его будут слушать и после. Здесь же после прямого эфира ничего не остаётся. Это менее обязывающая форма подкаста, зато более динамичная и с живым общением со случайными слушателями. А после обсуждения и пост на тему проще писать.
Чем тогда это отличается от созвона в дискорде?
Тем, что в дискорде не бывает случайной аудитории. Никто не узнает, что на каком-то никому неизвестном сервере собрались три калеки и что-то обсуждают. Никто не подключится, не задаст вопросов и не вклинится в разговор. Здесь же мы можем создать комнату в clubhouse заранее и расписание увидит много людей. Кто-то поставит в календарь, а кто-то подключится в прямом эфире и «поднимет руку», чтобы ему тоже дали слово. Это может быть гораздо более интересный движняк, чем разговор на пустом сервере.
Так и чё?
Один я ничего не сделаю. Нужно ещё хотя бы пара человек, кому интересно читать/изучать что-то новое для себя, и кому интересно это обсудить в таком формате. Пишите в комментариях, кому идея кажется хорошей. Ну и о том, как вас достали разговоры о clubhouse из всех утюгов, тоже туда пишите. Не держите в себе :)
#лайт
Знаете чего не хватает, когда все месяцами работают удалённо? Технической болтовни из-за мониторов. Нормальное гиковское чувство рассказать о том, какой крутой пейпер прочитал вчера ночью, и как здорово раст уделывает кресты на синтетических тестах. Не уныло бросить ссылку в чат, а голосом раскидать императивщику за тайп-дедукшен здорового человека.
Вы уже догадываетесь к чему я клоню? Если вы из тех, кого хайп вокруг трендов раздражает, как блики солнечного света на дисплее, вам впору зашторивать окна. Речь пойдёт о clubhouse.
Мне прислали инвайт пару дней назад и я немного помониторил, если там жизнь в плане игростроя. Оказалось, что есть! Днём, разумеется, никого нет, но по вечерам там появляется «геймдев курилка», в которой всегда на удивление много людей. Причём не левого народу: если вы в индустрии не первый год или тусили пару раз на конференциях, то без труда отыщите кучу знакомых лиц на аватарках. Но, к сожалению, тон разговоров в курилке, как это часто бывает, захватили основатели студий, инвесторы и менеджеры. Эдакий дискурс старого DTF (ещё до смены владельцев). Вклиниваться в эти высокие разговоры о free to play не очень интересно; да и это совсем не то, чего просит душа простого технаря. А запрос на свою комнату, мне кажется, есть.
Я предлагаю собраться втроём или вчетвером, каждому заранее прочесть/изучить по одной интересной статье или пейперу, и рассказать каким-нибудь воскресным вечером друг другу, почему этот подход классный (или отстой). Это может быть новый proposal в С++, какая-нибудь фича из kotlin, статья про новый метод рейтрейсинга или же и вовсе эзотерический язык программирования. Что угодно. Расширим кругозор друг друга, а может чего интересного узнаем от аудитории.
Чем это отличается от подкаста?
Я давно хотел попробовать провести что-то вроде подкаста, но не был уверен насчёт формата. В своё время, когда заводил блог, я выбрал телеграмм, потому что рассудил, что это наиболее вменяемая платформа для технических блогов в наше время. Никому мои посты не интересны настолько, чтобы заходить на какой-то мой личный сайт или мониторить на Medium. А когда это всё происходит внутри платформы Telegram, куда люди и так заходят время от времени, аудитория не пропадает, и мои посты кто-то видит и обсуждает, даже если я пишу раз в месяц. Так же и с clubhouse. Там уже есть какой-то движ и люди туда заходят каждый день, так что почему бы и не поболтать?
Кроме того, традиционный подкаст предполагает более строгое отношение к контенту, хотя бы потому, что сохраняется в вечность. Он нацелен на то, что его будут слушать и после. Здесь же после прямого эфира ничего не остаётся. Это менее обязывающая форма подкаста, зато более динамичная и с живым общением со случайными слушателями. А после обсуждения и пост на тему проще писать.
Чем тогда это отличается от созвона в дискорде?
Тем, что в дискорде не бывает случайной аудитории. Никто не узнает, что на каком-то никому неизвестном сервере собрались три калеки и что-то обсуждают. Никто не подключится, не задаст вопросов и не вклинится в разговор. Здесь же мы можем создать комнату в clubhouse заранее и расписание увидит много людей. Кто-то поставит в календарь, а кто-то подключится в прямом эфире и «поднимет руку», чтобы ему тоже дали слово. Это может быть гораздо более интересный движняк, чем разговор на пустом сервере.
Так и чё?
Один я ничего не сделаю. Нужно ещё хотя бы пара человек, кому интересно читать/изучать что-то новое для себя, и кому интересно это обсудить в таком формате. Пишите в комментариях, кому идея кажется хорошей. Ну и о том, как вас достали разговоры о clubhouse из всех утюгов, тоже туда пишите. Не держите в себе :)
Патетичная речь о том, что я покидаю Encased
Encased — это очень важный для меня проект. В конце 2017 года, во время моего мексиканского саббатикала после работы в одной инди-команде, я вписался в новый геймдев-стартап. Фактически, мои старые университетские друзья позвали меня писать «русский Fallout». Это выглядело именно так, как звучит. Даже хуже: это сегодня словосочетание «русский Fallout» уже немного реабилитировано, а на тот момент это было чистым ругательством. Словом, очередные «корованы».
Но меня тогда прельщала возможность работать удалённо (да-да, были времена, когда это воспринималось привилегией, а не наказанием), попробовать себя лидом большого хардкорного проекта, а также у меня ещё жутко свербел гештальт по написанию ролевой игры. Олдфаги форума gamedev.ru наверняка помнят мой эпичный тред «маленькое и скромное РПГ», которое я ещё со школьной скамьи два года пилил на Visual Basic. Так вот ещё с тех пор мне хотелось снова залететь в эту же историю, но уже профессионально.
Удивительное дело, но наша команда не развалилась через два месяца. Мы просто фигачили лучшее, что могли, и мало-помалу у меня сперва исчезал скептицизм по отношению к нашему детищу, а затем я и вовсе по-настоящему прикипел к нему душой. Я фигачил над Encased 3 года и 4 месяца от первой строчки кода, от архитектуры, собственной сериализации и компонентной системы; к кикстартеру, на котором у меня случился нервный срыв; через собеседования новых сотрудников, прожатие F5 на странице отзывов и через девгаммы к раннему доступу; с переносом сроков, сменой издателя, портированием и ещё со множеством эпичных историй, которые без пива не рассказывают; в ту точку, где мы находимся сейчас — в пред-релизный пятый патч.
Сейчас я уверен в проекте, как никогда. Мы действительно делали «игру-мечты». Я не просто закрывал таски, но оказал большое влияние на то, как в итоге реализованы многие механики, боёвка, интерфейс, да и в целом управление в игре. Потому что мне было не пофиг. И наверное сейчас я впервые верю, что у нас действительно получается игра, которая посоревнуется с лучшими мировыми.
И в этом месте я покидаю Encased.
С самого начала проекта я жил в двух реальностях. В одной реальности я делал самый офигенный и важный проект в своей карьере, а в другой — жил не совсем своей жизнью. Меня захантили на проект немного хитростью: при обсуждении стартапа в качестве возможного движка рассматривался Unreal, а когда я уже морально настроился работать, оказалось, что бизнес готов вести дела только на Unity. Пришлось переступить через себя и вернуться к нелюбимому Unity. Но вообще мой настоящий гендер, как многие знают, — это крестовик с движкописательным уклоном. Плюс быть лидом, конечно, круто; но когда нет более крутых коллег, начинаешь замыкаться в собственных соплях, что не очень круто. Мне пока ещё рано замыкаться — хочется покачаться ещё хотя бы пару лет. Не говоря уж о планах эмиграции.
Encased — это очень важный для меня проект. В конце 2017 года, во время моего мексиканского саббатикала после работы в одной инди-команде, я вписался в новый геймдев-стартап. Фактически, мои старые университетские друзья позвали меня писать «русский Fallout». Это выглядело именно так, как звучит. Даже хуже: это сегодня словосочетание «русский Fallout» уже немного реабилитировано, а на тот момент это было чистым ругательством. Словом, очередные «корованы».
Но меня тогда прельщала возможность работать удалённо (да-да, были времена, когда это воспринималось привилегией, а не наказанием), попробовать себя лидом большого хардкорного проекта, а также у меня ещё жутко свербел гештальт по написанию ролевой игры. Олдфаги форума gamedev.ru наверняка помнят мой эпичный тред «маленькое и скромное РПГ», которое я ещё со школьной скамьи два года пилил на Visual Basic. Так вот ещё с тех пор мне хотелось снова залететь в эту же историю, но уже профессионально.
Удивительное дело, но наша команда не развалилась через два месяца. Мы просто фигачили лучшее, что могли, и мало-помалу у меня сперва исчезал скептицизм по отношению к нашему детищу, а затем я и вовсе по-настоящему прикипел к нему душой. Я фигачил над Encased 3 года и 4 месяца от первой строчки кода, от архитектуры, собственной сериализации и компонентной системы; к кикстартеру, на котором у меня случился нервный срыв; через собеседования новых сотрудников, прожатие F5 на странице отзывов и через девгаммы к раннему доступу; с переносом сроков, сменой издателя, портированием и ещё со множеством эпичных историй, которые без пива не рассказывают; в ту точку, где мы находимся сейчас — в пред-релизный пятый патч.
Сейчас я уверен в проекте, как никогда. Мы действительно делали «игру-мечты». Я не просто закрывал таски, но оказал большое влияние на то, как в итоге реализованы многие механики, боёвка, интерфейс, да и в целом управление в игре. Потому что мне было не пофиг. И наверное сейчас я впервые верю, что у нас действительно получается игра, которая посоревнуется с лучшими мировыми.
И в этом месте я покидаю Encased.
С самого начала проекта я жил в двух реальностях. В одной реальности я делал самый офигенный и важный проект в своей карьере, а в другой — жил не совсем своей жизнью. Меня захантили на проект немного хитростью: при обсуждении стартапа в качестве возможного движка рассматривался Unreal, а когда я уже морально настроился работать, оказалось, что бизнес готов вести дела только на Unity. Пришлось переступить через себя и вернуться к нелюбимому Unity. Но вообще мой настоящий гендер, как многие знают, — это крестовик с движкописательным уклоном. Плюс быть лидом, конечно, круто; но когда нет более крутых коллег, начинаешь замыкаться в собственных соплях, что не очень круто. Мне пока ещё рано замыкаться — хочется покачаться ещё хотя бы пару лет. Не говоря уж о планах эмиграции.
Короче говоря, ещё в самом начале проекта я пообещал себе, что не буду просиживать штаны на поддержке и полировке, если полный релиз будет постоянно откладываться (о том, что с крупными долгостроями так бывает, я понимал изначально). Сделаю всё основное — и уйду. И вот я наконец-то сдержал обещание (хотя и так затянул сильно дольше, чем рассчитывал).
Я оставляю проект со спокойной душой, потому что он в хорошем состоянии. И не смотрите, что сейчас недавние отзывы на стиме красные. Это только последние штук 30 и только из-за нашего долгого молчания. Но вопреки крикам хейтеров, которые утверждают, что мы ничего не делали чуть ли не с прошлого августа; игра всё это время активно разрабатывалась. Игра действительно уже проходится от начала до конца и технически полностью готова. Я закончил архитектуру и все важнейшие подсистемы, а также «вырастил» нового лида, который шарит в моём коде уже чуть ли не как я сам, и который с лёгкостью дотащит проект. Собственно, уже даже в этом патче все самые сочные изменения были внесены под его руководством; а я последние два месяца лишь поглядывал за процессом из полу-саббатикала. Ну а теперь я полностью ушёл.
Посмотрите свежий дневник разработки в шапке поста, чтобы узнать в каком состоянии игра на самом деле. Поиграйте в патч 5, который выходит сегодня на Steam, чтобы убедиться, что это не монтаж. Но только сильно не увлекайтесь — полноценное прохождение стоит отложить до релиза. Это в самом деле хорошая игра получается.
Я оставляю проект со спокойной душой, потому что он в хорошем состоянии. И не смотрите, что сейчас недавние отзывы на стиме красные. Это только последние штук 30 и только из-за нашего долгого молчания. Но вопреки крикам хейтеров, которые утверждают, что мы ничего не делали чуть ли не с прошлого августа; игра всё это время активно разрабатывалась. Игра действительно уже проходится от начала до конца и технически полностью готова. Я закончил архитектуру и все важнейшие подсистемы, а также «вырастил» нового лида, который шарит в моём коде уже чуть ли не как я сам, и который с лёгкостью дотащит проект. Собственно, уже даже в этом патче все самые сочные изменения были внесены под его руководством; а я последние два месяца лишь поглядывал за процессом из полу-саббатикала. Ну а теперь я полностью ушёл.
Посмотрите свежий дневник разработки в шапке поста, чтобы узнать в каком состоянии игра на самом деле. Поиграйте в патч 5, который выходит сегодня на Steam, чтобы убедиться, что это не монтаж. Но только сильно не увлекайтесь — полноценное прохождение стоит отложить до релиза. Это в самом деле хорошая игра получается.
Я вас всех немножко обманул
В прошлом сообщении я писал, что покидаю Encased, но это оказалась не совсем правда. Уходить-то я уходил, но ненадолго (всего на 2 месяца). Видимо, такие проекты просто так не отпускают. Планы, сроки и даты сложились таким образом, что у меня образовалось идеальное окно, чтобы вернуться и довести проект до релиза. В конце июня я вернулся и работал над Encased практически вплоть до настоящего момента.
Ужасно рад, что так сложились звёзды, что мне всё-таки удалось работать над проектом от первой строчки кода и до версии 1.0. Которую я сегодня и хочу вам представить от имени всей команды.
Пару часов назад Encased 1.0 вышел в релиз в Steam, Gog и Epic Games Store. Бегите скорее покупать, если ещё этого не сделали:
https://store.steampowered.com/app/921800/
Что же касается моих планов, сроков и дат, о которые я упоминал выше; то со вчерашнего дня я работаю в Paradox Development Studio. Чем я там занимаюсь, я, естественно, не расскажу. Но это ровно та компания, в которую я хотел устроиться. Может быть отдельным постом как-нибудь расскажу, почему я хотел именно в Швецию, и почему именно в Paradox. Но это будет в другой раз. А сегодня мы празднуем выход Encased! Ваше здоровье, господа! Skål!
В прошлом сообщении я писал, что покидаю Encased, но это оказалась не совсем правда. Уходить-то я уходил, но ненадолго (всего на 2 месяца). Видимо, такие проекты просто так не отпускают. Планы, сроки и даты сложились таким образом, что у меня образовалось идеальное окно, чтобы вернуться и довести проект до релиза. В конце июня я вернулся и работал над Encased практически вплоть до настоящего момента.
Ужасно рад, что так сложились звёзды, что мне всё-таки удалось работать над проектом от первой строчки кода и до версии 1.0. Которую я сегодня и хочу вам представить от имени всей команды.
Пару часов назад Encased 1.0 вышел в релиз в Steam, Gog и Epic Games Store. Бегите скорее покупать, если ещё этого не сделали:
https://store.steampowered.com/app/921800/
Что же касается моих планов, сроков и дат, о которые я упоминал выше; то со вчерашнего дня я работаю в Paradox Development Studio. Чем я там занимаюсь, я, естественно, не расскажу. Но это ровно та компания, в которую я хотел устроиться. Может быть отдельным постом как-нибудь расскажу, почему я хотел именно в Швецию, и почему именно в Paradox. Но это будет в другой раз. А сегодня мы празднуем выход Encased! Ваше здоровье, господа! Skål!
Кажется я поехал кукухой
Одна из моих попутных целей недавнего переезда и перемен в жизни — это восстановить work-life баланс, чтобы иметь возможность заниматься своими проектами. Если не считать периодические командировки в Питер, то с декабря 2016 года я всё время работал на удалёнке. Мои личные пет-проекты заглохли ровно с того же момента. Прежде всего потому что я на работе занимался достаточно увлекательными вещами. Но вторая, и, пожалуй, главная причина — это наглухо поехавший график. Когда нет чёткого физического разделения между рабочим временем и личным — всё время рабочее. Конечно, это не означает, что ты всё время работаешь: есть время и на отдых и на свои дела. В будние дни ты отвлекаешься и прокрастинируешь гораздо больше, чем в офисе; но потом нагоняешь ночью и на выходных. И это проблема. У тебя нет твоих личных двух часов перед сном, когда можно что-то поделать для себя и не чувствовать при этом, что это в ущерб работе. Потому что всегда есть, что нагонять в рабочем проекте. Всегда есть пару часиков, которые хорошо бы добить в этом спринте.
Вот почему при выборе нового места мне крайне важно было выйти именно в офис. Благо, в Швеции ⅔ населения уже вакцинированы и народ постепенно возвращается в офисы, так что с этим проблемы нет. Я также прикупил основную мебель в квартиру и разгрёб первичную бюрократию, так что у меня и в самом деле появилось время на свои проекты.
Разумеется, первым делом я стряхнул пыль со своего движка Judy. Ну как движка. Там только редактор скриптов с плеером и дебагером, самопальная рефлексия для С++, да зачатки рендера. Но концепт эстетически приятный. Центральную роль в нём играет Lua, которая выступает одновременно и скриптовым языком, и форматом для конфигов, сцен, файлов сохранений и сетевых сообщений. Не самое быстрое решение, но оно быстрым быть и не собиралось: упор у меня был на удобные тулзы, а не скорость. Игры предполагалось писать всё равно относительно маленькие.
Вот только за последние 5 лет немножко изменились мои требования к движку мечты. Во-первых, IDE повсеместно теперь поддерживают Language Server Protocol, и писать скрипты без возможностей хотя бы EmmyLua нынче уже не комильфо, как говорится. Во-вторых, на старте Encased я сильно вложился в проектирование системы сериализации. Это многократно нам окупилось, а также по мере развития сильно повлияло на мои взгляды по теме. Теперь я хочу что-то подобное встроенное в скриптовый язык. Причём из коробки. Ясное дело, я не мог не пересмотреть немного концепцию Judy.
Меня всё ещё кидает из стороны в сторону, потому что я хочу слишком много всего и сразу, да и отказываться от Lua, которая уже подключена и работает, тоже жалко; но последние 2 дня я размышляю над своим языком. Пока только формулирую основные Design Goals, но одновременно прикидываю к носу, как конкретные фичи будут технически исполнены.
Писать свой скриптовый язык это особый уровень безумия. Даже по меркам движкописателей. Я прекрасно понимаю, что это адски объёмная задача и вряд ли вообще что-то путное получится, но похоже, что кукуха моя уже поехала и остановить будет сложно. Все нормальные названия для языка уже заняты, поэтому пусть пока будет рабочее — Ku-ku language. О том, чем я занимаюсь на работе всё равно больше рассказывать нельзя, так что ближайшее время блог будет в основном про Ku-ku.
Одна из моих попутных целей недавнего переезда и перемен в жизни — это восстановить work-life баланс, чтобы иметь возможность заниматься своими проектами. Если не считать периодические командировки в Питер, то с декабря 2016 года я всё время работал на удалёнке. Мои личные пет-проекты заглохли ровно с того же момента. Прежде всего потому что я на работе занимался достаточно увлекательными вещами. Но вторая, и, пожалуй, главная причина — это наглухо поехавший график. Когда нет чёткого физического разделения между рабочим временем и личным — всё время рабочее. Конечно, это не означает, что ты всё время работаешь: есть время и на отдых и на свои дела. В будние дни ты отвлекаешься и прокрастинируешь гораздо больше, чем в офисе; но потом нагоняешь ночью и на выходных. И это проблема. У тебя нет твоих личных двух часов перед сном, когда можно что-то поделать для себя и не чувствовать при этом, что это в ущерб работе. Потому что всегда есть, что нагонять в рабочем проекте. Всегда есть пару часиков, которые хорошо бы добить в этом спринте.
Вот почему при выборе нового места мне крайне важно было выйти именно в офис. Благо, в Швеции ⅔ населения уже вакцинированы и народ постепенно возвращается в офисы, так что с этим проблемы нет. Я также прикупил основную мебель в квартиру и разгрёб первичную бюрократию, так что у меня и в самом деле появилось время на свои проекты.
Разумеется, первым делом я стряхнул пыль со своего движка Judy. Ну как движка. Там только редактор скриптов с плеером и дебагером, самопальная рефлексия для С++, да зачатки рендера. Но концепт эстетически приятный. Центральную роль в нём играет Lua, которая выступает одновременно и скриптовым языком, и форматом для конфигов, сцен, файлов сохранений и сетевых сообщений. Не самое быстрое решение, но оно быстрым быть и не собиралось: упор у меня был на удобные тулзы, а не скорость. Игры предполагалось писать всё равно относительно маленькие.
Вот только за последние 5 лет немножко изменились мои требования к движку мечты. Во-первых, IDE повсеместно теперь поддерживают Language Server Protocol, и писать скрипты без возможностей хотя бы EmmyLua нынче уже не комильфо, как говорится. Во-вторых, на старте Encased я сильно вложился в проектирование системы сериализации. Это многократно нам окупилось, а также по мере развития сильно повлияло на мои взгляды по теме. Теперь я хочу что-то подобное встроенное в скриптовый язык. Причём из коробки. Ясное дело, я не мог не пересмотреть немного концепцию Judy.
Меня всё ещё кидает из стороны в сторону, потому что я хочу слишком много всего и сразу, да и отказываться от Lua, которая уже подключена и работает, тоже жалко; но последние 2 дня я размышляю над своим языком. Пока только формулирую основные Design Goals, но одновременно прикидываю к носу, как конкретные фичи будут технически исполнены.
Писать свой скриптовый язык это особый уровень безумия. Даже по меркам движкописателей. Я прекрасно понимаю, что это адски объёмная задача и вряд ли вообще что-то путное получится, но похоже, что кукуха моя уже поехала и остановить будет сложно. Все нормальные названия для языка уже заняты, поэтому пусть пока будет рабочее — Ku-ku language. О том, чем я занимаюсь на работе всё равно больше рассказывать нельзя, так что ближайшее время блог будет в основном про Ku-ku.
Ku-ku design goals
Вот я и накатал Design Goals для моего скриптового языка мечты Ku-ku.
Писал изначально на английском. Думал сначала перевести на русский для этого канала, но быстро понял, что любой, кому будет хоть капельку это интересно, должен по идее без труда справиться с техническим английским текстом. Так что пусть будет пока так.
Так или иначе, одно лишь пространное описание того, чего же я хочу от языка, даже без конкретных деталей, получилось весьма внушительным. Как я собираюсь это всё разрабатывать по вечерам и не лопнуть — ума не приложу.
Но это будет чуть позже. А пока я очень жду ваших комментариев по тому, что есть. Заряжайте холивар по каждому пункту. Дайте мне жару, как следует!
Вот я и накатал Design Goals для моего скриптового языка мечты Ku-ku.
Писал изначально на английском. Думал сначала перевести на русский для этого канала, но быстро понял, что любой, кому будет хоть капельку это интересно, должен по идее без труда справиться с техническим английским текстом. Так что пусть будет пока так.
Так или иначе, одно лишь пространное описание того, чего же я хочу от языка, даже без конкретных деталей, получилось весьма внушительным. Как я собираюсь это всё разрабатывать по вечерам и не лопнуть — ума не приложу.
Но это будет чуть позже. А пока я очень жду ваших комментариев по тому, что есть. Заряжайте холивар по каждому пункту. Дайте мне жару, как следует!
Google Docs
Ku-ku
KuKu* language design goals (* - working title) Abstract Easy-to-learn dev-supportive language both for gameplay scripting with close data integration and for store game data I. Core 1.1. Minimalistic Kuku should be relatively small. It must be possible…
Вы точно понимаете корутины?
#код
Много-много лет назад, когда я первый раз пришёл на настоящую работу программировать за настоящие деньги, меня научили корутинам. Ну, не то, чтобы прям научили. Просто там был проект с корутинами, и мне волей-неволей пришлось вникнуть, что это за yield такой непонятный. Новый мир, зазиявший передо мной, в корне перевернул моё тогдашнее представление о том, как можно писать геймплейный код. Я и до сих пор это воспринимаю, как одну из важнейших ментальных ступенек для программиста.
Речь сейчас идёт о тех диких временах, когда воевали с луками и стрелами против боевых машин самописными движками на плюсах. Я тогда успел немного походить из конторы в контору, но везде обнаруживал несчастных грустных людей, не вкусивших плодов корутин. Многие и слов таких не знали. И я нёс знания в простой народ. И жизнь людская преображалась на этих проектах.
Но эти времена давно прошли. Теперь каждый знает, что такое StartCoroutine() в Unity. Все к ним привыкли, и никого ими не удивишь; но я заметил, что самое крутое назначение корутин все ещё ускользает от многих в наше время.
Новички считают, что это просто удобный способ делать задержки. Каждый второй, делающий тестовое про самолётик в DarkCrystalGames, делал таймаут выстрелам через запуск корутины и WaitForSeconds(). Мой внутренний эстет при чтении такого кода всегда бьёт себя по лицу, но это вкусовщина. А проблема в том, что эти люди, как правило, только таким использованием и ограничиваются. Ничего более интересного, чем задержки, на корутинах и не пишут. Какое неуважение.
Но этого поста не было бы, если бы проблемы с пониманием возникали только у новичков. Опытные сеньористые ветераны порой думают о корутинах, как о неком легковесном способе организации мультипоточки.
Нет, нет и ещё раз нет! Мультипоточка тут вообще ни при чём!
Недопонимание происходит из-за того, что к одной и той же вещи можно прийти с двух сторон. Можно с одной стороны постепенно облегчать мультизадачность и повышать безопасность и прийти от потоков к так называемым Green Threads. А можно вообще идти с другой стороны, пытаясь в однопоточном приложении улучшить читабельность колбеков и стейт-машин, и прийти внезапно к тому же самому. В этом случае результат скорее назовут корутинами. Разница между получившимися вещами с точки зрения функционала будет весьма условна, но отличается мотивация и назначение. Если вы познакомились с корутинами не с той стороны, то эта статья для вас.
#код
Много-много лет назад, когда я первый раз пришёл на настоящую работу программировать за настоящие деньги, меня научили корутинам. Ну, не то, чтобы прям научили. Просто там был проект с корутинами, и мне волей-неволей пришлось вникнуть, что это за yield такой непонятный. Новый мир, зазиявший передо мной, в корне перевернул моё тогдашнее представление о том, как можно писать геймплейный код. Я и до сих пор это воспринимаю, как одну из важнейших ментальных ступенек для программиста.
Речь сейчас идёт о тех диких временах, когда воевали с луками и стрелами против боевых машин самописными движками на плюсах. Я тогда успел немного походить из конторы в контору, но везде обнаруживал несчастных грустных людей, не вкусивших плодов корутин. Многие и слов таких не знали. И я нёс знания в простой народ. И жизнь людская преображалась на этих проектах.
Но эти времена давно прошли. Теперь каждый знает, что такое StartCoroutine() в Unity. Все к ним привыкли, и никого ими не удивишь; но я заметил, что самое крутое назначение корутин все ещё ускользает от многих в наше время.
Новички считают, что это просто удобный способ делать задержки. Каждый второй, делающий тестовое про самолётик в DarkCrystalGames, делал таймаут выстрелам через запуск корутины и WaitForSeconds(). Мой внутренний эстет при чтении такого кода всегда бьёт себя по лицу, но это вкусовщина. А проблема в том, что эти люди, как правило, только таким использованием и ограничиваются. Ничего более интересного, чем задержки, на корутинах и не пишут. Какое неуважение.
Но этого поста не было бы, если бы проблемы с пониманием возникали только у новичков. Опытные сеньористые ветераны порой думают о корутинах, как о неком легковесном способе организации мультипоточки.
Нет, нет и ещё раз нет! Мультипоточка тут вообще ни при чём!
Недопонимание происходит из-за того, что к одной и той же вещи можно прийти с двух сторон. Можно с одной стороны постепенно облегчать мультизадачность и повышать безопасность и прийти от потоков к так называемым Green Threads. А можно вообще идти с другой стороны, пытаясь в однопоточном приложении улучшить читабельность колбеков и стейт-машин, и прийти внезапно к тому же самому. В этом случае результат скорее назовут корутинами. Разница между получившимися вещами с точки зрения функционала будет весьма условна, но отличается мотивация и назначение. Если вы познакомились с корутинами не с той стороны, то эта статья для вас.
Идём со стороны многопоточки
Для начала позанудствуем (если не осилите, пропускайте этот раздел) и вспомним, что многозадачность у нас бывает вытесняющей и кооперативной. Вытесняющая — это обычные потоки. У нас есть несколько физических процессоров и несколько потоков выполнения. В каждый момент времени один поток выполняется на одном процессоре, каждый поток имеет свой собственный стек, а операционная система часто-часто переключает потоки на конкретном процессоре (подменяя стек и регистры процессора). В общем, всё как мы любим: никаких сюрпризов.
Сразу обозначим, что всякие фреймворки на основе Job’ов или Promise’ов, если они приводят к созданию новых настоящих тредов, являются обёрткой над этим же типом многозадачности.
Но есть и другой тип — кооперативный. Это когда переключение контекстов происходит не по воле ОС, а когда сами нити выполнения решили. Те самые yield. На каком-то абстрактном академическом уровне уже в принципе можно заявить, что это и есть корутины, но давайте посмотрим на кооперативную многозадачность чуть повнимательнее.
Представители этого класса могут называться по-разному: фиберы, зелёные треды, горутины. Главное, что каждый такой фибер хранит свой собственный стек, подобно потокам. И если физические процессоры постоянно переключаются между потоками, то потоки уже в свою очередь переключаются внутри себя между фиберами. То есть эти два типа многозадачности могут стакаться и использоваться вместе.
Почему же фиберы/зелёные треды называют легковесными?
- переключения фиберов происходят реже и только тогда, когда они сами решили (например, раз в несколько секунд, а не много-много раз в секунду);
- так как точки переключения заранее известны, то нужно сохранять меньшее количество регистров процессора;
- те фиберы, которые было решено выполнять на одном потоке, не требуют синхронизации между собой.
Это неплохой бонус в сравнении с потоками, но на деле это не так уж и легковесно. При стандартном размере стека в 1 MB, запуск 1024 фиберов приведёт к выделению, как минимум, 1 GB памяти. Это нормально для Green Thread, но совершенно недопустимо для корутин. Для концепции корутин не должно быть проблемой и десять тысяч.
Очевидное решение — это делать динамические стеки, расширяющиеся по требованию. Так делают, например, горутины. И это уже чертовски близко к stackful-корутинам (например тем, что есть в boost). Но разница в мотивации. Горутины всё равно почти всегда используются в контексте многопоточности. Как правило, запускается несколько полноценных потоков по числу ядер процессора, а уже поверх них сотни лёгковесных горутин. Все эти свистопляски были для того, чтобы облегчить накладные ресурсы в изначально многопоточном приложении. Замасштабировав их с небольшого числа рутин до сотен и тысяч без изменения кода.
Фиберы и зелёные треды тоже применяются обычно в тех контекстах, когда речь идёт, так или иначе, о многопоточных приложениях. Будь то портирование с другой платформы или нечто похожее. А корутины это вообще не про это.
Для начала позанудствуем (если не осилите, пропускайте этот раздел) и вспомним, что многозадачность у нас бывает вытесняющей и кооперативной. Вытесняющая — это обычные потоки. У нас есть несколько физических процессоров и несколько потоков выполнения. В каждый момент времени один поток выполняется на одном процессоре, каждый поток имеет свой собственный стек, а операционная система часто-часто переключает потоки на конкретном процессоре (подменяя стек и регистры процессора). В общем, всё как мы любим: никаких сюрпризов.
Сразу обозначим, что всякие фреймворки на основе Job’ов или Promise’ов, если они приводят к созданию новых настоящих тредов, являются обёрткой над этим же типом многозадачности.
Но есть и другой тип — кооперативный. Это когда переключение контекстов происходит не по воле ОС, а когда сами нити выполнения решили. Те самые yield. На каком-то абстрактном академическом уровне уже в принципе можно заявить, что это и есть корутины, но давайте посмотрим на кооперативную многозадачность чуть повнимательнее.
Представители этого класса могут называться по-разному: фиберы, зелёные треды, горутины. Главное, что каждый такой фибер хранит свой собственный стек, подобно потокам. И если физические процессоры постоянно переключаются между потоками, то потоки уже в свою очередь переключаются внутри себя между фиберами. То есть эти два типа многозадачности могут стакаться и использоваться вместе.
Почему же фиберы/зелёные треды называют легковесными?
- переключения фиберов происходят реже и только тогда, когда они сами решили (например, раз в несколько секунд, а не много-много раз в секунду);
- так как точки переключения заранее известны, то нужно сохранять меньшее количество регистров процессора;
- те фиберы, которые было решено выполнять на одном потоке, не требуют синхронизации между собой.
Это неплохой бонус в сравнении с потоками, но на деле это не так уж и легковесно. При стандартном размере стека в 1 MB, запуск 1024 фиберов приведёт к выделению, как минимум, 1 GB памяти. Это нормально для Green Thread, но совершенно недопустимо для корутин. Для концепции корутин не должно быть проблемой и десять тысяч.
Очевидное решение — это делать динамические стеки, расширяющиеся по требованию. Так делают, например, горутины. И это уже чертовски близко к stackful-корутинам (например тем, что есть в boost). Но разница в мотивации. Горутины всё равно почти всегда используются в контексте многопоточности. Как правило, запускается несколько полноценных потоков по числу ядер процессора, а уже поверх них сотни лёгковесных горутин. Все эти свистопляски были для того, чтобы облегчить накладные ресурсы в изначально многопоточном приложении. Замасштабировав их с небольшого числа рутин до сотен и тысяч без изменения кода.
Фиберы и зелёные треды тоже применяются обычно в тех контекстах, когда речь идёт, так или иначе, о многопоточных приложениях. Будь то портирование с другой платформы или нечто похожее. А корутины это вообще не про это.
Идём со стороны стейт-машин
Прошлый раздел был нужен лишь для полноты картины и для того, чтобы сказать, что всё вообще не так. В этом разделе мы придём туда же, но другим путём.
Итак, забудьте пока про ужасный и суетливый мир мультипотока. Переместимся на время в спокойное безопасное место. Вы в своём однопоточном приложении. Больше нет никаких состояний гонок, всё выполняется последовательно и предсказуемо.
Вы пишите игру про ваше тотемное животное. Пусть это будет сурикат. Целая колония сурикатов. Все они бегают по Калахари в поисках еды и боевых приключений. Если каждая особь будет обладать маломальскими самостоятельными мозгами (а сурикаты они такие), то классическая имплементация — это, конечно же, стейт-машины. Ну или конечные автоматы, если вам так угодно. То есть вы пишите много-много классов (по одному на каждое возможное состояние машины), и в каждом классе реализуете функцию update(), которая выполняется каждый игровой тик, и возможно приводит к переходу в другие состояния.
Красота? Благодать? В принципе, да. Но не очень. Этот подход, конечно, понравится вашему тимлиду, потому что так ещё деды писали, но всё-таки писать столько служебного кода довольно утомительно. Ведь ежедневная рутина сурикатов так соблазнительно хорошо описывается в терминах одной обычной функции с ветвлениями:
Прошлый раздел был нужен лишь для полноты картины и для того, чтобы сказать, что всё вообще не так. В этом разделе мы придём туда же, но другим путём.
Итак, забудьте пока про ужасный и суетливый мир мультипотока. Переместимся на время в спокойное безопасное место. Вы в своём однопоточном приложении. Больше нет никаких состояний гонок, всё выполняется последовательно и предсказуемо.
Вы пишите игру про ваше тотемное животное. Пусть это будет сурикат. Целая колония сурикатов. Все они бегают по Калахари в поисках еды и боевых приключений. Если каждая особь будет обладать маломальскими самостоятельными мозгами (а сурикаты они такие), то классическая имплементация — это, конечно же, стейт-машины. Ну или конечные автоматы, если вам так угодно. То есть вы пишите много-много классов (по одному на каждое возможное состояние машины), и в каждом классе реализуете функцию update(), которая выполняется каждый игровой тик, и возможно приводит к переходу в другие состояния.
Красота? Благодать? В принципе, да. Но не очень. Этот подход, конечно, понравится вашему тимлиду, потому что так ещё деды писали, но всё-таки писать столько служебного кода довольно утомительно. Ведь ежедневная рутина сурикатов так соблазнительно хорошо описывается в терминах одной обычной функции с ветвлениями:
проснуться();Согласитесь, намного читабельнее и удобнее, чем писать штук 10 классов под это дело. Единственная проблемка в том, что каждая операция здесь потенциально может выполняться больше одного игрового тика (то есть длиться дольше одного кадра; например, из-за анимации), а потому простой функцией здесь не обойтись. Как вы уже догадались, нам просто нужна возможность писать функции с несколькими точками входа. Или, проще говоря, возможность нашпиговать этот код yield()-ами через строчку:
расчистить_вход_в_норку_от_песка();
пока ещё_не_перегрелись_на_солнце {
выбрать_точку_куда_идти();
пока не_дошли {
сделать_шаг_по_маршруту()
если встретили_пищу, то
есть();
если встретили_врага, то
сражаться();
}
}
идти_в_тенёк().
проснуться();Такие фаршированные функции называют генераторами. Они могут быть знакомы многим по C#. И по большому счёту это и есть машина состояний, только записанная более удобным синтаксисом; поскольку внутренне это всё разбивается всё равно на несколько маленьких функций от одного yield до другого, а общие переменные выносятся в небольшую структуру, которая передаётся дальше (на манер того, как это происходит с замыканиями). Каждая такая сгенерированная структурка и соответствующая ей функция по сути и образуют наши олдскульные классы состояний конечного автомата.
yield расчистить_вход_в_норку_от_песка();
пока ещё_не_перегрелись_на_солнце {
выбрать_точку_куда_идти();
пока не_дошли {
yield сделать_шаг_по_маршруту()
если встретили_пищу, то
yield есть();
если встретили_врага, то
yield сражаться();
}
}
yield идти_в_тенёк().
Но, по правде говоря, прямо функции и классы в терминах конкретного языка виртуальная машина вам генерить не обязана, потому что они наружу не видны, но по смыслу там ровно это происходит. Но вот эта простая смена формы записи и есть та самая ментальная ступенька, о которой я говорил в начале, так как она сильнейшим образом развязывает руки и мозги геймплейному программисту в плане стейт-машин.
Но погодите. Генераторы это ещё не корутины. Код с yield-ами, что я показал выше, в такой форме использовать вообще-то не получится. Дело в том, что внутри указанных функций есть вложенные yield-ы. Поэтому в вызывающем коде придётся крутить циклы. Либо же возвращать вложенный генератор и написать обёртку, которая будет складировать генераторы в стек и разруливать их вызовы соответствующим образом. Это пишется очень легко и так сделаны, например, всем известные Unity-корутины. И вот это уже тянет на некое подобие нормальных асимметричных корутин.
«Асимметричных», потому что стек выстраивает отношения между генераторами по принципу «вызывающий-вызываемый». Но в принципе можно переключаться между генераторами не по логике стека, а как взбредёт в голову. И тогда такие корутины будут называться «симметричными». В том смысле, что отношения между ними симметричные, то есть все на равных правах. Так можно, например, завести параллельные функции, которые передают друг другу управление по принципу пинг-понга. Но это мы опять же уходим в сторону многопоточных историй, а в нашем спокойном и безопасном однопоточном месте такие выкрутасы не особо нужны. Вернёмся к нашим тотемным животным.
У нас всё ещё не полноценные корутины, а симулякр. Если мы захотим сделать yield где-то далеко на глубине стека вызовов в C#, нам хоть и не придётся теперь крутить цикл в каждом месте, но протаскивать yield всё же придётся через все уровни. Если бы корутины были реализованы на уровне языка, как, например, в Kotlin, то мы могли бы ещё немного улучшить жизнь нашим сурикатам, прокидывая yield с любой глубины. Это намного удобнее. Но не идеально, потому что в Kotlin все промежуточные функции придётся пометить как suspend. Тогда компилятор будет обращаться с этими функциями, как с генераторами (для их промежуточного состояния будет создаваться объект), но эти функции тогда будет нельзя вызывать из non-suspend функций. То есть помечая функцию как suspend, мы рискуем, что часть старого кода отвалится.
— А бывают ли вообще идеальные корутины? — спросите вы. — Чтобы вызываться без боли из любого места и не думать о том, какая там была функция.
Да, бывают. Мы наконец-то пришли к stack-full корутинам. Это когда вместо вот этой всей пурги про разрезание функций на много маленьких и создание структурок-состояний, мы просто берём и сохраняем целиком весь стек приложения. Сохраняем со всеми его обычными и волшебными функциями вообще без ограничений.
Я встречал stack-full корутины как минимум 2 раза. Этол boost-овые в С++. Но сами понимаете: С++ сам по себе такой, что «без боли» к нему слабоприменимое понятие. И, конечно же, в Lua. Это мои первые и самые любимые корутины, потому что корутины там, как говорится, first-class-citizen: они yield-ятся в любой момент и с любого уровня, не требуют менять существующий код и никак не мешают вызывать эти же функции вне корутин — одно удовольствие. Идеально для написания толп сурикат.
Но вы заметьте — и это главная мысль поста — куда мы пришли. Мы начали с разговора об однопоточных стейт-машинах, но закончили чем-то, ужасно напоминающим фиберы или горутины. И это то место, где разница между ними, а вместе с тем и понимание, стирается. В какой-то литературе между фиберами и корутинами проводят чёткую черту по наличию планировщика, в какой-то — это синонимы. Но так или иначе у очень многих в головах сидит ассоциация, что корутины это обязательно что-то про многопоточный код и состояние гонки.
Надеюсь, я развеял этот миф и эта статья открыла для кого-то корутины по-новому. Ну или по крайней мере стал сам лучше понимать эту область.
Но погодите. Генераторы это ещё не корутины. Код с yield-ами, что я показал выше, в такой форме использовать вообще-то не получится. Дело в том, что внутри указанных функций есть вложенные yield-ы. Поэтому в вызывающем коде придётся крутить циклы. Либо же возвращать вложенный генератор и написать обёртку, которая будет складировать генераторы в стек и разруливать их вызовы соответствующим образом. Это пишется очень легко и так сделаны, например, всем известные Unity-корутины. И вот это уже тянет на некое подобие нормальных асимметричных корутин.
«Асимметричных», потому что стек выстраивает отношения между генераторами по принципу «вызывающий-вызываемый». Но в принципе можно переключаться между генераторами не по логике стека, а как взбредёт в голову. И тогда такие корутины будут называться «симметричными». В том смысле, что отношения между ними симметричные, то есть все на равных правах. Так можно, например, завести параллельные функции, которые передают друг другу управление по принципу пинг-понга. Но это мы опять же уходим в сторону многопоточных историй, а в нашем спокойном и безопасном однопоточном месте такие выкрутасы не особо нужны. Вернёмся к нашим тотемным животным.
У нас всё ещё не полноценные корутины, а симулякр. Если мы захотим сделать yield где-то далеко на глубине стека вызовов в C#, нам хоть и не придётся теперь крутить цикл в каждом месте, но протаскивать yield всё же придётся через все уровни. Если бы корутины были реализованы на уровне языка, как, например, в Kotlin, то мы могли бы ещё немного улучшить жизнь нашим сурикатам, прокидывая yield с любой глубины. Это намного удобнее. Но не идеально, потому что в Kotlin все промежуточные функции придётся пометить как suspend. Тогда компилятор будет обращаться с этими функциями, как с генераторами (для их промежуточного состояния будет создаваться объект), но эти функции тогда будет нельзя вызывать из non-suspend функций. То есть помечая функцию как suspend, мы рискуем, что часть старого кода отвалится.
— А бывают ли вообще идеальные корутины? — спросите вы. — Чтобы вызываться без боли из любого места и не думать о том, какая там была функция.
Да, бывают. Мы наконец-то пришли к stack-full корутинам. Это когда вместо вот этой всей пурги про разрезание функций на много маленьких и создание структурок-состояний, мы просто берём и сохраняем целиком весь стек приложения. Сохраняем со всеми его обычными и волшебными функциями вообще без ограничений.
Я встречал stack-full корутины как минимум 2 раза. Этол boost-овые в С++. Но сами понимаете: С++ сам по себе такой, что «без боли» к нему слабоприменимое понятие. И, конечно же, в Lua. Это мои первые и самые любимые корутины, потому что корутины там, как говорится, first-class-citizen: они yield-ятся в любой момент и с любого уровня, не требуют менять существующий код и никак не мешают вызывать эти же функции вне корутин — одно удовольствие. Идеально для написания толп сурикат.
Но вы заметьте — и это главная мысль поста — куда мы пришли. Мы начали с разговора об однопоточных стейт-машинах, но закончили чем-то, ужасно напоминающим фиберы или горутины. И это то место, где разница между ними, а вместе с тем и понимание, стирается. В какой-то литературе между фиберами и корутинами проводят чёткую черту по наличию планировщика, в какой-то — это синонимы. Но так или иначе у очень многих в головах сидит ассоциация, что корутины это обязательно что-то про многопоточный код и состояние гонки.
Надеюсь, я развеял этот миф и эта статья открыла для кого-то корутины по-новому. Ну или по крайней мере стал сам лучше понимать эту область.