DEV/MATH[ V0.0.1-ALPHA ]//articles/lootboxes

Математика невезения: как игры подкручивают случайность

Всем привет! Меня зовут Гриша Дядиченко, и я технический директор и основатель White Label Games. Уже больше десяти лет работаю с компьютерной графикой, AR/VR и компьютерным зрением — в основном заказная разработка, иногда собственные прототипы по выходным.

На этой неделе в телеграм-канале я постил короткие разборы про случайность в играх: про криты Dota, которые стартуют с 8,5% при обещанных 25%, про seed Minecraft, вычисленный по одной картинке, и про нож из кейса CS, где «монетка 50/50» стоит 660 $. Пора собрать всё в одну историю — с формулами, выводом констант и интерактивами, которые можно покрутить прямо со страницы.

Сталкивались ли вы с ситуацией, когда игрок после тридцати убийств без дропа на пятипроцентном шансе пишет в саппорт «у вас сломан рандом»? Генератор при этом работает идеально. Сломаны ожидания.

Сквозная идея простая. Случайность в играх собрана из трёх слоёв. Внизу — Pseudo-random number generator, генератор псевдослучайных чисел: детерминированная рекуррента, у которой случайного только начальное состояние — seed., детерминированная формула, которая лишь притворяется костями. В середине — искажатели вроде PRD и pity, которые меняют распределение под человеческое восприятие, потому что честная равномерность ощущается сломанной. Наверху — экономика: геометрическое распределение, кривая 1 − (1 − p)^N и регуляторы, которые решают, где тут граница с казино.

Если интересна тема — добро пожаловать.

[больше интересного в телеграм]

1. Внутри генератора

Одна картинка против 2⁴⁸ вариантов

В Minecraft есть файл pack.png — маленькая картинка 512×512, которая годами лежала в ресурсах игры как заставка. Скриншот какого-то мира из старой версии. В 2020 году группа энтузиастов Minecraft@Home задалась вопросом: а можно ли по одной этой картинке вычислить seed мира, в котором её сняли?

Можно. Сначала по облакам и текстурам восстановили точку съёмки и ракурс (выяснилось заодно, что снимали обычным Print Screen с кропом до 512×512). Потом написали фильтр: какие сиды вообще могут породить вот такое сочетание песка и земли в кадре. Прогнали перебор по всем 2⁴⁸ вариантам на распределённой сети BOINC — около 3700 добровольцев, примерно три дня машинного времени. Фильтр сузил пространство до ~700 тысяч кандидатов, дальше добили точной проверкой. Ответ: seed 3257840388504953787.

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

Почему перебор был именно по 2⁴⁸, мы сейчас увидим — это число прямо следует из формулы генератора.

Строка арифметики

Minecraft написан на Java, а java.util.Random устроен так: внутри лежит 48-битное состояние, и каждый запрос нового числа делает один шаг рекурренты

state = (a · state + c) mod m

с конкретными константами a = 0x5DEECE66D, c = 0xB, m = 2⁴⁸ — они прописаны в официальной документации класса. Наружу отдаются старшие биты состояния. Вот и весь источник «случайности»: умножить, прибавить, взять остаток. Такая схема называется линейным конгруэнтным генератором (LCG), и в учебниках она идёт первой уже полвека.

На C# тот же генератор пишется в пять строк:

public class Lcg
{
    private ulong _state; // seed

    public Lcg(ulong seed) => _state = seed;

    public double Next()
    {
        _state = (0x5DEECE66DUL * _state + 0xBUL) & ((1UL << 48) - 1);
        return (_state >> 24) / (double)(1UL << 24); // [0, 1)
    }
}

Из числа в [0, 1) дальше делается всё: value < 0.05 — выпал ли дроп на 5%, min + value * (max - min) — разброс урона, индекс в массиве — выбор реплики NPC.

Ключевое свойство здесь — детерминизм. Начальное значение _state и есть seed. Один seed — одна и та же последовательность, всегда, на любой машине. Проверьте сами:

seed
нажмите «Сгенерировать»
формула state = (0x5DEECE66D·state + 0xB) mod 2⁴⁸seed по умолчанию — pack.png

