Рисуем первые формы

Любая картинка на экране — от мема до заставки твоей любимой игры — это набор простых форм, которые кто-то расставил в нужных местах. Сегодня ты научишься делать то же самое кодом.

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

Зачем вообще рисовать формы по отдельности

Представь, что ты собираешь персонажа в редакторе вроде того, что есть в Roblox или The Sims. Ты не лепишь его одним движением — ты ставишь голову, потом туловище, потом глаза по одному. В p5.js всё то же самое, только вместо мышки у тебя команды. Ты говоришь холсту: «нарисуй круг вот здесь», «проведи линию отсюда сюда» — и из этих кусочков постепенно вырастает картинка.

В прошлом уроке про холст и координаты ты разобрался, как устроена система координат: начало (0, 0) сидит в левом верхнем углу, x растёт вправо, а y — вниз. Это как клетки в тетради, только считать строки начинаем сверху. Сегодня мы наконец используем эти клетки по назначению: будем ставить в них формы.

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

Почему именно эти четыре формы, а не что-то посложнее? Потому что они — алфавит. Как из 33 букв складываются все книги на свете, так из точки, линии, прямоугольника и круга складывается любая графика: иконки в телефоне, спрайты в пиксельных играх, логотипы брендов на футболках. Даже плавные кривые внутри устроены как множество крошечных прямых отрезков, поставленных встык, — глаз просто не замечает стыков. Освоишь алфавит — дальше уже дело техники и фантазии.

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

Четыре кирпичика: point, line, rect, ellipse

Точка — point()

Самая простая форма — это точка. Один пиксель на холсте. Чтобы её поставить, нужен всего один адрес — пара координат (x, y):

function setup() {
  createCanvas(400, 400);
  background(240);
  strokeWeight(8);
  point(200, 200);
}

Результат: на светло-сером холсте ровно по центру — одна жирная чёрная точка. Без strokeWeight(8) точка была бы размером в один пиксель, и ты бы её еле разглядел; восьмёрка делает её толстой и заметной.

Запомни: point(x, y) принимает два числа — это координаты единственной точки. Никакого размера у точки нет, кроме толщины обводки.

Кажется, что одна точка — это бесполезно. Зачем вообще такая команда? А представь дождь из тысячи капель, звёздное небо или след кисти, который рассыпается на пиксели. Всё это — точки, расставленные в нужных местах. Чуть позже, когда мы дойдём до циклов и случайности, ты будешь сыпать сотнями точек одной строкой кода и раскидывать пёрышки нашего цыплёнка по всему холсту. А пока запомни главное: точка — это просто адрес на холсте, отмеченный видимой меткой.

Линия — line()

Чтобы провести линию, одной точки мало — нужны две: откуда и куда. Значит, и чисел будет четыре:

function setup() {
  createCanvas(400, 400);
  background(240);
  strokeWeight(4);
  line(50, 50, 350, 350);
}

Результат: по холсту наискосок — от левого верхнего угла к правому нижнему — протянута толстая диагональная линия. Первая пара (50, 50) — это начало, вторая пара (350, 350) — конец.

Думай об этом как о том, как ты соединяешь две точки в тетради линейкой: ставишь карандаш в первую точку и ведёшь ко второй. line(x1, y1, x2, y2) — ровно это и делает.

Маленький фокус для понимания: поменяй местами концы — напиши line(350, 350, 50, 50) вместо прежнего. Картинка не изменится ни на пиксель! Линии всё равно, в каком порядке ты назвал её концы, ведь отрезок между двумя точками один и тот же. А вот если сдвинуть только один конец, например line(50, 50, 350, 50), линия станет горизонтальной: у обоих концов одинаковый y = 50, значит, высота не меняется, и отрезок ложится ровно поперёк. Это удобный способ рисовать прямые горизонтали и вертикали — следи, чтобы у концов совпадала одна из координат.

Прямоугольник — rect()

С прямоугольником интереснее. Ему нужны четыре числа, но смысл у них другой: где находится угол и какого размера сама фигура.

function setup() {
  createCanvas(400, 400);
  background(240);
  rect(100, 80, 200, 120);
}

Результат: в верхней половине холста — прямоугольник с белой заливкой и чёрным контуром. Его левый верхний угол стоит в точке (100, 80), ширина — 200 пикселей, высота — 120.

Вот это важно не перепутать: rect(x, y, w, h) — это не «от угла до угла», как было у линии. Первые два числа — координаты верхнего левого угла, а третье и четвёртое — это ширина и высота. Если хочешь квадрат — просто сделай ширину и высоту одинаковыми: rect(100, 100, 80, 80).

