Фон, наслоение и порядок отрисовки

Холст в p5.js работает как стопка наклеек: кто лёг последним, тот и сверху — и именно ты решаешь, кто окажется наверху.

Главное правило урока: в p5.js фигуры рисуются строго по порядку строк кода. Что вызвано позже — ложится выше и перекрывает то, что было раньше. Фон background() почти всегда идёт первым, потому что он должен лежать под всем.

Зачем тебе порядок отрисовки

Представь, что ты собираешь коллаж в сторис: сначала кладёшь фотку заката, потом поверх неё лепишь стикер с сердечком, а сверху — подпись. Если бы ты сделал наоборот — сначала подпись, потом фотку, — фотка закрыла бы текст, и его никто бы не увидел. В p5.js всё ровно так же. Холст — это лист в скетчбуке, а каждая фигура — наклейка, которую ты приклеиваешь сверху. Порядок наклеивания решает всё.

В прошлом уроке мы разбирали заливку, обводку и толщину линий — ты научился красить фигуры и рисовать им контур. Но как только фигур стало больше одной, появился новый вопрос: а что будет, если они пересекаются? Кто кого закроет? Сегодня мы превратим этот хаос в управляемую сцену и наконец-то посадим нашего цыплёнка CodeChick на настоящее небо — так, чтобы он оказался поверх фона, а не утонул в нём.

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

Звучит как мелочь? На самом деле это один из тех моментов, после которых код перестаёт быть случайной кашей из строк и становится управляемой картиной. Любая игра, которую ты запускал на телефоне, рисует сцену именно так: сначала фон уровня, потом платформы, потом враги, потом герой, а сверху — интерфейс с очками и сердечками. Если бы порядок сбился, твой персонаж скрылся бы за фоном, а счёт спрятался под землёй. Освоив правило слоёв на цыплёнке, ты получишь тот же инструмент, которым пользуются авторы настоящих игр и анимаций. Так что давай разбираться по шагам — это проще, чем кажется.

Фон — это первый слой

Что делает background()

Функция background() заливает весь холст одним цветом, стирая всё, что было нарисовано до неё в этом кадре. Это как взять широкую кисть и закрасить весь лист одним движением. Поэтому фон почти всегда вызывают в самом начале draw() — он расчищает сцену и кладёт нижний слой, на который потом ляжет всё остальное.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(135, 206, 235); // небесно-голубой (RGB)
}

Результат: весь холст 400×400 равномерно залит спокойным небесно-голубым цветом. Больше на кадре ничего нет — это чистое небо, наш будущий нижний слой.

Цвет можно задавать так же, как и заливку fill() из прошлого урока: одним числом (оттенок серого), тремя числами (RGB) или строкой вроде "#87ceeb". Например, background(20) даст почти чёрный фон — удобно для ночных сцен и фейерверков, до которых мы дойдём ближе к концу курса. А background(255) вернёт чистый белый лист, будто ты только что открыл новую страницу скетчбука.

Маленькая, но важная деталь: createCanvas() мы по-прежнему вызываем один раз в setup(), а вот background() живёт в draw() и срабатывает каждый кадр. Почему так? Потому что холст создаётся однажды — это сам лист бумаги. А фон мы перекрашиваем заново на каждом кадре — это как стирать ластиком всё нарисованное и начинать кадр с чистого неба. Если перепутать и засунуть createCanvas() в draw(), p5.js будет создавать новый холст по сто раз в секунду — а это уже точно не то, что тебе нужно.

Почему фон стирает кадр

Тут кроется важная мысль. draw() повторяется десятки раз в секунду, и каждый её прогон рисует новый кадр поверх старого. Если ты не вызовешь background() в начале draw(), то старые кадры останутся на холсте и новые будут накапливаться поверх — как если бы ты рисовал в скетчбуке, не переворачивая страницу. Иногда это даже красиво (получаются следы и шлейфы), но чаще всего ты хочешь чистый лист каждый кадр. Поэтому привыкай: первая строка в draw() — почти всегда background().