Отсюда сиды Minecraft, общие daily-уровни в Spelunky и RNG-манипуляции в спидранах, где игроки покадрово «заказывают» себе нужный дроп. И отсюда же история pack.png: состояние генератора 48-битное, значит миров существует ровно 2⁴⁸, и это число переборное. Сообщество, кстати, развлекается этим до сих пор — есть инструмент SeedcrackerX, который вычисляет seed чужого сервера просто по координатам найденных структур.

Чтож, формула из одной строки порождает мир. Но почему этой строке вообще можно доверять? Любые ли a, c, m годятся?

Спектральный тест и 15 плоскостей RANDU

Конечно же нет. И хрестоматийный пример провала — генератор RANDU, который IBM поставляла в 60-х и которым успели посчитать заметное количество научных работ. У RANDU a = 65539, c = 0, m = 2³¹. Выглядит безобидно, пока не взять три подряд идущих числа и не нарисовать их как точку в кубе. Все такие точки ложатся ровно на 15 плоскостей. Причина зашита в арифметике: 65539 = 2¹⁶ + 3, и если возвести это в квадрат по модулю 2³¹, получится тождество

x[k+2] = 6·x[k+1] − 9·x[k]   (mod 2³¹)

Каждая тройка «случайных» чисел RANDU связана линейным уравнением. Для физической симуляции, которая берёт по три числа на координаты точки, это катастрофа: вся «случайность» живёт на 15 листах внутри куба. Покрутите ракурс — плоскости видны глазами:

Ракурс0%
0% — наивная проекция, 100% — взгляд вдоль нормали (9, −6, 1)у RANDU видно 15 плоскостей

Формальный способ ловить такие провалы — спектральный тест: он меряет, насколько плотно точки из последовательных чисел генератора заполняют пространство, и описан у Кнута во втором томе The Art of Computer Programming вместе с разбором, какие константы LCG выбирать. Там же критерий полного периода — теорема Халла—Добелла. Так что подбор a, c, m давно перестал быть искусством, это таблицы и проверки.

Что стоит в движках сегодня

