random(): управляемый хаос
Сегодня ты научишь компьютер бросать кубик: получать случайные числа функцией random() и раскидывать вокруг цыплёнка перья так, что двух одинаковых кадров не будет.
random() — функция p5.js, возвращающая случайное число в заданном диапазоне. Один вызов — одно непредсказуемое число, и именно из этой непредсказуемости рождается живая, неповторимая картинка.
Зачем тебе случайность
Открой любую игру, в которую ты залипаешь. Звёзды на ночном фоне разбросаны как попало, искры от взрыва летят в разные стороны, трава колышется не строем, а вразнобой. Если бы дизайнер расставлял каждую звезду вручную по линейке — получился бы скучный узор из обоев, а не небо. Живое ощущение даёт именно лёгкий беспорядок.
В прошлом уроке про цикл for ты научился строить ровные ряды: цыплята выстраивались по сетке, аккуратно, как клетки в тетради. Это здорово для порядка, но природа так не работает. Перья, упавшие с цыплёнка, не лягут ровной решёткой — они разлетятся куда попало. Вот за этим «куда попало» и нужна случайность.
К концу урока ты соберёшь такой кадр: жёлтый цыплёнок в центре холста, а вокруг него — облако перьев. Каждое перо в своей точке, своего размера и чуть-чуть своего оттенка. И главное — каждый раз, когда скетч перезапускается, облако новое. Ты не рисуешь конкретную картинку, ты задаёшь правила, по которым она рождается. Это и есть генеративность, фишка всего нашего курса.
Подумай вот о чём. Когда ты делаешь скриншот мема и кидаешь другу, картинка всегда одна и та же — фиксированная. А генеративная работа похожа скорее на ленту рекомендаций: правила одни, а лента каждый раз новая. Художники по всему миру продают целые коллекции картинок, где они написали один скетч с random(), а компьютер сгенерировал тысячу непохожих вариантов. Ты сегодня делаешь ровно первый шаг к этому: учишься писать не картинку, а её рецепт.
Как работает random(): кубик внутри кода
Представь обычный игральный кубик. Ты его бросаешь — и не знаешь заранее, выпадет 1 или 6. Но ты точно знаешь правило: число будет от 1 до 6, не меньше и не больше. random() — это такой же кубик, только грани у него ты задаёшь сам.
У функции три способа вызова, и их полезно держать в голове все три.
| Вызов | Что вернёт |
random() | дробное число от 0 (включительно) до 1 (не включая) |
random(10) | дробное число от 0 до 10 |
random(5, 15) | дробное число от 5 до 15 |
Обрати внимание на слово «дробное». Кубик в жизни даёт только целые числа, а random() щедрее: он может вернуть 7.42, или 0.0013, или 14.999. Это удобно, когда ты раскидываешь точки по холсту — там не нужны ровные целые, наоборот, дробные координаты делают картинку плавнее.
Самое важное, что нужно почувствовать: random() возвращает значение. Это не команда «нарисуй» и не магия — это просто число, которое ты можешь сохранить в переменную, передать в fill(), в circle(), куда угодно. Везде, где ты раньше писал конкретную цифру, теперь можно поставить случайную.
Первый бросок
Давай посмотрим самый простой пример — нарисуем кружок в случайном месте холста.
function setup() {
createCanvas(400, 400);
background(30);
noStroke();
fill(255, 221, 51); // жёлтый, цвет цыплёнка
let x = random(400); // случайное число от 0 до 400
let y = random(400);
circle(x, y, 40);
}Результат: на тёмно-сером холсте появляется один жёлтый кружок диаметром 40 пикселей. Где именно он окажется — заранее неизвестно: может прижаться к верхнему углу, может оказаться в центре. Перезапусти скетч — кружок прыгнет в новое место.
Разберём построчно. random(400) бросает наш кубик с диапазоном от 0 до 400 — как раз ширина холста. Полученное число мы кладём в переменную x. То же самое для y. А дальше circle(x, y, 40) рисует кружок в этой случайной точке. Никакой магии: сначала получили числа, потом нарисовали по ним.
Случайность в цвете
Координаты — это только начало. Случайным может быть всё, что выражается числом. Покрасим цыплёнка в случайный оттенок жёлто-оранжевого.
function setup() {
createCanvas(400, 400);
background(30);
noStroke();
let r = 255;
let g = random(150, 230); // зелёная доля гуляет
let b = random(0, 60); // синей почти нет
fill(r, g, b);
circle(200, 200, 120); // тело цыплёнка в центре
}Результат: в центре холста — большой кружок-цыплёнок. Его цвет каждый запуск чуть разный: от тёплого оранжевого (когда зелёная доля маленькая) до ясно-жёлтого (когда она ближе к 230). Красная доля закреплена на максимуме, поэтому оттенок всегда остаётся в тёплой гамме и никогда не уходит в зелень.
Видишь приём? Мы не делаем случайным весь цвет, иначе цыплёнок мог бы стать фиолетовым или синим. Мы фиксируем красный канал и даём гулять только зелёному и синему в узких рамках. Так разнообразие есть, но герой остаётся узнаваемым. Это и называется «управляемый хаос»: ты решаешь, где случайности можно разгуляться, а где нельзя. Запомни это словосочетание — оно описывает почти всю генеративную графику. Чистый хаос выглядит как помехи на старом телевизоре, а чистый порядок — как клетка в тетради. Красота живёт посередине, и регулируешь её именно ты, шириной диапазонов random().
Случайный выбор из вариантов
Иногда тебе не нужно дробное число вообще — нужно выбрать один из нескольких вариантов. Скажем, перо у цыплёнка бывает трёх цветов. У random() для этого есть отдельный фокус: если передать ей массив, она вернёт случайный элемент.
let palette = [
color(255, 221, 51), // жёлтый
color(255, 160, 40), // оранжевый
color(255, 120, 30) // рыжий
];
let c = random(palette); // вернёт ОДИН цвет из трёх
fill(c);Результат: переменная c получает один из трёх заранее выбранных цветов, и какой именно — решает случай. Это удобнее, чем гонять числа в random(): ты сам собрал палитру, которая точно красива, и доверил выбор кубику. Тот же приём работает с любым списком — формами, словами, картинками.
Раскидываем перья: random() внутри цикла
Один случайный кружок — это мило, но настоящая сила открывается, когда random() встречается с циклом for из прошлого урока. Цикл повторяет рисование много раз, а random() на каждом повторе даёт новые числа — и вместо ряда одинаковых фигур получается рассыпанное облако.
function setup() {
createCanvas(400, 400);
background(30);
noStroke();
// сначала сам цыплёнок в центре
fill(255, 221, 51);
circle(200, 200, 100);
// теперь 80 перьев вокруг него
for (let i = 0; i < 80; i++) {
let x = random(width);
let y = random(height);
let d = random(4, 14); // размер пера тоже случайный
fill(255, random(160, 210), 40);
circle(x, y, d);
}
}Результат: в центре сидит крупный жёлтый цыплёнок, а по всему холсту рассыпано 80 маленьких пёрышек разного размера и слегка разного оттенка. Картинка выглядит живой и неповторимой; перезапусти — и перья лягут совсем иначе. Двух одинаковых кадров ты не получишь.
Что здесь происходит на каждом витке цикла. Переменная i считает от 0 до 79 — всего 80 повторов. Внутри каждого повтора мы заново бросаем кубик для x, для y, для размера d и для зелёной доли цвета. Поэтому каждое перо получает свою четвёрку случайных чисел и встаёт на своё место. Заметь: я использовал width и height вместо чисел 400 — это встроенные переменные p5.js с размерами холста, с ними код не сломается, если ты поменяешь размер canvas.
Поиграй с числами
Лучший способ понять случайность — крутить ручки. Попробуй прямо мысленно (или в реальном скетче) поменять:
- Количество перьев. Замени
80на300— холст загустеет, цыплёнок утонет в метели из перьев. Поставь10— будет редкая россыпь. - Диапазон размера.
random(4, 14)даёт мелкие пёрышки. Сделайrandom(10, 40)— и перья станут крупными, разнобой заметнее. - Гамму цвета. Расширь зелёную долю до
random(100, 255)— оттенки запрыгают сильнее, от рыжего до почти белого.
Никакого «правильного» числа тут нет. Ты художник, а random() — твоя кисть, которая немного дрожит. Сколько дрожи — решаешь ты диапазоном.
Частые ошибки и подводные камни
Случайность простая, но именно на ней новички спотыкаются особенно обидно. Вот что ломается чаще всего.
1. random() зовут один раз, а ждут разнообразия
Классика. Человек хочет 50 перьев разных размеров и пишет так:
let d = random(4, 14); // вызвали ОДИН раз, до цикла
for (let i = 0; i < 50; i++) {
circle(random(width), random(height), d); // d всё время одно и то же!
}Результат: перья разбросаны по холсту в разных местах, но все одинакового размера — скучно и неестественно. Случайное число посчиталось один раз и легло в d; в цикле оно просто переиспользуется. Чтобы размер менялся, вызов random() должен стоять внутри цикла.
2. Случайные числа сыплются в draw() и всё мерцает
Если положить рисование перьев в draw() вместо setup(), то random() будет бросать новые числа 60 раз в секунду. Перья начнут бешено скакать и мигать — глазам больно. Случайную, но застывшую картинку рисуй в setup() (он выполняется один раз). В draw() случайность уместна, только когда ты хочешь постоянного движения — например, мерцание звёзд.
3. Путают random(5, 15) и random(15, 5)
Первый аргумент — нижняя граница, второй — верхняя. Если случайно переставить их местами, в одних версиях p5.js диапазон отработает как ожидалось, а логика чтения кода всё равно сломается у тебя в голове. Привыкай писать от меньшего к большему: random(min, max).
4. Забывают, что число дробное
Иногда тебе нужен случайный целый индекс — например, выбрать одну из 4 картинок. Если написать random(4), получишь дробное вроде 2.71, и обращение по такому индексу к массиву сломается. Оберни вызов в floor(): floor(random(4)) даст ровно 0, 1, 2 или 3. Это частая засада на следующих уроках, запомни её заранее.
5. Ждут «равномерности на глаз»
80 случайных точек на холсте почти наверняка лягут неровно: где-то будет сгусток, где-то пустое пятно. Это нормально! Случайность не обязана выглядеть равномерно — настоящая равномерность как раз кажется неестественной. Если тебе нужна именно гладкая, «причёсанная» случайность без резких скачков, для этого есть шум Перлина — о нём поговорим в следующем уроке.
6. Забывают фиксировать фон
Ещё одна тонкость, связанная с предыдущими. Если ты рисуешь случайные перья в draw() и при этом не вызываешь background() в начале каждого кадра, перья будут накапливаться поверх старых. За пару секунд весь холст забьётся точками до белизны. Иногда такой эффект «накопления» нужен специально — получается красивая текстура. Но если ты ждал аккуратное облако из 80 перьев, а холст заплывает, проверь именно это: фон должен перерисовываться каждый кадр, либо рисование вообще должно жить в setup().
Мини-проект: облако перьев цыплёнка
Теперь твоя очередь. Возьми пример с перьями за основу и доведи его до настоящего портрета цыплёнка в метели. Вот чек-лист задания:
- Нарисуй в центре холста цыплёнка: большой жёлтый круг-тело и круг поменьше сверху — голову. Координаты головы привяжи к телу, чтобы они не разъезжались.
- Добавь оранжевый клюв — маленький треугольник или кружок сбоку головы. Это уже знакомый тебе герой курса, просто теперь вокруг него будет буря.
- Циклом
forраскидай 60–120 перьев. Размер каждого —random(3, 16), координаты — по всему холсту черезrandom(width)иrandom(height). - Сделай цвет перьев слегка случайным в тёплой гамме: красную долю закрепи, зелёную пусти через
random(). - Бонус. Добавь немного прозрачности перьям — четвёртый аргумент в
fill(), напримерfill(255, random(160, 210), 40, 150). Перья начнут просвечивать друг сквозь друга, и облако станет воздушнее.
Запусти скетч несколько раз. Каждый запуск — новый портрет одного и того же цыплёнка. Сохрани тот, что понравится больше всего: это уже маленькая генеративная работа, твоя собственная.
Когда базовая версия заработает, попробуй задать себе вопрос художника: где случайности слишком много, а где мало? Если перья закрывают цыплёнка — уменьши их число или отодвинь подальше от центра. Если облако выглядит мёртвым и одинаковым — расширь диапазон размеров. В этом и есть настоящая работа генеративного художника: ты не двигаешь каждую точку мышкой, ты крутишь диапазоны и смотришь, как меняется характер всей картины. Покажи результат другу и попроси перезапустить — пусть увидит, что один и тот же код рождает разные кадры. Это всегда производит впечатление.
Итоги
Сегодня ты приручил случайность. Коротко главное:
random()возвращает случайное число, а не рисует само по себе — его можно поставить везде, где раньше стояла цифра.- Диапазон ты задаёшь сам:
random(max)илиrandom(min, max). Это грани твоего кубика. - Случайность раскрывается в паре с циклом
for: вызовrandom()должен быть внутри цикла, иначе все фигуры выйдут одинаковыми. - Застывшую случайную картинку рисуй в
setup(), иначе она замигает вdraw(). - «Управляемый хаос» — это когда ты решаешь, чему случаться, а что закрепить. Так цыплёнок остаётся жёлтым, но каждый кадр всё равно уникален.
Есть одна проблема: наш random() прыгает резко, без всякой плавности. Для перьев это здорово, а вот для плавно колышущейся травы или дыма такие скачки не годятся — там нужна гладкая случайность. В следующем уроке мы познакомимся с шумом Перлина: той же случайностью, но текучей, как вода. Цыплёнок там обзаведётся развевающимся на ветру хохолком. До встречи!