Чтобы это уложилось окончательно, представь флипбук — блокнот, в углу которого ты рисуешь человечка и быстро листаешь страницы, чтобы он «ожил». Каждая страница — это новый кадр. background() в начале draw() — это как взять новую чистую страницу перед тем, как рисовать следующую позу человечка. Без него ты бы рисовал новую позу прямо поверх старой на том же листе, и вместо движения получилась бы мазня из наложенных друг на друга человечков. Один вызов background() — одна чистая страница — один аккуратный кадр.

Правило слоёв: позже нарисовано — выше лежит

Теперь — сердце урока. p5.js читает твой код сверху вниз и рисует фигуры в том же порядке. Каждая новая фигура ложится поверх предыдущих, как наклейка поверх наклейки. Никакого z-index, как в CSS, здесь нет: единственный способ управлять слоями — это порядок строк.

Давай посмотрим на это вживую. Нарисуем два круга, которые пересекаются.

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  background(240);

  fill(255, 120, 60);   // оранжевый
  circle(170, 200, 180); // рисуется первым → ниже

  fill(60, 180, 255);   // голубой
  circle(230, 200, 180); // рисуется вторым → выше
}

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

А теперь сделай мысленный эксперимент: поменяй два блока fill/circle местами. Голубой круг нарисуется первым, оранжевый — вторым, и в зоне пересечения сверху окажется уже оранжевый. Картинка изменится, хотя ты не тронул ни координаты, ни цвета — только порядок. Это и есть главная суперсила сегодняшнего урока: переставляя строки, ты меняешь, кто кого закрывает.

Как читать сцену по слоям

Удобно держать в голове такую табличку — она показывает, в каком порядке стоит выкладывать типичную сцену:

ОчередьЧто рисуемПочему здесь
1background() — небонижний слой, под всем
2солнце, облакадальний план, за героем
3тело цыплёнкаглавный объект, поверх фона
4клюв, глаздетали героя, поверх тела

Логика простая: чем дальше объект «от зрителя» и чем крупнее он как фон — тем раньше его рисуем. Чем мельче деталь и чем она «ближе» — тем позже. Глаз цыплёнка должен лежать поверх головы, а не наоборот, иначе голова его закрасит.

Есть удобный приём — проговаривать сцену вслух, как режиссёр, который выставляет декорации: «Сначала задник — небо. Потом дальние декорации — солнце и облака. Потом на сцену выходит актёр — цыплёнок. И в самом конце гримёр дорисовывает ему лицо — клюв и глаз». Если ты можешь рассказать сцену в таком порядке словами, ты уже знаешь, в каком порядке писать строки кода. Этот навык — раскладывать картинку на слои в голове — пригодится тебе во всём курсе: и когда мы будем рисовать стаю цыплят циклом, и когда запустим над героем фейерверк из частиц. Везде один и тот же вопрос: что должно лежать ниже, а что — выше?

Собираем небо с цыплёнком

Пришло время большого примера. Соберём всю сцену по слоям — и ты увидишь, как порядок строк буквально строит картинку этаж за этажом.

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  // Слой 1: небо
  background(135, 206, 235);

  // Слой 2: солнце в углу
  fill(255, 220, 90);
  circle(60, 60, 90);

  // Слой 2: облако (два кружка рядом)
  fill(255);
  circle(300, 90, 70);
  circle(340, 95, 60);

  // Слой 3: тело CodeChick
  fill(255, 209, 64);   // тёплый жёлтый
  circle(200, 250, 140);

  // Слой 4: клюв
  fill(255, 140, 30);   // оранжевый
  triangle(200, 240, 250, 255, 200, 275);

  // Слой 4: глаз
  fill(40);
  circle(180, 225, 18);
}

Результат: голубое небо, в левом верхнем углу мягкое жёлтое солнце, справа сверху белое облако из двух кружков. В центре кадра — крупный жёлтый круг-тело цыплёнка CodeChick. Поверх него справа торчит оранжевый треугольник-клюв, а слева — маленький тёмный глаз. Всё лежит ровно теми слоями, что мы задумали: герой поверх неба, детали поверх тела.

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