Индустрия из LCG в основном выросла. Кто чем пользуется:

  • Java java.util.Random — тот самый LCG с 48-битным состоянием. Живая классика.
  • Python random и C++ std::mt19937 — Mersenne Twister (Мацумото и Нисимура, 1998). Период 2¹⁹⁹³⁷ − 1, гигантское состояние в 2,5 КБ.
  • JavaScript — в V8 Math.random() работает на xorshift128+: несколько сдвигов и XOR, очень быстро.
  • .NET — начиная с .NET Core 3.0 внутри System.Random семейство xoshiro.
  • Unity UnityEngine.Random — Xorshift128 по статье Марсальи, это прямо написано в документации. Состояние из четырёх 32-битных слов, статическое, поэтому из джобов и потоков пользоваться им нельзя.
  • PCG (Мелисса О'Нил, 2014, pcg-random.org) — LCG с пермутацией выходных бит; популярный выбор, когда в проекте нужен свой переносимый генератор с маленьким состоянием.

С генератором разобрались. Дальше начинается интересное: равномерная случайность, которую он выдаёт, игрокам не нравится.

2. Слой искажения: PRD против тильта

Почему Spotify переписал shuffle

В 2014 году Spotify опубликовал в инженерном блоге пост «How to shuffle songs?». Завязка такая: с запуска сервиса shuffle работал на тасовании Фишера—Йетса, то есть выдавал математически безупречную случайную перестановку плейлиста. И пользователи писали жалобы, что shuffle сломан. Два трека одного исполнителя подряд? «Это не рандом». Песня повторилась в начале новой сессии? «Это не рандом».

В посте Spotify прямо ссылается на ошибку игрока (gambler's fallacy): человек ждёт, что случайность «помнит» прошлое и обязана выравниваться на коротких дистанциях. Настоящая случайность ничего не помнит. Кластеры и повторы для неё норма, и именно кластеры люди опознают как неслучайность. Spotify сдался и заменил честный shuffle на алгоритм, который намеренно размазывает треки одного исполнителя по плейлисту. Стало «менее случайно» и перестало раздражать.

Теперь переложите это на игры. На крите с шансом 25% серия из шести промахов подряд имеет вероятность 0,75⁶ ≈ 17,8% — почти каждая пятая шестёрка атак. За вечер у игрока таких серий будет несколько, и каждая ощущается как обман. Поэтому поверх честного генератора игры строят второй слой — искажение распределения под восприятие. Давайте разберём основные приёмы, все с формулами.

PRD: крит, который помнит прошлые промахи

Самый известный приём — pseudo-random distribution (PRD) из Warcraft III и Dota 2. Идея: шанс прока растёт с каждой неудачей и сбрасывается после успеха. Вероятность на N-й атаке после последнего крита:

P(N)=CNP(N) = C \cdot N

Первая атака — шанс C, вторая — 2C, третья — 3C, и так до прока. Крит случился — счётчик обнулился.

Вся хитрость в подборе константы C. Условие: на длинной дистанции частота критов обязана совпасть с номиналом из тултипа. Распишем. Вероятность того, что прок случится ровно на n-й атаке:

Pn=Cni=1n1(1Ci)P_n = C n \prod_{i=1}^{n-1} (1 - C i)

Отсюда среднее число атак на один прок E[N] — обычное матожидание по этому распределению, а эффективная частота критов — 1/E[N]. Требуем 1/E[N] = p, где p — номинал, и решаем относительно C. В замкнутом виде уравнение не решается, C находят численно. Для номинальных 25% получается C = 0,08474409…

Смотрите, что это означает на практике для «25% крита» в Dota:

Атака после критаШанс крита
18,5%
216,9%
325,4%
650,8%
12100%

Первый удар критует в три раза реже тултипа, зато к 12-й атаке (C·12 ≥ 1) крит гарантирован. Длинная полоса невезения становится математически невозможной. Таблица констант для других номиналов (по Liquipedia, там она полная, от 5% до 95%):

НоминалCГарантия на атаке
5%0,00380264
10%0,0147568
15%0,0322232
20%0,0557018
25%0,0847412
30%0,118959
50%0,302104
70%0,571433

Реализация — десяток строк:

public class PrdRoller
{
    private readonly float _c;
    private int _n;

    public PrdRoller(float c) => _c = c; // C под номинал, из таблицы

    public bool Roll()
    {
        _n++;
        if (Random.value < _c * _n) // UnityEngine.Random, [0, 1)
        {
            _n = 0;
            return true;
        }
        return false;
    }
}

В Dota через PRD идут криты Crystalys и Daedalus, Coup de Grace у Phantom Assassin, Blade Dance Джаггернаута, Chaos Strike, бэши Skull Basher и Abyssal Blade — список длинный.

А вот как PRD ощущается против честной монетки — симуляция на 200 000 атак. Смотрите на хвост распределения: у честного генератора он уходит далеко вправо, у PRD обрывается на гарантии:

Номинал25%
C = 0,08474 · первая атака 8,5% · гарантия на 12
честный / PRD частота: честный PRD худшая засуха: честный PRD медианная серия: честный PRD
первые 60 атак — честный генератор (закрашено = прок):
те же 60 атак — PRD:

3. Pity, два броска и aim assist

Pity: у невезения появляется потолок

Гача-игры решают ту же проблему другим инструментом — pity-системой, то есть жёсткой гарантией. Канонический пример — Genshin Impact. Официально раскрытые в самой игре цифры для пятизвёздочного персонажа: базовый шанс 0,6%, «суммарная вероятность с учётом гарантии» 1,6%, гарантия на 90-й крутке. Откуда берётся разрыв между 0,6 и 1,6? Из устройства pity: где-то ближе к концу окна шанс начинает расти, и почти все «пятёрки» выпадают в этой зоне.

Соберите свою гарантию и посмотрите, как база превращается в эффективную вероятность:

База, %
Soft с крутки
Рост, п.п.
Hard cap
средняя крутка 62,3медианная 76эффективная вероятность 1,61%в тултипе при этом 0,60%дошли до гарантии 0,00%

Hearthstone устроен похоже. Гарантию легендарки в первых 10 паках нового набора и защиту от дублей (легендарка, которая у вас уже есть, из пака не выпадет, пока не собран весь пул сета) Blizzard анонсировала официально. Третье правило вывело комьюнити по статистике вскрытий: дольше 40 паков подряд сет легендарку не задерживает, и таймер у каждого набора свой.

По сути pity превращает геометрическое распределение «сколько паков до удачи» в распределение с обрезанным хвостом: «мне не везёт уже 200 круток» становится невозможным событием. Игрок защищён от худшего сценария. Запомните этот механизм — в разделе про деньги мы увидим его вторую сторону.

Fire Emblem: два броска вместо одного

Приём поинтереснее — подкрутить сами проценты на экране. В Fire Emblem начиная с Binding Blade (2002) и примерно до 12-й части работала система, которую фанаты назвали 2RN или true hit. Игра показывает шанс попадания, скажем, 70%. Но при расчёте бросается два числа от 0 до 99, берётся их среднее, и попадание засчитывается, если среднее меньше экранного шанса.

Сумма двух равномерных бросков распределена треугольником: середина диапазона вероятна, края редки. Поэтому реальная вероятность расходится с экранной:

На экранеНа самом деле
30%18,3%
50%50,5%
70%82,3%
90%98,1%
99%99,99%

Высокие шансы система завышает, низкие — занижает. И это ровно та поправка, которой требует интуиция: игроки воспринимают «90%» как «почти наверняка», и 2RN делает 90% почти наверняка (98,1%). А вражеский удар с шансом 30% долетает лишь в 18,3% случаев, и игрок чувствует себя «тактиком, который грамотно рискнул». Числа на экране врут, и именно поэтому им верят.

XCOM: подкрутка прямо в конфиге

XCOM с его легендарными промахами в упор пошёл тем же путём, только это можно прочитать глазами. В XCOM 2 откройте DefaultGameCore.ini — скрытые модификаторы меткости лежат прямо там. На ветеране работает BaseXComHitChanceModifier=1.2: экранные шансы вашего отряда умножаются на 1,2. После серии промахов включается aim assist: +10 к шансу на ветеране, +15 на командире. Отключено всё это только на сложности Legend. Источник знания — сами файлы игры и моды вроде Remove Aim Assists, которые эти строки вычищают.

То есть «подлый рандом XCOM», про который написаны тысячи гневных постов, на самом деле жульничает в пользу игрока. Просто промах с экранными «95%» запоминается на годы, а тихая прибавка +20% к реальному шансу — нет.

Shuffle bag: мешок вместо бросков

Последний приём — самый старый и самый простой: убрать независимые броски совсем. Shuffle bag: складываем все варианты в мешок, тасуем, раздаём по очереди; мешок опустел — собираем и тасуем заново. Канонический пример — правило 7-bag из Tetris Guideline: семь фигур тасуются и выдаются все по разу, потом новый мешок. Поэтому засуха по конкретной фигуре ограничена 12 ходами (фигура первой в одном мешке и последней в следующем), I-образная палка гарантированно приходит.

public class ShuffleBag<T>
{
    private readonly List<T> _items;
    private int _cursor;

    public ShuffleBag(IEnumerable<T> items)
    {
        _items = new List<T>(items);
        _cursor = _items.Count;
    }

    public T Next()
    {
        if (_cursor >= _items.Count) // мешок пуст — тасуем (Фишер—Йетс)
        {
            for (int i = _items.Count - 1; i > 0; i--)
            {
                int j = Random.Range(0, i + 1);
                (_items[i], _items[j]) = (_items[j], _items[i]);
            }
            _cursor = 0;
        }
        return _items[_cursor++];
    }
}

Тот же принцип отлично работает для всего, что не должно слипаться: фоновые реплики NPC, случайные события на карте, выбор музыкального трека. Spotify со своим размазыванием исполнителей по плейлисту, по сути, построил усложнённую версию мешка.

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

[больше интересного в телеграм]

4. Математика дропрейта и деньги

Сколько стоит нож

11 сентября 2017 года издатель Perfect World, через которого Valve работает в Китае, впервые опубликовал точные шансы кейсов CS:GO — китайский регулятор обязал раскрывать вероятности. Цифры такие: Mil-Spec 79,92%, Restricted 15,98%, Classified 3,20%, Covert 0,64%, нож или перчатки — 0,26%. Комьюнити-статистика по миллионам вскрытий эти проценты подтверждает, и до CS2 они не менялись. Ключ к кейсу стоит 2,49 $ с 2013 года. Pity-механики в кейсах нет: каждое вскрытие независимо.

Чтож, p = 0,0026 — давайте посчитаем, что это значит. Число попыток до первого успеха при независимых испытаниях описывается геометрическим распределением, и у него среднее равно 1/p:

E[N]=10,0026385E[N] = \frac{1}{0{,}0026} \approx 385

Звучит как ответ на вопрос «сколько кейсов до ножа». Да? Конечно же нет. Среднее у геометрического распределения тянет вверх длинный хвост везунчиков наоборот — тех, кто открыл тысячи. Спросим лучше: к какому кейсу нож выпадет у половины игроков? Это медиана:

N1/2=ln0,5ln(10,0026)267N_{1/2} = \frac{\ln 0{,}5}{\ln(1 - 0{,}0026)} \approx 267

Медиана 267 при среднем 385. И даже дойдя до «среднего» 385-го кейса, вы с вероятностью 36,7% всё ещё сидите без ножа — потому что 0,9974³⁸⁵ ≈ 0,367. У распределения нет памяти: после 384 пустых кейсов шанс в следующем всё те же 0,26%.

Теперь деньги. Медианный путь до ножа — 267 ключей по 2,49 $, то есть около 660 $ (цену самих кейсов на маркете для простоты опустим). Хотите уверенности 99%? Считаем N из 1 − 0,9974^N = 0,99, получается ≈1769 кейсов — около 4 400 $. А теперь мой любимый парадокс этой недели. Вероятность открыть 2000 кейсов и остаться без ножа:

0,997420000,55%0{,}9974^{2000} \approx 0{,}55\%

Это вдвое больше шанса выбить нож из первого же кейса (0,26%). Человек, потративший пять тысяч долларов впустую, статистически встречается чаще, чем человек, поймавший нож с первой попытки. Когда в следующий раз увидите ролик «нож с первого кейса» — помните, что где-то рядом ходят двое таких же везучих, только наоборот, и роликов они не снимают.

Кривая «хотя бы один раз»

Общая формула всей этой кухни — шанс хотя бы одного успеха за N независимых попыток:

P=1(1p)NP = 1 - (1 - p)^N

Для p = 1%: сто попыток дают 63,4%, девяносто процентов уверенности требуют 230 попыток, девяносто девять — 459. Интуиция читает «1%» как «гарантия за сотню», формула отвечает: за сотню — почти монетка, и даже после 459 попыток один из ста игроков уйдёт пустым.

У кривой есть наглядная характеристика — период полураспада. Каждые ln(2)/p попыток (для ножа CS — каждые ~266 кейсов) вероятность «всё ещё ничего» падает вдвое: 100% → 50% → 25% → 12,5%… Хвост тает по экспоненте, но в ноль не превращается никогда.

Введите свой дропрейт и цену попытки — кривая пересчитает невезение в деньги:

Шанс p, %
Цена, $
Попытки385
шанс к 38563,3%потрачено 959 $среднее 385медиана 26699% к 1 769 (4 405 $)пустым на «среднем» 36,7%

Где вероятность становится монетизацией

До этого момента вся математика была нейтральной: PRD и pity честно режут дисперсию, формулы открыты. Монетизация начинается там, где знание этих формул применяют против интуиции игрока, которая их не знает.

Зазор «среднее против медианы» мы уже видели: «в среднем нож за 385 кейсов» звучит как обещание, при том что треть игроков на 385-м кейсе ещё ни с чем, а часть улетает в хвост за 2000. «В среднем» в маркетинге дропрейта почти ничего не обещает конкретному человеку — это свойство распределения, у которого правый хвост бесконечный.

Дальше классика дарк-паттернов. Near-miss: анимация рулетки, где легендарка проезжает мимо указателя и тормозит в соседней ячейке — приём, давно описанный в литературе про игровые автоматы; проигрыш, замаскированный под «почти выигрыш», подталкивает к следующей попытке. Sunk cost через pity: счётчик «до гарантии осталось 12 круток» превращает накопленное невезение в актив, который жалко бросить, и игрок докупает круток, чтобы «не потерять прогресс» — та же механика, которая защищает от худшего сценария, работает двигателем доплат. Нераскрытые шансы: пока закон не заставит, точные вероятности предпочитают не публиковать, и кейсы CS — живой пример: цифры мир увидел только после китайского регулятора, через четыре года после запуска механики.

Что на это ответили регуляторы

Хронология короткая, и она вся вертится вокруг одного вопроса: является ли платный случайный приз азартной игрой.

  • Китай, 2017 — первый серьёзный ход: обязательное раскрытие вероятностей для лутбоксов и гачи. Прямое следствие — те самые проценты кейсов CS:GO от Perfect World.
  • Apple, декабрь 2017 — App Store Review Guidelines, пункт 3.1.1: приложение с платными лутбоксами обязано раскрывать шансы каждого типа предмета до покупки.
  • Бельгия, апрель 2018 — Gaming Commission изучила FIFA 18, Overwatch, CS:GO и Star Wars Battlefront II и признала платные лутбоксы в первых трёх азартной игрой по бельгийскому закону. Издатели отключили продажу лутбоксов в стране. Честности ради: исследование 2023 года в Collabra показало, что на практике запрет соблюдается неровно — формально запрещённые механики в бельгийских сторах находятся до сих пор.
  • Нидерланды — самый поучительный сюжет. В 2020-м суд Гааги поддержал регулятора: ЕА грозил штраф до 10 млн евро за паки FIFA Ultimate Team. А в марте 2022-го Государственный совет решение отменил: паки признали частью «более широкой игры навыка», под закон об азартных играх они не попадают. Регулирование лутбоксов — живой процесс с ходами в обе стороны, и финальной точки в нём пока нет.

Для разработчика мораль практическая: если в игре есть платная случайность, раскрытие шансов уже фактически стандарт (этого требуют Китай и App Store), а юридический статус механики зависит от страны и может поменяться за один судебный цикл.

5. Что выбрать разработчику

Соберём инструменты в одну картину. Выбор зависит от того, насколько игрок видит последовательность исходов.

  • Голый PRNG — когда отдельные исходы не образуют видимой игроку серии: разброс урона, ветвление генерации, визуальный шум. Дёшево, честно, дисперсию никто не заметит.
  • PRD — когда игрок наблюдает длинную плотную серию проверок: криты, проки, уклонения в бою. Режет тильт и отрезает катастрофы. Помните о цене: короткие серии промахов учащаются, а счётчик выучивается и становится частью меты.
  • Pity — когда событие редкое и дорогое: легендарки, пятизвёздочные персонажи, джекпот-дроп. Защищает худший сценарий и удерживает игрока. И он же — самый мощный монетизационный крючок из всех перечисленных, так что решение про pity всегда продуктовое, со всеми вытекающими вопросами этики.
  • Shuffle bag — когда варианты не должны слипаться и каждый обязан появляться регулярно: фигуры, треки, реплики, события. Простейший код, нулевая математика, предсказуемый максимум засухи.

И общее ограничение, которое стоит проговорить вслух: дисперсию нельзя убрать бесплатно. Любое сглаживание оплачивается либо предсказуемостью (PRD-счётчик в Dota отсчитывают, мешок Тетриса просчитывают), либо смещением коротких серий, либо — в случае pity — созданием финансового крючка. Случайность в игре всегда компромисс между математической честностью, ощущением честности и деньгами. Полезно хотя бы понимать, в какую сторону вы этот компромисс двигаете и что именно говорите игроку в тултипе. Если тултип обещает 25%, а первый удар критует с 8,5% — где-то об этом стоит честно рассказать хотя бы вики-сообществу, Valve вон не скрывает.

Итого

Пробежимся по выводам. Случайность в играх собрана из трёх слоёв. Внизу генератор — детерминированная рекуррента, у которой случайного только seed, а качество измеряется спектральным тестом. В середине искажатели — PRD, pity, 2RN, aim assist, shuffle bag, которые меняют распределение под человеческое восприятие, потому что честная равномерность ощущается сломанной. Наверху экономика — геометрическое распределение с его зазором между средним и медианой, кривая 1 − (1 − p)^N и регуляторы, которые пытаются решить, где тут граница с казино.

Мне в этой истории больше всего нравится момент со Spotify: компания с лучшими инженерами по рекомендациям публично признала, что математически идеальный shuffle пришлось сломать, потому что людям он казался нечестным. В геймдеве это знание стоит денег в обе стороны — и когда вы делаете криты приятными, и когда кто-то делает кейсы прибыльными.

Надеюсь, материал был полезен. Буду рад обсудить ваши способы готовить случайность — особенно если вы шипили игру с собственной pity-системой и можете рассказать, как она пережила встречу с игроками.

Референсы

[больше интересного в телеграм]
// нужна помощь в проекте?

Консультации и техническое сопровождение

Геймдев, графика, AR/VR и computer vision — с математикой как ядром. Разбор вашей задачи и предложение по формату — бесплатно.

Оставить заявку →