Эллипс — ellipse()

И, наконец, эллипс — то, чем мы рисуем круги, овалы и тело цыплёнка. Тут есть приятный сюрприз: эллипс рисуется от центра.

function setup() {
  createCanvas(400, 400);
  background(240);
  ellipse(200, 200, 160, 160);
}

Результат: ровно по центру холста — большой круг диаметром 160 пикселей с белой заливкой и тонким контуром. Центр круга стоит в точке (200, 200).

ellipse(x, y, w, h): первые два числа — координаты центра, а w и h — ширина и высота (то есть диаметры по горизонтали и вертикали). Если они равны — получается круг. Если разные, например ellipse(200, 200, 200, 100), выйдет вытянутый овал — приплюснутый, как яйцо, лежащее на боку. Кстати, circle(x, y, d) — это сокращённая запись для ровного круга: центр и один диаметр.

То, что эллипс рисуется от центра, — это огромное удобство, и сейчас поймёшь почему. Когда ты хочешь поставить что-то «вот сюда, по центру», ты обычно думаешь именно о центре предмета, а не о его угле. Глаз цыплёнка, солнце на небе, мячик в игре — у всего этого есть естественная серединка. С эллипсом ты задаёшь её напрямую: «центр здесь, диаметр такой» — и форма сама раскидывается симметрично во все стороны. Поэтому круги и овалы в p5.js ставить интуитивно проще, чем прямоугольники, и большую часть нашего цыплёнка мы соберём именно из эллипсов.

Маленькая, но важная разница: где «якорь» формы

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

ФормаКомандаЧто значат числа
Точкаpoint(x, y)координаты самой точки
Линияline(x1, y1, x2, y2)начало и конец
Прямоугольникrect(x, y, w, h)верхний левый угол, ширина, высота
Эллипсellipse(x, y, w, h)центр, ширина, высота

Видишь подвох? У прямоугольника (x, y) — это угол, а у эллипса то же самое (x, y) — это центр. Поэтому если поставить рядом rect(200, 200, 100, 100) и ellipse(200, 200, 100, 100), они окажутся в разных местах: квадрат уйдёт вправо-вниз от точки 200/200, а круг будет ровно вокруг неё. Это не баг, это просто два разных способа «привязки», и их полезно держать в голове.

Хорошая новость: поведение прямоугольника можно переключить командой rectMode(CENTER) — тогда rect тоже начнёт рисоваться от центра, как эллипс. Но пока мы оставим всё по умолчанию, чтобы не запутаться.

Собираем фигуру: первый силуэт CodeChick

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

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

function setup() {
  createCanvas(400, 400);
  background(200, 230, 255);

  // тело — большой круг внизу
  ellipse(200, 250, 180, 180);

  // голова — круг поменьше сверху
  ellipse(200, 150, 120, 120);

  // глаз — маленькая жирная точка
  strokeWeight(10);
  point(230, 135);

  // клюв — пока простой прямоугольник
  strokeWeight(1);
  rect(250, 145, 40, 18);
}

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

Разберём по шагам, что здесь происходит:

  1. background(200, 230, 255) заливает весь холст светло-голубым — это небо, мир нашего героя. Заодно стирает всё, что было.
  2. Первый ellipse рисует тело: центр в точке (200, 250) (нижняя половина холста), диаметр 180.
  3. Второй ellipse — голова: центр выше, в (200, 150), и он меньше, 120 пикселей. Поскольку он нарисован позже, он ложится поверх тела и немного перекрывает его — получается единый силуэт.
  4. strokeWeight(10) делает точку толстой, и point(230, 135) ставит глаз чуть правее и выше центра головы.
  5. Мы возвращаем strokeWeight(1), чтобы контур клюва был тонким, и рисуем rect сбоку от головы.

Попробуй сам поменять числа и посмотри, что будет: сдвинь глаз в point(170, 135) — он переедет на другую сторону. Увеличь клюв до rect(250, 145, 60, 25) — цыплёнок станет носатее. Это и есть творческое программирование: меняешь число — меняется картинка.

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

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

На этих граблях прыгают почти все новички. Лучше узнать о них заранее.

1. Путать «угол» и «центр» у rect и ellipse

Ты задаёшь rect(200, 200, ...), ждёшь фигуру по центру, а она уезжает вправо-вниз. Помни: у прямоугольника (x, y) — это верхний левый угол, а у эллипса — центр. Если хочешь рисовать прямоугольник от центра, включи rectMode(CENTER).

2. Путать «размер» и «вторую точку»