Давай пройдёмся по коду строка за строкой, как камера, которая снимает сборку декораций. Сначала background(135, 206, 235) заливает весь холст голубым — есть небо. Дальше fill(255, 220, 90) задаёт тёплый жёлтый, и circle(60, 60, 90) ставит солнце в левый верхний угол: координаты 60, 60 — это центр круга, а 90 — его диаметр. Потом мы переключаем заливку на белый fill(255) и двумя перекрывающимися кружками лепим облако: один покрупнее, другой поменьше и чуть правее — так облако выглядит пухлым, а не как идеальный шар.

И только теперь на сцену выходит сам герой. fill(255, 209, 64) — это тёплый жёлтый цвет CodeChick, который мы закрепили за ним ещё в модуле про цвет. Круг circle(200, 250, 140) ставит его тело по центру и чуть ниже середины холста. Заметь: тело нарисовано после неба, солнца и облаков, поэтому оно перекрывает всё, что попадает под него. Завершаем лицом — оранжевым клювом-треугольником и тёмным глазом. Поскольку они идут последними, они аккуратно лежат поверх жёлтого тела. Шесть смысловых шагов, и из пустого холста собралась целая сцена. Меняя порядок этих шагов, ты каждый раз получаешь немного другую картинку — это и есть твой главный рычаг управления.

Полупрозрачный слой поверх сцены

Помнишь прозрачность (alpha) из прошлого урока? Слои и alpha — отличная команда. Можно положить поверх всей сцены полупрозрачный прямоугольник и получить эффект «вечернего света» или дымки, не перерисовывая ничего под ним.

function draw() {
  background(135, 206, 235);

  fill(255, 209, 64);
  circle(200, 250, 140);   // тело цыплёнка

  // Полупрозрачная оранжевая вуаль поверх всего
  fill(255, 140, 30, 70);  // четвёртое число — alpha
  rect(0, 0, 400, 400);
}

Результат: та же сцена с цыплёнком, но будто залита тёплым закатным светом — поверх неба и тела лежит лёгкая оранжевая плёнка, сквозь которую видно всё, что под ней. Цвета стали теплее и приглушённее.

Это работает именно потому, что прямоугольник нарисован последним и поэтому лежит сверху. А раз у него есть alpha, нижние слои просвечивают сквозь него. Поменяй alpha с 70 на 200 — и вуаль станет плотной, почти закрасив сцену. Поставь её первой строкой после фона — и эффект пропадёт, ведь тогда цыплёнок ляжет поверх вуали.

Частые ошибки и подводные камни

  • background() стоит в конце draw(). Самая болезненная ошибка новичка. Если залить фон после фигур, он закрасит их все — и ты увидишь чистый цвет без единой детали. Кажется, что «код не работает», а на самом деле фон просто лёг сверху. Правило: background() — почти всегда первая строка в draw().

  • Деталь нарисована раньше крупной фигуры. Нарисовал глаз, а потом тело — и тело закрасило глаз. Детали (клюв, глаз, блик) всегда идут после того объекта, на котором они лежат.

  • Ждёшь z-index, как в CSS. В p5.js нет свойства «поднять наверх». Единственный рычаг — порядок вызовов в коде. Хочешь поднять фигуру выше — физически перемести её строки ниже.

  • Забыл про alpha и удивляешься, почему ничего не просвечивает. Обычный fill(255, 140, 30) — это плотный цвет, он полностью закрывает то, что под ним. Чтобы нижние слои были видны сквозь верхний, добавь четвёртое число — alpha: fill(255, 140, 30, 70).

  • Несколько background() в одном draw(). Если случайно вызвать background() дважды, второй вызов сотрёт всё, что ты нарисовал между ними. Фон должен быть один и в начале.

Мини-практика: достраиваем мир CodeChick

