Сиды и воспроизводимость
Сегодня ты научишь случайность слушаться: с помощью сида сможешь поймать понравившийся вариант цыплёнка и воспроизвести его снова и снова, кадр в кадр.
Главная идея урока:
random()иnoise()на самом деле не «настоящая» случайность, а длинная заранее заготовленная цепочка чисел. Сид (seed) — это стартовое число, с которого цепочка начинается. Одинаковый сид — одинаковая цепочка — одинаковая картинка. Задаёшь его черезrandomSeed()иnoiseSeed().
Зачем тебе вообще нужна повторяемость
Представь: ты сидишь, обновляешь свой генеративный скетч с цыплёнком, и вдруг — бац! — на экране появляется идеальный вариант. Перья легли как надо, поза смешная, цвета совпали. Ты делаешь скриншот, тянешься за телефоном, чтобы скинуть другу… и случайно жмёшь обновление страницы. Кадр исчез. Навсегда. random() в следующий раз раскидал всё иначе, и тот самый цыплёнок утонул где-то среди миллиардов других вариантов.
Знакомое чувство потери? Так бывает, когда в любимой игре с процедурной генерацией тебе выпадает шикарный мир, а потом ты случайно стартуешь новую игру и теряешь его. И тут многие игры предлагают спасение — seed мира: маленький код, который можно записать, дать другу и получить ровно тот же самый мир. В Minecraft вводишь сид — и горы, пещеры, деревни встают точно там же, что у тебя. Это та же самая магия, которую мы сегодня освоим в p5.js.
В прошлых уроках мы намеренно отпускали хаос на волю. В уроке про управляемый хаос ты раскидывал перья случайным образом, а в уроке про шум Перлина заставлял CodeChick двигаться плавно и органично. Но у обоих приёмов был один минус: каждый запуск рисовал что-то новое, и поймать «тот самый» вариант было невозможно. Сегодня мы добавим к случайности кнопку «зафиксировать». К концу урока ты сможешь перебирать варианты цыплёнка по номерам, как страницы в каталоге, и в любой момент вернуться к тому, что понравился.
Что такое сид и почему случайность — это притворство
Метафора: толстая книга случайных чисел
Вот неожиданная правда: компьютер не умеет придумывать по-настоящему случайные числа. Он притворяется. Представь, что у компьютера есть огромная толстая книга, на каждой странице которой напечатан длинный-предлинный ряд цифр, перемешанных так хитро, что никакой закономерности глазом не уловить. Когда ты зовёшь random(), компьютер просто читает следующее число из этой книги и переходит к следующему. Числа выглядят случайными — но они давно напечатаны и лежат в строгом порядке.
Теперь главный вопрос: с какой страницы начать читать? Вот это стартовое место и есть сид (seed). По умолчанию p5.js открывает книгу на случайной странице при каждом запуске — поэтому ты каждый раз получаешь новую последовательность и новую картинку. Но если ты сам скажешь «начни читать со страницы 42», то и цепочка чисел пойдёт всегда одна и та же. А раз числа те же — значит, и перья лягут одинаково, и цыплёнок получится точь-в-точь как в прошлый раз.
Сид (seed) — стартовое число генератора случайности: одинаковый сид даёт одинаковый результат каждый раз. Команда
randomSeed(42)говорит: «открой книгу на странице 42 и читай оттуда».
Почему это называют «псевдослучайностью»
Из-за этой книги-обманки настоящие программисты называют такую случайность псевдослучайной — то есть «как будто случайной». Приставка «псевдо» по-гречески значит «ложный, поддельный». Числа достаточно перемешаны, чтобы для рисунка, игры или раскидывания перьев их хватало за глаза, но в глубине это строгий, повторяемый порядок. И именно эта «поддельность» — наш лучший друг: благодаря ей мы можем фиксировать результат. По-настоящему случайные числа (их берут, например, из шума атмосферы или из дрожания электронов) повторить было бы нельзя в принципе — а вот нашу книгу-обманку можно открыть на любой странице сколько угодно раз.
Может показаться, что «поддельная» случайность — это что-то второсортное. На самом деле наоборот: для творчества и игр она удобнее настоящей. Художнику-генеративщику важно не то, чтобы хаос был «честным», а то, чтобы он был красивым и чтобы понравившийся результат не утёк сквозь пальцы. Псевдослучайность даёт ровно это: бесконечное разнообразие вариантов плюс возможность вернуться к любому из них по номеру. Настоящий неповторимый хаос тут был бы только помехой — ты бы любовался шедевром, который никогда больше не увидишь.
Сид у random и сид у noise — две разные книги
Запомни важное: у random() своя книга чисел, а у noise() (шума Перлина) — отдельная. Поэтому и сидов два: randomSeed() управляет первой, а noiseSeed() — второй. Если в твоём скетче работают оба генератора, для полной воспроизводимости нужно зафиксировать оба сида. Зафиксируешь только один — половина картинки повторится, а половина опять будет скакать.
Разбираем на примерах
Пример 1: один и тот же разброс перьев каждый раз
Начнём с простого. Раскидаем вокруг CodeChick кучку перьев через random() — но так, чтобы при каждом запуске они ложились абсолютно одинаково. Секрет в одной строке: randomSeed() в самом начале draw().
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
randomSeed(42); // открываем книгу всегда на странице 42
// CodeChick в центре
fill(255, 209, 64);
circle(200, 200, 80);
fill(255, 140, 30);
triangle(226, 195, 250, 200, 226, 205);
// 40 перьев вокруг — разброс одинаковый каждый запуск
fill(255, 235, 150);
for (let i = 0; i < 40; i = i + 1) {
let x = random(width);
let y = random(height);
let d = random(6, 16);
ellipse(x, y, d, d * 0.5);
}
}Результат: на голубом небе сидит жёлтый CodeChick с оранжевым клювом, а вокруг него рассыпаны 40 светло-жёлтых перьев разного размера. Картинка выглядит хаотичной и живой — но сколько бы раз ты ни перезапускал скетч, перья ложатся точь-в-точь на те же места. Будто это вовсе не случайность, а заранее нарисованная композиция.
Вся магия — в строке randomSeed(42);. Она стоит в начале draw() и каждый кадр заново «отматывает книгу» на страницу 42. Поэтому первый random(width) всегда возвращает одно и то же число, второй — тоже своё постоянное, и так далее по списку. Убери эту строку — и перья при каждом обновлении страницы будут раскиданы заново. Поменяй число 42 на 7, на 100, на 2024 — и получишь другие, но тоже фиксированные раскладки. Каждый сид — это отдельная застывшая композиция, которую ты можешь вызвать по номеру.
Пример 2: каталог цыплят — листаем варианты по номеру сида
Раз каждый сид даёт свою застывшую картинку, давай сделаем из этого каталог: по клику мыши будем переходить к следующему сиду и собирать цыплёнка заново. Получится листалка вариантов, как лента в соцсети, только каждый «пост» помечен номером и его легко вернуть.
let seed = 0; // текущий номер варианта
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(245, 240, 230);
randomSeed(seed); // каждый кадр собираем вариант по текущему сиду
// тело цыплёнка со случайными «настройками внешности»
let bodyW = random(70, 110);
let bodyH = random(60, 100);
let beakLen = random(18, 34);
let cheekR = random(255);
fill(255, 209, 64);
ellipse(200, 210, bodyW, bodyH);
fill(255, cheekR, 30);
triangle(200 + bodyW / 2, 200, 200 + bodyW / 2 + beakLen, 205,
200 + bodyW / 2, 210);
// глаз
fill(40);
circle(215, 195, 10);
// подпись номера варианта
fill(60);
textSize(18);
text("CodeChick #" + seed, 20, 30);
}
function mousePressed() {
seed = seed + 1; // клик — следующий вариант каталога
}Результат: на светлом фоне стоит цыплёнок, у которого тело то пошире, то поуже, клюв то короткий, то длинный, а оттенок клюва меняется. В левом верхнем углу подпись вроде «CodeChick #0». Каждый клик мышью увеличивает сид на единицу — и цыплёнок мгновенно «пересобирается» в новый вариант с другой подписью: #1, #2, #3… А если ты вернёшься к #2 (например, перезапустишь скетч и кликнешь дважды), увидишь ровно того же самого цыплёнка, что был под номером 2. Номер сида — это его адрес.
Тут важно, что randomSeed(seed) стоит в начале каждого кадра, а сам seed хранится в переменной состояния вне draw() — поэтому он переживает кадры и помнит, на каком варианте мы остановились. Каждый клик в mousePressed() двигает нас на следующую «страницу каталога». Заметь, как удобно: чтобы запомнить понравившегося цыплёнка, тебе не нужен скриншот — достаточно записать его номер. Увидел красавчика под #17 — записал «17» в заметки, и в любой момент вернёшь его одной строкой seed = 17.
Обрати внимание ещё на одну тонкость: внутри одного кадра мы зовём random() несколько раз подряд — для ширины тела, высоты, длины клюва, оттенка щёк. И каждый из этих вызовов читает своё число из книги, по порядку. Поэтому важен не только сам сид, но и порядок вызовов random(): если ты переставишь строки местами или добавишь новый random() в середину, то все числа после него «сдвинутся» и вариант под номером 17 станет выглядеть иначе. Это нормально — просто помни, что сид фиксирует картинку только при неизменном коде. Меняешь логику рисования — каталог пересобирается заново.
Пример 3: фиксируем шум Перлина через noiseSeed()
Теперь покажем, что у шума своя книга и свой сид. Нарисуем «холмистый ландшафт» под цыплёнком через noise() — мягкую волнистую линию. С noiseSeed() один и тот же холм будет получаться каждый запуск, а меняя сид, мы получаем разные пейзажи — все плавные, но непохожие.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(135, 206, 235);
noiseSeed(10); // фиксируем ИМЕННО шум — у него своя книга
// волнистая земля из шума Перлина
fill(120, 190, 90);
noStroke();
beginShape();
vertex(0, height);
for (let x = 0; x <= width; x = x + 10) {
let h = noise(x * 0.01) * 150 + 180;
vertex(x, h);
}
vertex(width, height);
endShape(CLOSE);
// CodeChick стоит на холме
fill(255, 209, 64);
circle(200, noise(200 * 0.01) * 150 + 150, 60);
fill(255, 140, 30);
triangle(225, noise(200 * 0.01) * 150 + 148,
245, noise(200 * 0.01) * 150 + 152,
225, noise(200 * 0.01) * 150 + 156);
}Результат: на фоне голубого неба раскинулся плавный зелёный холм — мягкая волнистая линия земли без резких скачков, типичная для шума Перлина. На вершине холма сидит жёлтый CodeChick с клювом. Сколько ни перезапускай — холм всегда одной и той же формы, потому что noiseSeed(10) фиксирует шумовую книгу. Поменяй 10 на 3 или 99 — рельеф станет другим (другие пригорки и впадины), но таким же плавным.
Главный вывод примера: здесь мы трогали noiseSeed(), а не randomSeed() — потому что рисуем через noise(). Это две независимые системы. Если бы в этом же скетче ещё и перья раскидывались через random(), для полной воспроизводимости пришлось бы поставить обе строки: и randomSeed(...), и noiseSeed(...). Зафиксируешь только шум — холм застынет, но перья продолжат прыгать при каждом запуске.
Частые ошибки и подводные камни
Ставят randomSeed() в setup() вместо draw() при анимации. Если в скетче меняются варианты и ты хочешь, чтобы каждый кадр пересобирался по текущему сиду,
randomSeed(seed)должен стоять в началеdraw(). Поставишь вsetup()— сид зафиксируется один раз на старте, а при смене переменнойseedвdraw()картинка уже не отмотается на новую страницу книги.Путают randomSeed и noiseSeed. Это разные книги.
randomSeed()фиксирует толькоrandom(), аnoiseSeed()— толькоnoise(). Зафиксировал не тот — и нужный генератор продолжит выдавать новое каждый запуск. Если в скетче работают оба, фиксируй оба.Зовут randomSeed() в середине цикла отрисовки. Сид надо задавать до первого вызова
random()в кадре — обычно сразу послеbackground(). Если воткнутьrandomSeed()между вызовамиrandom(), ты «перематываешь книгу» на полпути, и числа после этой строки начнут повторять начало последовательности — получится неожиданный сбой композиции.Думают, что сид 42 чем-то особенный. Никакой магии в конкретном числе нет — это просто номер страницы. Сид 42, 7 или 100000 одинаково хорошо работают, каждый даёт свою застывшую картинку. Число 42 любят программисты как шутку (привет «Автостопом по галактике»), но генератору всё равно.
Забывают записать понравившийся сид. Самая обидная ошибка: нашёл идеального цыплёнка, полюбовался — и не записал его номер. Закрыл вкладку, и без сида вернуть его уже почти нереально. Привыкай: понравился вариант — сразу запиши его сид в заметку или прямо в комментарий к коду.
Мини-практика: галерея любимых цыплят
Доделай каталог из Примера 2 в маленькую персональную галерею. План такой:
- Возьми за основу листалку с переменной
seedи кликом мыши. - Добавь больше «настроек внешности», которые зависят от
random(): например случайный цвет тела (fill(random(200, 255), random(180, 230), random(40, 120))), случайный размер глаза, случайный наклон клюва. Чем больше параметров — тем разнообразнее каталог. - Полистай варианты кликами и запиши номера 3–4 любимых цыплят прямо в комментарий в начале файла, например
// топ: 7, 23, 41, 88. - Сделай две клавиши через
keyPressed(): стрелка вправо —seed = seed + 1(следующий), стрелка влево —seed = seed - 1(предыдущий). Так листать удобнее, чем только вперёд.
Когда заработает, попробуй продвинутый шаг: добавь в скетч ещё и фон из noise() (например волнистый холм из Примера 3) и зафиксируй его отдельным noiseSeed(). Сделай так, чтобы холм при листании цыплят не менялся (фиксированный noiseSeed(10)), а сам цыплёнок менялся по randomSeed(seed). Это наглядно покажет тебе, что две книги случайности и правда независимы: одна застыла, другая листается.
Итоги
Сегодня ты приручил случайность и научил её повторяться:
- Случайность в p5.js — псевдослучайная. Это длинная заготовленная цепочка чисел («книга»), а не настоящий хаос. Поэтому её можно зафиксировать и воспроизвести.
- Сид (seed) — стартовое число, «страница книги», с которой начинается цепочка. Одинаковый сид → одинаковая последовательность → одинаковый кадр.
randomSeed(n)фиксируетrandom(), аnoiseSeed(n)—noise(). Это две разные книги: при использовании обоих генераторов фиксируй оба сида. Ставь их в началеdraw(), до первого вызова случайности.- Сид — это адрес варианта. Понравился кадр — запиши его номер, и вернёшь любимого цыплёнка одной строкой, без всяких скриншотов.
Теперь у тебя есть суперсила генеративного художника: ты можешь отпускать хаос на волю, ловить лучшие случайные находки и сохранять их по номеру. Это ровно тот приём, которым пользуются авторы NFT-коллекций и генеративных аватарок — у каждой работы свой сид. В следующем уроке мы пойдём дальше по разделу случайности и научимся смешивать управляемый хаос с шумом, чтобы получать ещё более живые и непредсказуемые, но при этом воспроизводимые миры вокруг CodeChick. А пока — полистай свой каталог цыплят и запиши номера тех, что заставили тебя улыбнуться!