Мышь: mouseX и mouseY
До этого момента твой цыплёнок жил сам по себе — а сейчас он впервые посмотрит на тебя и пойдёт за твоей мышью.
Главное правило урока:
mouseXиmouseY— это две встроенные переменные p5.js, в которых всегда лежат текущие координаты курсора на холсте. Они сами обновляются каждый кадр, и тебе достаточно просто их прочитать.
Зачем тебе координаты мыши
Открой любую игру на телефоне или представь, как ты водишь пальцем по экрану в редакторе стикеров: куда ведёшь палец — туда едет кисть, персонаж, прицел. Картинка реагирует на тебя прямо сейчас, без задержки. Именно это превращает картинку в интерактив — то, с чем можно играть, а не просто смотреть.
В прошлом уроке про переменные состояния и движение ты заставил цыплёнка двигаться самому: завёл переменную с координатой и менял её каждый кадр. Цыплёнок ездил по холсту, отскакивал от стен — но делал это по своему сценарию, не обращая на тебя внимания. Сегодня мы перевернём управление: пусть движением рулишь ты, прямо мышью.
К концу урока ты соберёшь вот такой кадр: голубое небо, а по нему за твоим курсором плавно скользит жёлтый CodeChick с оранжевым клювом — куда мышь, туда и он. Подведёшь курсор к углу — цыплёнок прибежит в угол. Поведёшь по кругу — он закружится следом. И всё это уместится буквально в несколько строк, потому что вся тяжёлая работа — слежение за мышью — уже сделана за тебя самим p5.js.
Звучит как магия? На самом деле это две самые простые и приятные переменные во всём p5.js. Давай разберёмся, что в них лежит и как ими пользоваться.
И ещё одна приятная мысль на старте: всё, что ты сегодня узнаешь, можно сразу применить в чём угодно. Кисть, которая рисует там, где мышь; прицел в простой игре; ползунок, который тянется за пальцем; персонаж, который смотрит на курсор. Под капотом у всех этих штук — те же самые mouseX и mouseY. Ты не учишь узкий фокус ради одного эффекта — ты получаешь базовый кирпич, из которого собирается почти любой интерактив на холсте.
Что такое mouseX и mouseY
Две переменные, которые всё знают
Вспомни систему координат с самых первых уроков: любая точка на холсте — это пара чисел (x, y), где (0, 0) — левый верхний угол, x растёт вправо, y растёт вниз. Так вот, p5.js постоянно следит за курсором и кладёт его координаты в две специальные переменные:
mouseX— горизонтальная координата курсора (сколько пикселей вправо от левого края);mouseY— вертикальная координата курсора (сколько пикселей вниз от верхнего края).
Представь, что у холста есть невидимый датчик, как у двери в магазине: он всё время замечает, где сейчас курсор, и шепчет эти два числа твоему коду. Тебе не нужно ничего настраивать или включать — mouseX и mouseY доступны сразу, в любой момент внутри draw(). Ты просто читаешь их, как читаешь показания часов: посмотрел — узнал текущее значение.
Это и есть ключевая идея: ты не задаёшь эти переменные сам и не двигаешь их. За тебя это делает p5.js. Твоя задача — взять готовое число и подставить туда, где нужны координаты: в центр круга, в начало линии, в позицию текста.
Сравни это с прошлым уроком. Там тебе приходилось вручную заводить переменную, ставить ей стартовое значение и каждый кадр прибавлять скорость — целый ритуал, чтобы объект ожил. С мышью же половина работы исчезает: значение уже посчитано и обновлено за тебя, остаётся только им воспользоваться. Можно сказать, что mouseX и mouseY — это «бесплатные» переменные состояния, которые ведёт сам p5.js, опираясь на твою руку, а не на код. Поэтому первые интерактивные скетчи получаются такими короткими и при этом эффектными.
Самый первый пример: круг под курсором
Давай сразу нарисуем кружок ровно там, где сейчас мышь.
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235); // небо
fill(255, 209, 64); // тёплый жёлтый
circle(mouseX, mouseY, 60); // центр круга = позиция мыши
}Результат: на голубом небе жёлтый круг диаметром 60, который всё время держится прямо под курсором. Ведёшь мышь — круг едет следом без задержки, повторяя каждое движение руки.
Разберём по шагам, что здесь происходит. circle(mouseX, mouseY, 60) рисует круг, и вместо обычных чисел-координат мы подставили mouseX и mouseY. На каждом кадре p5.js сначала подставляет в них свежие координаты курсора, потом выполняет circle() — и круг оказывается ровно под мышью. А background() в начале draw() стирает прошлый кадр, поэтому за кругом не тянется хвост из старых кружков (помнишь правило: фон — первая строка).
Почему круг едет за мышью: всё дело в кадрах
Тут спрятана самая важная мысль урока, и её легко проскочить. Почему круг вообще движется, если в коде нет ни одной строчки про движение?
Вспомни про кадры и draw(). Функция draw() повторяется десятки раз в секунду — это как страницы флипбука, которые быстро листаются. И на каждом новом кадре значения mouseX и mouseY — свежие: p5.js обновляет их перед каждым прогоном draw(). То есть кадр за кадром круг рисуется во всё новой точке — там, где курсор оказался в этот момент. Глаз склеивает эти кадры в плавное движение, как страницы блокнота складываются в анимацию.
Сделаем это совсем наглядным — выведем координаты на экран прямо во время движения мыши.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(20);
fill(255);
textSize(28);
text("x: " + mouseX, 20, 40);
text("y: " + mouseY, 20, 80);
}Результат: на тёмном фоне белым текстом две строки — например «x: 213» и «y: 96». Стоит шевельнуть мышью, как числа мгновенно меняются: ведёшь вправо — растёт x, ведёшь вниз — растёт y. Уведёшь курсор в левый верхний угол — оба числа стремятся к нулю.
Поэкспериментируй: подведи курсор к каждому углу холста и посмотри, какие числа получаются. В левом верхнем будет около (0, 0), в правом нижнем — около (400, 400), ровно по размеру холста. Так ты прямо глазами увидишь, что mouseX и mouseY — это не магия, а просто два числа, которые всё время держат руку на пульсе курсора.
Кстати, обрати внимание на крошечную деталь, которая часто сбивает новичков: координата y растёт вниз, а не вверх, как нас учили на уроках математики. Ведёшь мышь к нижнему краю — mouseY увеличивается. Это привычно для экранов: отсчёт идёт от верхнего левого угла, как чтение текста — слева направо и сверху вниз. Если вдруг твой цыплёнок поедет «не в ту сторону» по вертикали, первым делом вспомни про это: вниз — это плюс, вверх — это минус. А ещё подвигай мышь за пределы холста и обратно: пока курсор внутри, числа меняются; когда он за краем, p5.js обычно показывает последнее значение у границы. Эти мелочи сейчас кажутся занудством, но именно они спасут тебя от часа недоумения позже.
Привязываем цыплёнка к мыши
Пора собрать настоящего CodeChick, который ходит за тобой. Хитрость одна: раз мы рисуем цыплёнка из нескольких фигур (тело, клюв, глаз), нам нужно, чтобы все они отсчитывались от позиции мыши. Иначе тело уедет за курсором, а клюв останется на месте — и цыплёнок развалится на части.
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
// запоминаем позицию мыши в понятные имена
let x = mouseX;
let y = mouseY;
// тело
fill(255, 209, 64);
circle(x, y, 100);
// клюв (правее центра тела)
fill(255, 140, 30);
triangle(x, y - 8, x + 45, y, x, y + 12);
// глаз (левее-выше центра)
fill(40);
circle(x - 22, y - 18, 14);
}Результат: по голубому небу за курсором плавно скользит целый цыплёнок CodeChick: жёлтое тело, оранжевый клюв-треугольник справа и тёмный глаз слева сверху. Куда ведёшь мышь — туда едет весь цыплёнок, не разваливаясь: клюв и глаз держатся на своих местах относительно тела.
Вот в чём фокус. Мы один раз записали позицию мыши в удобные переменные: let x = mouseX; и let y = mouseY;. А дальше все фигуры рисуем относительно этой точки: глаз — это x - 22 и y - 18 (чуть левее и выше центра), клюв — x + 45 (правее). Поэтому, когда мышь сдвигается, сдвигаются все части разом, ведь все они посчитаны от одного и того же x, y. Это намного аккуратнее, чем втыкать mouseX в каждую фигуру по отдельности, и куда легче читать.
Плавное преследование: цыплёнок не телепортируется, а догоняет
Заметил, что сейчас цыплёнок прилипает к курсору намертво, будто приклеен? А что, если он будет мягко догонять мышь, чуть отставая, как привязанный на резинке? Это и есть easing — приём плавного движения, который мы упоминали в глоссарии: объект на каждом кадре проходит лишь часть расстояния до цели и потому красиво замедляется к концу.
let chickX = 200;
let chickY = 200;
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
// двигаемся на 10% оставшегося пути к мыши
chickX = chickX + (mouseX - chickX) * 0.1;
chickY = chickY + (mouseY - chickY) * 0.1;
fill(255, 209, 64);
circle(chickX, chickY, 100);
fill(255, 140, 30);
triangle(chickX, chickY - 8, chickX + 45, chickY, chickX, chickY + 12);
}Результат: цыплёнок уже не прилипает к курсору, а мягко катится за ним, будто на невидимой резинке. Резко дёрнул мышь — цыплёнок плавно разгоняется и догоняет, тормозя у самой цели. Остановил курсор — он мягко доезжает и замирает рядом.
Здесь у цыплёнка появилась своя переменная состояния: chickX и chickY хранят его собственную позицию между кадрами (как в прошлом уроке). А выражение (mouseX - chickX) — это расстояние, которое осталось проехать до мыши; умножая его на 0.1, мы каждый кадр проходим лишь десятую часть пути. Чем меньше множитель, тем ленивее и плавнее погоня. Поменяй 0.1 на 0.02 — и цыплёнок станет совсем неспешным; поставь 0.5 — почти прилипнет к курсору.
Не только координаты: реагируем на близость мыши
Раз mouseX и mouseY — это обычные числа, с ними можно делать всё то же, что и с любыми числами: складывать, сравнивать, подставлять в условия. А значит, цыплёнок может не просто ехать за мышью, а реагировать на то, где она. Например, радоваться, когда курсор близко, и грустить, когда он далеко.
Чтобы узнать, далеко ли мышь от центра цыплёнка, в p5.js есть удобная функция dist() — она считает расстояние между двумя точками. Дадим ей координаты цыплёнка и координаты мыши, а потом по этому расстоянию решим, какого размера сделать глаз: близко — глаз большой и «живой», далеко — маленький.
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(135, 206, 235);
let chickX = 200;
let chickY = 200;
// расстояние от центра цыплёнка до курсора
let d = dist(chickX, chickY, mouseX, mouseY);
// тело
fill(255, 209, 64);
circle(chickX, chickY, 120);
// глаз тем больше, чем ближе мышь
let eyeSize = 30 - d * 0.08;
if (eyeSize < 6) {
eyeSize = 6; // не даём глазу исчезнуть совсем
}
fill(40);
circle(chickX - 20, chickY - 15, eyeSize);
}Результат: в центре неба сидит неподвижный жёлтый цыплёнок. Когда ты подводишь курсор близко к нему, его тёмный глаз становится крупным и удивлённым; стоит увести мышь в дальний угол — глаз сжимается в маленькую точку. Цыплёнок будто «следит» за тем, насколько ты близко.
Смотри, что мы сделали. dist(chickX, chickY, mouseX, mouseY) вернула число — расстояние в пикселях от цыплёнка до курсора. Когда мышь рядом, это число маленькое; когда далеко — большое (до ~280 в углу холста). Дальше мы превратили это число в размер глаза: 30 - d * 0.08. Близко (d мало) — вычитаем чуть-чуть, глаз остаётся крупным; далеко (d велико) — вычитаем много, глаз уменьшается. А if страхует нас, чтобы размер не ушёл в минус и глаз не исчез. Вот и вся «эмоция» — простая арифметика над расстоянием до мыши.
Частые ошибки и подводные камни
Пишешь
mouseX()со скобками.mouseXиmouseY— это переменные, а не функции. Скобки тут не нужны и вызовут ошибку. Простоcircle(mouseX, mouseY, 60), без круглых скобок после имени.Используешь mouseX внутри setup(). В
setup()скетч ещё только настраивается, мышь толком не отслеживается — тамmouseXравен 0. Координаты курсора имеют смысл только вdraw(), который крутится каждый кадр. Читай мышь именно там.Забыл background() — за цыплёнком тянется хвост. Если не стирать кадр в начале
draw(), все прошлые позиции цыплёнка останутся на холсте, и ты получишь жирную полосу из наложенных кружков. Иногда это красиво (эффект кисти), но если хочешь чистое движение — первой строкой ставьbackground().Подставил mouseX только в одну фигуру из нескольких. Тело поехало за мышью, а клюв и глаз остались на старом месте — цыплёнок развалился. Решение: записать
let x = mouseX;один раз и рисовать все части относительно этогоx,y.Перепутал X и Y. Подставил
mouseYтуда, где ждут горизонтальную координату, и цыплёнок двигается вверх-вниз, когда ты ведёшь мышь влево-вправо. Запомни:mouseX— это всегда «вправо», первый параметр;mouseY— «вниз», второй.
Мини-практика: оживи мир CodeChick
Возьми за основу пример с цыплёнком, привязанным к мыши, и доработай его сам. Вот план:
- Добавь цыплёнку второй глаз справа (например,
x + 6по горизонтали) — так, чтобы оба глаза ездили вместе с телом. Проверь, что лицо не разваливается при движении мыши. - Сделай так, чтобы размер цыплёнка зависел от высоты курсора: подставь
mouseYв диаметр тела вместо числа 100. Поведи мышь сверху вниз и посмотри, как цыплёнок раздувается. Подумай, почему вверху он крошечный. - Нарисуй рядом зёрнышко (маленький овал) в фиксированной точке холста — пусть цыплёнок «гоняется» за мышью мимо неподвижного зерна. Так ты увидишь разницу между тем, что привязано к мыши, и тем, что стоит на месте.
- Для смелых: добавь к погоне easing из примера выше и поиграй с множителем от
0.02до0.4, подбирая «характер» цыплёнка — ленивый он или шустрый.
Меняй числа и сразу смотри, что вышло. Именно так, руками, лучше всего чувствуется, как координаты мыши превращаются в живое движение.
Итоги
Сегодня твой цыплёнок впервые ожил и отреагировал на тебя. Вот что ты теперь умеешь:
mouseXиmouseY— встроенные переменные p5.js с текущими координатами курсора; их не надо заводить, просто читай.- Эти значения сами обновляются каждый кадр перед вызовом
draw()— поэтому фигура, привязанная к мыши, движется без всякого кода движения. - Чтобы фигура ехала за курсором, достаточно подставить
mouseX,mouseYв её координаты. - Сложного героя из нескольких фигур рисуй относительно одной точки (
let x = mouseX;), чтобы он не разваливался. - Добавив переменную состояния и easing, можно заставить цыплёнка плавно догонять мышь, а не прилипать к ней.
Дальше будет ещё живее: в следующем уроке мы научимся ловить нажатия кнопки мыши — и цыплёнок сможет не просто следить за курсором, а реагировать на клик. Хлопнул мышью — что-то произошло. Увидимся на следующей странице!