Тригонометрия для арта: sin и cos
Две функции — sin и cos — превращают скучный счётчик кадров в плавную волну и в движение по кругу. Сегодня они заставят CodeChick покачиваться, будто он плывёт на воде.
sin и cos — это не страшная формула из учебника, а два колёсика, которые берут угол и выдают число, плавно гуляющее от-1до1и обратно. Если кормить их растущим углом каждый кадр, получится волна — а из волны рождается любое плавное движение в анимации.
Зачем это вообще нужно
Открой любую игру или приложение и присмотрись к мелочам. Иконка уведомления, которая мягко дышит — становится чуть больше, чуть меньше. Кнопка «Играть», которая еле заметно покачивается, маня тебя нажать. Облака, которые лениво плывут по небу в платформере. Лоадер-спиннер, который крутится по кругу, пока грузится ролик. Всё это — не случайные дёрганья. За каждым таким плавным движением почти всегда стоят две функции: sin и cos.
В прошлом уроке про цикл for и ряды фигур ты выстраивал цыплят и зёрна в ровные ряды и сетки — это была статика, картинка стояла на месте. Сегодня мы добавим в эту картину жизнь: научимся брать угол и превращать его в плавное колебание. CodeChick перестанет стоять столбиком — он начнёт покачиваться вверх-вниз, будто сидит на волне, а потом и вовсе побежит по кругу.
К концу урока ты соберёшь скетч, где жёлтый цыплёнок мягко качается по синусоиде — поднимается и опускается так плавно, что глаз не находит ни одного рывка. И, что важнее, ты поймёшь, почему это работает, и сможешь крутить эту волну как захочешь: сделать качание быстрее, размашистее или пустить цыплёнка кружить по орбите. Давай разберёмся, что это за колёсики такие.
Метафора: колесо обозрения
Забудь на минуту про треугольники из школы. Представь колесо обозрения в парке — большое, медленно крутится. Ты сидишь в кабинке и едешь по кругу. У твоей кабинки в каждый момент есть две вещи: высота над землёй и сдвиг вбок от центра колеса.
Так вот: пока колесо равномерно крутится, твоя высота меняется по закону синуса, а твой сдвиг вбок — по закону косинуса. Внизу колеса высота минимальная, наверху — максимальная, а на полпути ты летишь мимо центра. Высота не скачет рывками: она плавно растёт, плавно тормозит у самого верха, плавно идёт вниз. Точно такую же плавную волну и рисует функция sin.
sin(угол)иcos(угол)берут угол поворота и возвращают число от-1до1.sin— это высота точки на колесе,cos— её сдвиг вбок. Чем больше угол, тем дальше кабинка проехала по кругу.
Главное, что нужно запомнить про эти числа: они никогда не вылезают за пределы от -1 до 1. Что бы ты ни подставил — хоть маленький угол, хоть огромный — на выходе всегда будет аккуратное число в этом диапазоне. Это как качели: как сильно их ни раскачивай по фазе, они не улетят в космос — есть крайняя левая точка и крайняя правая, между ними они и ходят.
Углы в p5.js считаются в радианах
Есть одна ловушка, о которой надо сказать сразу. sin и cos в p5.js по умолчанию ждут угол не в привычных градусах (0–360), а в радианах. Не пугайся слова: радианы — это просто другая линейка для углов. Полный круг — это не 360, а TWO_PI (примерно 6.28). Половина круга — PI (примерно 3.14). Четверть — HALF_PI.
На практике тебе почти не придётся считать радианы в уме. Ты будешь просто плавно увеличивать угол маленькими шажками каждый кадр — например, на 0.05, — и колесо само поедет. А PI и TWO_PI — это готовые константы p5.js, их не надо запоминать числом, достаточно писать имя.
Пример 1. Волна из чисел
Прежде чем что-то рисовать, давай просто посмотрим глазами, какие числа выдаёт sin, когда угол растёт. Это самый важный пример урока — пойми его, и всё остальное пойдёт легко.
let ugol = 0;
function setup() {
createCanvas(400, 200);
}
function draw() {
background(255, 245, 200);
let v = sin(ugol);
// v гуляет от -1 до 1
// превращаем -1..1 в высоту 0..200
let y = map(v, -1, 1, 180, 20);
fill(255, 140, 0);
noStroke();
ellipse(200, y, 30, 30);
ugol = ugol + 0.05;
}Результат: на тёплом жёлтом холсте оранжевый кружок-зёрнышко плавно ездит вверх-вниз ровно по центру. Он мягко поднимается почти к самому верху, чуть притормаживает там, так же плавно опускается вниз, замирает на мгновение и снова идёт вверх. Никаких рывков — движение гладкое, как дыхание. Это и есть синусоида, увиденная вживую.
Что здесь происходит по шагам
- Снаружи функций мы завели
ugol = 0— переменную состояния, которая хранит текущий угол поворота нашего «колеса» между кадрами. - Каждый кадр
sin(ugol)выдаёт число от-1до1и кладёт его вv. map(v, -1, 1, 180, 20)— переводчик диапазонов. Он берёт число из диапазона-1..1и растягивает его в координату по вертикали:-1станет180(низ),1станет20(верх). Без этого шага кружок дёргался бы в пределах одного пикселя, ведьsinсам по себе крошечный.- Рисуем кружок на этой высоте по центру холста (x всегда 200).
- В самом конце прибавляем к углу
0.05. Это и есть «колесо медленно поехало»: на следующем кадре угол чуть больше,sinвыдаст чуть другое число, кружок сместится. За много кадров складывается плавная волна.
Поиграй с числом 0.05. Поставь 0.2 — кружок заметается вверх-вниз быстро, как нервный. Поставь 0.01 — он будет ползать величаво и медленно. Это число — скорость вращения колеса.
Пример 2. CodeChick качается на волне
Теперь главная цель урока — заставим самого цыплёнка покачиваться по синусоиде, будто он сидит на воде и его несёт лёгкая волна.
let ugol = 0;
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(180, 220, 255);
// базовая высота 200, качаемся на 40 пикселей вверх-вниз
let y = 200 + sin(ugol) * 40;
// тело
fill(255, 215, 0);
ellipse(200, y, 150, 140);
// голова
ellipse(200, y - 80, 100, 90);
// клюв
fill(255, 140, 0);
triangle(245, y - 85, 245, y - 65, 285, y - 75);
// глаз
fill(0);
ellipse(215, y - 90, 14, 14);
ugol = ugol + 0.04;
}Результат: на светло-голубом небе жёлтый цыплёнок с оранжевым клювом мягко покачивается вверх и вниз вокруг центра холста. Он поднимается примерно на 40 пикселей выше середины, плавно тормозит, опускается на 40 пикселей ниже и снова всплывает. Голова, клюв и глаз двигаются вместе с телом единым целым — будто цыплёнок и правда сидит на невидимой волне и блаженно покачивается.
Сердце примера — строка с высотой
Вся магия в одной строке: let y = 200 + sin(ugol) * 40;. Разберём её по кусочкам, это очень частый рецепт.
200— это центр качания, точка, вокруг которой всё колышется. Середина холста по высоте.sin(ugol)— это волна, число от-1до1.* 40— это амплитуда, размах качания. Мы растягиваем крошечное-1..1до-40..40. Поставишь* 100— цыплёнок будет нырять размашисто, почти на весь экран; поставишь* 10— будет еле заметно подрагивать.
Формула центр + sin(угол) * размах — твой главный инструмент для любого плавного колебания: дыхание иконки, парение облака, мерцание звезды. Запомни её как таблицу умножения.
А обрати внимание на хитрость: координаты головы, клюва и глаза мы считаем относительно y — пишем y - 80, y - 85 и так далее. Поэтому, когда тело уезжает вверх или вниз, все детали едут вместе с ним и цыплёнок не разваливается на части. Это важный приём: привязывай детали персонажа к одной общей переменной, и они всегда будут двигаться слаженно.
Пример 3. Бег по кругу: sin и cos вместе
До сих пор мы использовали только sin для вертикали. А если взять sin для одной оси и cos для другой — точка побежит по идеальному кругу. Помнишь колесо обозрения: sin даёт высоту, cos — сдвиг вбок. Вместе они и есть кабинка, едущая по окружности.
let ugol = 0;
let cx = 200;
let cy = 200;
let r = 120;
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(255, 245, 200);
// гнездо-центр
fill(150, 110, 60);
ellipse(cx, cy, 60, 60);
// точка на окружности по углу
let x = cx + cos(ugol) * r;
let y = cy + sin(ugol) * r;
// цыплёнок бежит по кругу
fill(255, 215, 0);
ellipse(x, y, 40, 40);
ugol = ugol + 0.03;
}Результат: в центре кремового холста лежит коричневый кружок-гнездо. Вокруг него по ровной круговой орбите бежит маленький жёлтый цыплёнок — как Луна вокруг планеты. Он движется с одинаковой скоростью, не ускоряясь и не замедляясь, и через равные промежутки возвращается в ту же точку. Орбита идеально круглая.
Рецепт точки на окружности
Две строки делают всю работу:
let x = cx + cos(ugol) * r;
let y = cy + sin(ugol) * r;Читай так: cx, cy — центр круга (наше гнездо), r — радиус (как далеко от центра летит цыплёнок). cos(ugol) отвечает за сдвиг по горизонтали, sin(ugol) — за сдвиг по вертикали. Когда угол растёт, пара (cos, sin) обходит весь круг, и точка едет по орбите. Это тот самый рецепт, по которому рисуют стрелки часов, спиннеры загрузки, планеты в симуляторах и круговые меню в играх.
Хочешь эллипс вместо круга? Дай разный радиус для x и y, например cos(ugol) * 150 и sin(ugol) * 80 — орбита станет вытянутой. Поэкспериментируй.
Частые ошибки и подводные камни
1. Забывают про map или умножение — и движения «не видно»
Самая частая жалоба новичка: «я написал sin(ugol), а цыплёнок стоит на месте». Так и должно быть! sin выдаёт числа от -1 до 1 — это меньше пикселя, глаз такое смещение не заметит. Обязательно умножай результат на амплитуду (* 40, * 100) или прогоняй через map, иначе волна спрячется в одну точку.
2. Думают, что угол в градусах
В p5.js sin и cos по умолчанию считают угол в радианах, а не в градусах. Если прибавлять к углу по 1 каждый кадр, думая, что это «один градус», движение будет дёрганым и слишком быстрым — ведь 1 радиан это аж 57 градусов. Для плавности прибавляй маленькие доли: 0.02, 0.05. Полный круг — это TWO_PI, примерно 6.28, а не 360.
3. Не увеличивают угол — волна замирает
Если забыть строку ugol = ugol + 0.05; в конце draw(), угол навсегда останется нулём, sin(0) всегда даст 0, и цыплёнок застынет в одной позе. Колесо едет только потому, что угол растёт каждый кадр. Нет роста угла — нет движения.
4. Путают sin и cos местами в круге
Для движения по кругу cos идёт на горизонталь (x), а sin — на вертикаль (y). Если перепутать и поставить обе функции на одну ось, точка не побежит по кругу, а будет ездить по диагонали туда-сюда. Запомни связку: cos — «вбок» (по горизонтали слева направо), sin — «вверх-вниз».
5. Объявляют ugol внутри draw()
Если написать let ugol = 0; внутри draw(), угол будет рождаться заново нулём на каждом кадре и никогда не вырастет. Переменная угла, как любая переменная состояния, должна жить снаружи всех функций, в самом верху файла. Только тогда она копит значение от кадра к кадру.
Мини-проект: дышащее солнце над гнездом
Собери собственную сцену, где сразу две вещи живут по синусоиде. База — пример 2, дальше доделай сам:
- Нарисуй цыплёнка, который качается вверх-вниз, как в примере 2 (формула
200 + sin(ugol) * 40). - Добавь в угол холста солнце — жёлтый круг. Пусть оно дышит: его диаметр меняется по формуле
60 + sin(ugol) * 15. Так радиус будет плавно расти и уменьшаться. - Сделай так, чтобы солнце и цыплёнок качались не в такт. Для этого заведи второй угол или прибавляй к углу солнца сдвиг: используй
sin(ugol + PI)— тогда, когда цыплёнок вверху, солнце будет в нижней фазе. - Усложни: пусти по небу облако, которое движется по горизонтали через
cos:let oblakoX = 200 + cos(ugol) * 150;. Оно будет плавно уплывать к краю и возвращаться.
Когда заработает — поиграй с амплитудами и скоростями. Сделай качание цыплёнка медленным и широким, а дыхание солнца — быстрым и мелким. Почувствуй, как два числа в формуле полностью меняют характер движения. Это твоя сцена — оживляй её как нравится.
Итоги
Сегодня ты приручил две самые полезные функции для анимации и понял, что за ними нет никакой магии — только колесо обозрения. Главное, что стоит унести:
sinиcosберут угол и возвращают плавное число от-1до1.sin— высота точки на колесе,cos— её сдвиг вбок.- Чтобы движение было видно, результат надо растянуть: умножить на амплитуду или прогнать через
map. - Рецепт колебания:
центр + sin(угол) * размах. Рецепт движения по кругу:x = cx + cos(угол) * r,y = cy + sin(угол) * r. - Угол надо плавно увеличивать каждый кадр маленькими шажками (
0.02–0.05), и хранить его в переменной снаружи функций. Углы в p5.js — в радианах, полный круг этоTWO_PI.
Теперь у тебя в руках инструмент для любого плавного движения — а это половина всей красивой анимации. В следующем уроке мы соединим волну с циклом for и нарисуем сразу ряд точек по синусоиде — получится настоящая бегущая волна из десятков элементов, как эквалайзер в музыкальном плеере. Тот же sin, только теперь много раз за кадр. До встречи в коде!