Возьми пример со сценой «небо + цыплёнок» за основу и доработай его сам. Вот план:

  1. Добавь второе облако в левой части неба. Помести его так, чтобы оно лежало за солнцем (нарисуй облако до солнца) — и посмотри, как солнце перекроет край облака.
  2. Нарисуй цыплёнку щёчку-румянец: маленький полупрозрачный розовый круг (fill(255, 150, 150, 120)) поверх тела. Убедись, что он стоит после тела, иначе исчезнет.
  3. Положи поверх всей сцены полупрозрачную вуаль своего цвета и подбери alpha так, чтобы получилось настроение: голубая дымка — туман, оранжевая — закат, тёмно-синяя — сумерки.

Меняй порядок строк и смотри, как перестраивается картинка. Это лучший способ почувствовать правило слоёв руками. Поиграй с тем, что будет, если поднять тело цыплёнка выше деталей — и верни обратно.

Хочешь челлендж посложнее? Добавь цыплёнку отражение в луже внизу холста: нарисуй второе, более бледное и слегка приплюснутое тело под основным. Подумай сам, на каком слое оно должно лежать — выше неба, но это отдельный объект, а не деталь героя. А ещё попробуй пустить поверх сцены две вуали разом: сначала тёплую снизу, потом холодную сверху, и посмотри, как они смешиваются. Тут нет единственно верного ответа — есть твой вкус и понимание того, кто за кем ложится. Именно так художники-генеративщики и находят свой стиль: крутят порядок и прозрачность, пока кадр не «зазвучит».

Итоги

Сегодня ты освоил то, без чего не собрать ни одной сложной сцены:

  • background() заливает весь холст и стирает кадр — поэтому он почти всегда идёт первой строкой в draw().
  • Фигуры рисуются по порядку строк: что позже — то выше. Это и есть управление наслоением.
  • Сцену удобно выкладывать слоями: фон → дальний план → герой → детали героя.
  • Полупрозрачный слой (alpha) поверх сцены даёт эффекты света и дымки, не трогая то, что под ним.

Теперь CodeChick уверенно сидит поверх неба — и это уже не просто кружок, а настоящий персонаж со своим миром. Дальше будет ещё интереснее: в следующем уроке мы перестанем задавать цвета вручную и научимся перебирать оттенки плавно через модель HSB — ту самую, что даёт радужные переходы. CodeChick сможет менять цвет неба от рассвета к закату одним числом. Увидимся на следующей странице!

Проверьте себя
1. В каком порядке p5.js рисует фигуры на холсте?
AПо размеру: сначала крупные, потом мелкие
BСверху вниз по строкам кода: что вызвано позже — лежит выше
CВ случайном порядке каждый кадр
DПо свойству z-index, как в CSS
2. Почему background() обычно ставят первой строкой в draw()?
AИначе p5.js выдаст ошибку
BЭто ускоряет работу скетча
Cbackground() заливает весь холст и стирает то, что нарисовано до него, поэтому он должен лечь под всё остальное
DБез этого холст не создастся
3. Ты нарисовал глаз цыплёнка, а потом — его тело тем же кодом. Что увидишь на холсте?
AГлаз поверх тела — всё в порядке
BТело закрасит глаз, потому что нарисовано позже
CОба исчезнут
Dp5.js сам поднимет глаз наверх
4. Как сделать так, чтобы верхний слой просвечивал и было видно фигуры под ним?
AИспользовать noStroke()
BВызвать background() ещё раз
CДобавить в fill() четвёртое число — alpha (прозрачность)
DУменьшить размер верхней фигуры
5. В p5.js есть ли свойство вроде z-index, чтобы поднять фигуру наверх, не трогая её положение в коде?
AДа, функция zIndex()
BНет: единственный способ управлять слоями — порядок вызовов в коде
CДа, через свойство layer у фигуры
DДа, p5.js поднимает крупные фигуры автоматически
6. Что произойдёт, если случайно вызвать background() дважды посреди draw()?
AНичего, второй вызов игнорируется
BФон станет в два раза ярче
CВторой вызов сотрёт всё, что было нарисовано между первым и вторым background()
DСкетч перестанет обновляться