Паттерны и орнаменты
Сетку из одинаковых фигур ты уже строить умеешь — сегодня мы научим её играть: чередовать цвета, менять формы и собираться в настоящий орнамент из цыплят.
Главное правило урока: у цикла всегда есть счётчик-индекс (
i,x,y), и если внутри цикла менять цвет или форму в зависимости от этого индекса, повторение перестаёт быть скучным и превращается в узор.
Зачем тебе паттерны
Посмотри на свой телефон. Чехол в горошек, плитка в ванной, шахматная доска в игре, фон чата в мессенджере, узор на свитере, обёртка подарка — всё это паттерны: один и тот же мотив, повторённый по правилу. Глазу это нравится, потому что он сразу видит ритм и порядок, как в припеве любимого трека: одна и та же фраза возвращается, но цепляет.
В прошлом уроке про вложенные циклы и сетки ты научился раскладывать фигуры рядами и столбцами: два цикла, один внутри другого, заполняют весь холст одинаковыми кружками. Получалась ровная сетка — аккуратно, но однообразно, как тетрадь в клетку. Сегодня мы вдохнём в эту сетку жизнь: пусть клетки чередуются цветами, как на шахматной доске, пусть в одних ячейках сидит круг, а в других — квадрат, и пусть весь холст в итоге покроется обоями из маленьких CodeChick.
К концу урока ты соберёшь вот такой кадр: ровные ряды цыплят на нежном фоне, где жёлтые птенцы чередуются с оранжевыми, как клетки доски, а между ними рассыпаны зёрнышки. Издалека — обои для телефона, вблизи — стая аккуратных CodeChick. И всё это рисует цикл всего из нескольких строк: ты задаёшь правило, а компьютер сам повторяет его сотни раз.
Секрет, который мы сегодня раскроем, до смешного простой. Внутри цикла у тебя всегда есть число — номер текущего шага. Если ты научишься читать это число и принимать по нему решения («каждый второй — оранжевый», «каждый третий — квадрат»), ты получишь ключ почти ко всем узорам на свете.
Главная идея: решай по индексу
Что такое индекс цикла
Вспомни обычный цикл из прошлых уроков:
for (let i = 0; i < 5; i++) {
circle(i * 60 + 40, 200, 40);
}Результат: пять одинаковых кружков в ряд по горизонтали на одной высоте, на равном расстоянии друг от друга.
Здесь i — это индекс, то есть номер текущего повторения: на первом круге i равен 0, на втором 1, дальше 2, 3, 4. Раньше мы пользовались индексом только чтобы сдвигать фигуру (i * 60 — каждый следующий круг правее). Но индекс — это обычное число, а значит, по нему можно ещё и принимать решения: какой цвет дать фигуре, какую форму нарисовать, какого она будет размера.
Представь учительницу, которая на физкультуре командует: «первый-второй рассчитайсь!» — и каждый запоминает свой номер, а потом «первые номера — шаг вперёд». Так и здесь: цикл присваивает каждой фигуре номер, а ты внутри говоришь «фигуры с чётным номером — жёлтые, с нечётным — оранжевые». Узор рождается не из разных команд, а из одной команды, которая по-разному ведёт себя в зависимости от номера.
Чётный или нечётный: остаток от деления
Как в коде отличить чётный номер от нечётного? Тут на сцену выходит главный герой урока — оператор остатка от деления, он же % (читается «по модулю» или просто «процент»). Он делит одно число на другое и возвращает остаток.
4 % 2равно 0 — четыре делится на два без остатка;5 % 2равно 1 — пятёрку на двойку нацело не поделить, остаётся 1;6 % 2равно 0,7 % 2равно 1 — и так далее.
Замечаешь ритм? Для чётных чисел i % 2 всегда 0, для нечётных всегда 1. Получается выключатель, который сам щёлкает туда-сюда на каждом шаге цикла: 0, 1, 0, 1, 0, 1… Именно это нам и нужно для чередования.
А ещё остаток умеет считать «по кругу». Посмотри на i % 3 при i = 0, 1, 2, 3, 4, 5: получится 0, 1, 2, 0, 1, 2 — последовательность зацикливается каждые три шага. Это как стрелки часов: дошли до 12 — и снова к единице. С помощью % 3 можно делать узор из трёх повторяющихся мотивов, с % 4 — из четырёх. Запомни этот образ: остаток превращает бесконечно растущий счётчик в короткий повторяющийся ритм.
Разбираем на примерах
Пример 1: полоски через одну
Начнём с самого наглядного — раскрасим ряд кругов через один, как зебру.
function setup() {
createCanvas(400, 200);
noStroke();
}
function draw() {
background(245);
for (let i = 0; i < 6; i++) {
if (i % 2 === 0) {
fill(255, 209, 64); // чётный — жёлтый
} else {
fill(255, 140, 30); // нечётный — оранжевый
}
circle(i * 60 + 50, 100, 50);
}
}Результат: на светлом фоне ряд из шести кругов, которые чередуются по цвету: жёлтый, оранжевый, жёлтый, оранжевый, жёлтый, оранжевый. Ровный ритм, как на леденце или дорожной разметке.
Разберём по шагам. Цикл крутится шесть раз, и на каждом шаге сначала проверяет if (i % 2 === 0) — «остаток от деления номера на 2 равен нулю?». Если да (номер чётный) — ставим жёлтую заливку, иначе — оранжевую. И только потом рисуем круг. Важен порядок: сначала выбрали цвет через fill(), затем нарисовали фигуру — ведь fill() действует на всё, что рисуется после него. Поменяй % 2 на % 3 и добавь третий цвет — получишь узор из трёх повторяющихся оттенков.
Пример 2: шахматная доска на вложенных циклах
Один ряд — это разминка. Настоящий паттерн живёт на сетке, а сетку, как ты помнишь, строят два вложенных цикла. Сделаем шахматную доску.
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(245);
let size = 50; // размер клетки
for (let y = 0; y < 8; y++) {
for (let x = 0; x < 8; x++) {
if ((x + y) % 2 === 0) {
fill(255, 209, 64);
} else {
fill(120, 200, 120);
}
rect(x * size, y * size, size, size);
}
}
}Результат: весь холст превратился в шахматную доску 8 на 8: жёлтые и зелёные квадраты чередуются в обе стороны — и по горизонтали, и по вертикали, как настоящая клетчатая скатерть.
Тут спрятан красивый трюк, и его легко проглядеть. Мы проверяем не x % 2 и не y % 2 по отдельности, а сумму: (x + y) % 2. Почему? Если сдвинуться на одну клетку вправо, x вырастет на 1, и сумма x + y сменит чётность — цвет переключится. Сдвинулись на клетку вниз — y вырос на 1, сумма снова сменила чётность, цвет опять переключился. Так получается, что соседи всегда разного цвета и по горизонтали, и по вертикали — ровно как на шахматной доске. Если бы мы взяли только x % 2, вышли бы вертикальные полоски; только y % 2 — горизонтальные. Сумма даёт именно клетку. Это стоит запомнить как готовый рецепт.
Пример 3: чередуем не цвет, а форму
По индексу можно менять не только цвет, но и саму фигуру. Давай в чётных ячейках рисовать круг, а в нечётных — квадрат.
function setup() {
createCanvas(400, 400);
noStroke();
rectMode(CENTER); // квадрат рисуем от центра, как круг
}
function draw() {
background(245);
fill(255, 170, 60);
let step = 80;
for (let y = 0; y < 5; y++) {
for (let x = 0; x < 5; x++) {
let px = x * step + 40;
let py = y * step + 40;
if ((x + y) % 2 === 0) {
circle(px, py, 50);
} else {
rect(px, py, 50, 50);
}
}
}
}Результат: оранжевая сетка 5 на 5, где круги и квадраты чередуются в шахматном порядке: где сосед-круг, там по диагонали тоже круг, а между ними квадраты. Получается ритмичный орнамент из двух форм одного цвета.
Логика та же, что и с цветом, только в ветках if/else теперь стоят разные фигуры, а не разные fill(). Обрати внимание на две детали. Во-первых, я завёл px и py — позицию ячейки, посчитанную один раз, чтобы и круг, и квадрат рисовались в одной и той же точке. Во-вторых, в setup() появилась строка rectMode(CENTER): по умолчанию rect() рисуется от левого верхнего угла, а circle() — от центра, и без этой настройки квадраты съехали бы относительно кругов. Маленькая, но важная согласованность.
Пример 4: обои из цыплят
Собираем всё вместе — настоящий фон-паттерн из CodeChick. Вынесем рисование одного цыплёнка в отдельную функцию, чтобы цикл оставался чистым.
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(200, 230, 255); // нежно-голубое небо
let step = 90;
for (let y = 0; y < 5; y++) {
for (let x = 0; x < 5; x++) {
let px = x * step + 45;
let py = y * step + 45;
// чётные цыплята жёлтые, нечётные — оранжевые
if ((x + y) % 2 === 0) {
drawChick(px, py, color(255, 209, 64));
} else {
drawChick(px, py, color(250, 160, 60));
}
}
}
}
function drawChick(x, y, bodyColor) {
// тело
fill(bodyColor);
circle(x, y, 50);
// клюв
fill(255, 140, 30);
triangle(x + 18, y - 4, x + 38, y, x + 18, y + 6);
// глаз
fill(40);
circle(x - 8, y - 8, 7);
}Результат: весь холст затянут обоями из маленьких CodeChick на голубом небе: ряды аккуратных птенцов, где жёлтые и оранжевые чередуются в шахматном порядке. У каждого свой оранжевый клюв справа и тёмный глаз — целая дружная стая, готовая стать фоном для телефона.
Что здесь нового. Мы спрятали все фигуры одного цыплёнка в функцию drawChick(x, y, bodyColor) — она принимает координаты и цвет тела, а рисует уже знакомого нам героя относительно точки (x, y), как в уроках про мышь. Внутри цикла мы только решаем где и каким цветом, а сам цыплёнок собирается в одном месте. Это огромное удобство: захочешь добавить цыплёнку лапки — поправишь функцию один раз, и обновятся все птенцы сразу. Цвет передаём через color(...) — этот объект хранит готовый цвет, который потом уходит в fill(). Попробуй заменить условие на (x + y) % 3 === 0 — получишь узор, где особый цыплёнок встречается реже, через двух обычных.
Частые ошибки и подводные камни
Ставишь
fill()после фигуры. Если написать сначалаcircle(...), а потомfill(...), цвет применится к следующей фигуре, а не к текущей — и узор «съедет» на один шаг. Правило железное: сначала выбираем цвет, потом рисуем.Путаешь
%и/. Деление/возвращает частное (5 / 2 это 2.5), а остаток%— то, что осталось (5 % 2 это 1). Для чередования нужен именно остаток. Если узор не чередуется, первым делом проверь, что стоит%, а не/.Сравниваешь через одно
=вместо===. В условииif (i % 2 = 0)один знак равно — это присваивание, и будет ошибка. Сравнение пишется двумя или тремя знаками:i % 2 === 0. Один=«кладёт» значение, два-три — «спрашивают, равны ли».Проверяешь
x % 2вместо(x + y) % 2на сетке. Тогда вместо шахматной доски получишь вертикальные полоски: цвет меняется только по горизонтали, а сверху вниз остаётся одинаковым. Чтобы клетки чередовались в обе стороны, бери сумму индексов.Забыл про
rectMode, когда мешаешь круги и квадраты.circle()рисуется от центра, аrect()по умолчанию — от угла. БезrectMode(CENTER)квадраты окажутся сдвинуты относительно кругов, и ровный орнамент рассыплется. Либо ставьrectMode(CENTER), либо сам учитывай сдвиг при расчёте координат.
Мини-практика: собери свой орнамент
Возьми за основу пример с обоями из цыплят и поэкспериментируй. Вот план:
- Поменяй условие чередования с
% 2на% 3и добавь третий цвет цыплёнка черезelse if. Посмотри, как меняется ритм узора, когда мотивов становится три, а не два. - Между цыплятами в «пустых» местах нарисуй маленькое зёрнышко (крошечный овал) — например, в ячейках, где
(x + y) % 2 === 1, вместо оранжевого цыплёнка клади зерно. Получится узор «цыплёнок — зёрнышко — цыплёнок». - Сделай так, чтобы размер цыплёнка зависел от индекса: попробуй диаметр
40 + (x % 3) * 15. Узор станет «дышать» — птенцы будут расти и уменьшаться по рядам. - Для смелых: добавь к каждому цыплёнку лёгкий поворот через
push()/translate()/rotate()/pop(), где угол зависит от(x + y). Орнамент оживёт и станет похож на узор на ткани.
Меняй числа и условия по одному и сразу смотри, что вышло. Именно так, на ощупь, лучше всего чувствуется, как из простого правила рождается сложный на вид узор.
Итоги
Сегодня твоя ровная сетка научилась играть и превратилась в настоящий орнамент. Вот что ты теперь умеешь:
- Индекс цикла (
i,x,y) — это число, по которому можно принимать решения: какой цвет, какую форму, какой размер дать фигуре. - Оператор остатка
%превращает растущий счётчик в короткий повторяющийся ритм:% 2чередует через одного,% 3— через двух. - На сетке шахматное чередование даёт сумма индексов:
(x + y) % 2переключает цвет и по горизонтали, и по вертикали. - По условию можно менять не только заливку, но и саму фигуру — круг или квадрат.
- Сложный мотив (целого цыплёнка) удобно вынести в отдельную функцию и вызывать её из цикла, передавая позицию и цвет.
Дальше будет ещё интереснее: до сих пор все наши узоры были строго регулярными, как клетка в тетради. В следующем уроке мы добавим в них случайность — научимся раскидывать перья и зёрна так, чтобы каждый раз получался новый, живой и неповторимый узор. Идеальный порядок встретится с лёгким хаосом. Увидимся на следующей странице!