У линии четыре числа — это две точки (начало и конец). У прямоугольника четыре числа — это угол плюс размер. Если написать rect(50, 50, 350, 350), думая «от угла до угла», получишь огромный прямоугольник шириной и высотой 350 пикселей, который вылезет за холст. Команды выглядят похоже, а смысл чисел разный.

3. Точка размером в один пиксель

Поставил point(200, 200) и не видишь ничего? Точка по умолчанию — это один-единственный пиксель, его легко пропустить. Добавь strokeWeight(8) перед ней, чтобы сделать заметной.

4. Рисовать в неправильном порядке

Если нарисовать глаз до головы, голова закрасит его сверху, и глаз пропадёт. p5.js кладёт формы слоями в том порядке, в каком ты их пишешь. Сначала — то, что снизу (тело, голова), потом — то, что сверху (глаз, клюв).

5. Забыть, что y растёт вниз

Хочешь поднять голову выше — и увеличиваешь y? Голова уедет вниз. В системе координат холста ось y направлена вниз: чем больше число, тем ниже. Чтобы поднять форму, y нужно уменьшать.

Мини-практика: доделай цыплёнка сам

Теперь твоя очередь. Возьми код силуэта CodeChick и доведи его до ума. Вот задания по нарастающей:

  • Лапки. Добавь цыплёнку две лапки внизу с помощью line(). Подсказка: проведи короткие линии от низа тела (примерно y = 335) ещё ниже. Например, line(180, 335, 180, 370) — и вторую такую же чуть правее.
  • Второй глаз. Сейчас глаз один. Добавь симметричный второй слева от центра головы. Помни про strokeWeight до и после.
  • Земля. Нарисуй под цыплёнком длинный тонкий rect через весь низ холста — пусть стоит на земле, а не висит в небе.
  • Эксперимент со взглядом. Сделай глаз больше или меньше, меняя strokeWeight. Найди размер, при котором цыплёнок выглядит самым милым, — это субъективно, и в этом весь кайф.

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

Когда справишься с заданиями, попробуй задачу со звёздочкой: нарисуй цыплёнку гнездо. Это просто широкий низкий эллипс под телом — например, ellipse(200, 320, 220, 70), поставленный до тела, чтобы цыплёнок сидел в нём, а не за ним. Помнишь про порядок слоёв? Гнездо рисуем первым, тело — поверх. Получится, будто наш герой устроился в своём домике. И вот тут, кстати, особенно ясно видно, зачем эллипсу центр: ты ставишь точку (200, 320) прямо под цыплёнком и просто расширяешь её вширь — гнездо само раскидывается симметрично влево и вправо.

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

Итоги

Сегодня ты освоил четыре кирпичика, из которых строится любая картинка на холсте:

  • point(x, y) — точка по координатам;
  • line(x1, y1, x2, y2) — линия от точки к точке;
  • rect(x, y, w, h) — прямоугольник от верхнего левого угла;
  • ellipse(x, y, w, h) — эллипс от центра (а circle(x, y, d) — ровный круг).

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

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

Проверьте себя
1. Что означают четыре числа в вызове rect(100, 80, 200, 120)?
AКоординаты двух противоположных углов прямоугольника
BКоординаты верхнего левого угла, затем ширина и высота
CКоординаты центра, затем ширина и высота
DШирину, высоту и две координаты центра
2. Чем отличается «якорь» (точка привязки) у rect и ellipse по умолчанию?
AУ обоих это центр фигуры
BУ обоих это верхний левый угол
CУ rect это верхний левый угол, а у ellipse — центр
DУ rect это центр, а у ellipse — верхний левый угол
3. Почему точка point(200, 200) может оказаться невидимой на холсте?
AТочку нельзя нарисовать без заливки fill()
BПо умолчанию точка размером в один пиксель, её легко не заметить
Cpoint() работает только внутри draw(), а не setup()
DКоордината 200 выходит за пределы холста
4. Ты рисуешь глаз цыплёнка раньше, чем голову. Что произойдёт?
AГлаз окажется поверх головы, как и задумано
Bp5.js выдаст ошибку из-за неправильного порядка
CГолова, нарисованная позже, закрасит глаз сверху, и он пропадёт
DГлаз и голова сольются в один цвет
5. Сколько чисел принимает line() и что они задают?
AДва числа: координаты одной точки линии
BТри числа: начало и длину линии
CЧетыре числа: координаты начала и конца линии
DЧетыре числа: угол, ширину и высоту
6. Как поднять голову цыплёнка выше на холсте?
AУвеличить координату y, потому что y растёт вверх
BУменьшить координату y, потому что ось y направлена вниз
CУвеличить координату x
DПоменять местами w и h