В моем последнем посте Программирование для геймеров мы говорили о воспроизведении счетчика голода в The Long Dark. Сегодняшний пост посвящен повторению функции из Hellblade: Senua’s Sacrifice. Hellblade — одно из самых мучительных путешествий в разум психически больного человека, которое я когда-либо видел в видеоигре. Если вы не играли в нее, настоятельно рекомендую ознакомиться. Вам даже не нужно беспокоиться о зависимости, потому что в игре есть конкретное начало, середина и конец. Одним из уникальных аспектов Hellblade является мини-игра-головоломка, в которой нужно найти форму в природе, которая соответствует форме, вырезанной на различных рунах в мире.

Я решил воссоздать простую версию этой мини-игры-головоломки с Javascript в Glitch. Вы можете посмотреть его сразу здесь или сначала попробовать сами. В этом уроке мы будем использовать HTML5 Canvas и ванильный Javascript. Мы загрузим фоновое изображение некоторых деревьев, а пользователь будет управлять треугольной формой поверх него и пытаться найти, где среди деревьев можно найти такую ​​же фигуру. Когда они наводят треугольную форму в правильном месте, которое совпадает с местом, где деревья образуют эту форму, треугольник меняет цвет. Вы могли бы использовать более сложную форму, но я хотел сохранить простоту, используя треугольник для этого урока.

К счастью, HTML очень прост, нам нужно сделать всего две вещи. Сначала нам нужно создать элемент холста с помощью ‹canvas› и задать ему ширину, высоту и идентификатор, как показано ниже. Ширина и высота должны быть примерно равны размеру нашего изображения. Мы будем использовать идентификатор для идентификации холста в Javascript. Вся игра в значительной степени будет происходить на этом холсте, что позволяет выполнять расширенные манипуляции с графикой, которые вы не можете делать с другими элементами HTML.

Во-вторых, нам нужно добавить наше фоновое изображение дерева, чтобы наш холст мог получить доступ к данным изображения. Однако я также добавлю скрытый класс, потому что иначе мы увидим наше изображение дважды, так как оно появится внутри нашего холста. Мы также хотим присвоить нашему изображению идентификатор, так как холст также должен получить к нему доступ. Я назвал это «деревьями», потому что это изображение деревьев. Приведенный ниже код будет находиться внутри ваших тегов ‹body›.

<img id="trees" class="hidden" src="https://cdn.glitch.com/eb083ff0-5e3b-41d0-be19-711a1dcd89f5%2FDSC0063-1024x680.jpg?v=1589402686658"/> canvas width="800" height="600" style="border:1px solid #d3d3d3;" id="canvas"></canvas> <script>Our Javascript will go here, or in a .js file if you prefer </script>

Затем, чтобы ваше изображение было скрыто, вам нужно будет добавить его в свои теги ‹head›.

<style> .hidden { display: none; } </style>

Не беспокойтесь, даже если изображение скрыто, наш волшебный холст по-прежнему сможет получить доступ к данным, чтобы отобразить их во всей красе. Чудесно! Теперь наш файл HTML настроен, и мы можем сосредоточиться на Javascript. Первым шагом является идентификация нашего холста и получение контекста, который позволяет нам запускать функции для фактического изменения того, что отображается.

let context; let img; let canvas; window.onload = function() { canvas = document.getElementById("canvas"); context = canvas.getContext("2d"); img = document.getElementById("trees"); context.drawImage(img, 0, 0); };

Я объявляю переменные изображения, холста и контекста вверху, потому что нам понадобится доступ к ним по всему коду. Наличие window.onload гарантирует, что мы не попытаемся получить холст до того, как он будет загружен в наш браузер. В первой строке функции мы получаем наш холст, который нам нужен для того, чтобы получить наш контекст. Затем мы получаем наше изображение и рисуем его на холсте с помощью context.drawImage. Эта функция берет наше изображение, а затем координаты x и y (которые начинаются с 0 в верхнем левом углу, поэтому в этом случае наше изображение займет весь холст). Если бы наш контекст находился в трехмерном пространстве, а не в двухмерном, мы бы также добавили третье значение для нашего индекса z, плоскость перспективы.

Ну и что дальше? Давайте немного подумаем о том, какие данные нам нужны, чтобы это работало. Пока все, что у нас есть, это фоновое изображение дерева на холсте. Мы хотим, чтобы была форма, которую пользователь может перемещать поверх изображения. Хотя было бы неплохо позволить пользователю перетаскивать фигуру, самый простой вариант — просто заставить фигуру следовать за мышью пользователя.

Для этого нам нужно получить координаты мыши пользователя. На самом деле это самая сложная часть, потому что холст не очень сложен с данными, которые он предоставляет по умолчанию. Мы должны сделать некоторую математику, чтобы учесть расположение холста на окне. Функция ниже сделает это за вас.

function getPosition(el) { var xPosition = 0; var yPosition = 0; while (el) { xPosition += (el.offsetLeft - el.scrollLeft + el.clientLeft); yPosition += (el.offsetTop - el.scrollTop + el.clientTop); el = el.offsetParent; } return { x: xPosition, y: yPosition }; }

Эта функция принимает элемент холста и возвращает координаты x и y холста относительно окна браузера. Мы вызовем эту функцию внутри window.onload, чтобы получить позицию холста, которая затем будет использоваться для получения точной позиции мыши. Не беспокойтесь слишком сильно, если вы не понимаете всего этого. Если бы мы использовали другой фреймворк, такой как P5js, в этой дополнительной математике вообще не было бы необходимости.

Важная часть следующая. Мы собираемся добавить то, что называется прослушивателем событий, то есть функцией, которая будет вызываться каждый раз, когда окно обнаруживает взаимодействие с пользователем. Мы можем определить, какое взаимодействие с пользователем мы слушаем. В данном случае это будет перемещение мыши. Пока мы на этом, давайте также вызовем нашу функцию getPosition, чтобы получить положение нашего холста и добавить наши переменные координаты мыши вверху, так как нам скоро понадобится получить к ним доступ.

let context; let mouseX = 0; let mouseY = 0; let canvasPos; let img; let canvas; window.onload = function() { canvas = document.getElementById("canvas"); canvasPos = getPosition(canvas); // getting our canvas position context = canvas.getContext("2d"); img = document.getElementById("trees"); context.drawImage(img, 0, 0); canvas.addEventListener("mousemove", setMousePosition, false); //the line above is listening for when the user moves their mouse, and will call the function "setMousePosition" };

Итак, теперь у нас есть прослушиватель событий, но этот код не запустится, потому что функция setMousePosition еще не существует. Именно здесь происходит большая часть волшебства. Нам нужно будет перерисовывать нашу фигуру каждый раз, когда мышь движется. Нам также нужно будет проверить, находится ли фигура в том месте, где она соответствует шаблону, чтобы мы могли сообщить пользователю, что он ее нашел! Вы можете добавить эту функцию ниже window.onload.

function setMousePosition(e) { mouseX = e.clientX - canvasPos.x; mouseY = e.clientY - canvasPos.y; }

Приведенный выше код даст нам текущие координаты мыши пользователя на холсте. Мы передаем e, который обозначает элемент, который передается в функцию, в данном случае наш элемент холста. Вычитание происходит для учета смещения положения холста в окне браузера, как упоминалось ранее. Теперь мы можем нарисовать нашу фигуру!

function setMousePosition(e) { mouseX = e.clientX - canvasPos.x; mouseY = e.clientY - canvasPos.y; context.beginPath(); // tell canvas you want to begin drawing lines context.moveTo(mouseX, mouseY); // move where the cursor starts the line context.lineTo(mouseX - 25, mouseY + 125); // draw first line context.lineTo(mouseX + 25, mouseY + 125); // draw second line context.fillStyle = "#FF6A6A"; //set the color context.fill(); //fill shape with color }

Как вы, наверное, поняли из моих комментариев к приведенному выше коду, рисование фигуры состоит из нескольких шагов. Сначала мы должны сообщить холсту, что мы хотим рисовать линии с помощью context.beginPath, а затем нам нужно переместить наш курсор. Поскольку мы хотим, чтобы наш треугольник следовал за мышью, мы перемещаем курсор в те же координаты.

Я хочу, чтобы мой треугольник был немного вытянутым, поэтому, когда я определяю конечные координаты моей первой строки, я хочу, чтобы они были немного левее (-25) и дальше вниз (+125). Чтобы моя мышь находилась в центре вершины моего треугольника, я установил координаты других линий на ту же величину, но в другом направлении по координате x (+25). Последняя строка возвращает к исходным координатам, поэтому вам не нужен дополнительный код для завершения формы треугольника. Теперь мы можем установить стиль заливки в шестнадцатеричном коде цвета лосося. Вы должны вызвать функцию заполнения, чтобы этот цвет действительно был применен к вашей фигуре.

Мы приближаемся, но если вы запустите код сейчас, вы можете увидеть что-то немного странное! Вместо того, чтобы иметь треугольник, который следует за нашей мышью, мы, кажется, рисуем холст. Это потому, что холст постоянно рисует больше треугольников каждый раз, когда мы двигаем мышь, и холст не очищается. К счастью, очистить холст довольно легко.

function setMousePosition(e) { mouseX = e.clientX - canvasPos.x; mouseY = e.clientY - canvasPos.y; // add the lines below context.clearRect(0, 0, canvas.width, canvas.height); //clearing canvas context.drawImage(img, 10, 10); //drawing our image again since that got cleared out context.beginPath(); context.moveTo(mouseX, mouseY); context.lineTo(mouseX - 25, mouseY + 125); context.lineTo(mouseX + 25, mouseY + 125); context.fillStyle = "#FF6A6A"; context.fill(); }

Функция clearRect принимает четыре значения: координаты x и y, которые определяют верхний левый угол прямоугольника, а также высоту и ширину. Если бы мы предоставили что-то меньшее, чем высота и ширина холста, только часть нашего холста была бы очищена, но мы хотим очистить его целиком. Конечно, это также очистит наше изображение, поэтому нам нужно снова нарисовать его на холсте. Все это должно произойти до того, как мы нарисуем наш треугольник, иначе он будет закрыт нашим изображением.

Теперь у вас должен получиться красивый маленький вытянутый треугольник лосося, плавающий поверх нашего изображения леса, послушно следуя за нашей мышью. Осталось сделать только одно. Нам нужно дать пользователю некоторое указание, когда он «обнаружил» паттерн. Здесь можно сделать много необычных вещей. Мы могли бы отобразить некоторый текст, чтобы сообщить пользователю, что он нашел шаблон. Мы могли бы добавить немного причудливой анимации, как в самой игре Hellblade. Но для краткости и чтобы дать вам свободу экспериментировать с холстом по своему усмотрению, давайте просто изменим цвет нашего треугольника. Этот код будет добавлен в конец функции our setMousePosition.

if(mouseX > 625 && mouseX < 630) { if(mouseY > 10 && mouseY < 20) { context.fillStyle = #a117f2"; context.fill(); } }

Здесь мы проверяем наши координаты mouseX и mouseY, чтобы увидеть, совпадают ли они с координатами, где, как мы знаем, находится наша фигура на изображении. Вы можете заметить, что координаты x и y имеют диапазон в 5 пикселей, потому что на самом деле довольно сложно навести мышь на 1 или 2 определенных пикселя.

Я позволил себе вычислить координаты изображения в нашем уроке, но если вы хотите сделать это с другим изображением или другой формой, вам нужно будет добавить несколько операторов console.log к вашим mouseX и mouseY, чтобы вы могли определить, где форма должна изменить цвет. Я меняю цвет на простой белый, хотя вы, очевидно, можете изменить его на любой другой цвет. Посмотрите мою версию на Glitch здесь.

Вот и все! Теперь вы можете подключить любое изображение и посмотреть, смогут ли ваши друзья выяснить, смогут ли они найти шаблон. Очевидно, что это не так уж сложно с формой и изображением, которые я предоставил, но, безусловно, это может быть сложнее с большим изображением или более необычной формой. Я рекомендую ознакомиться со следующими учебными пособиями, если вы заинтересованы в расширении своих знаний о рисовании фигур и изображений с помощью элемента холста:

Рисование фигур

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes

Преобразование + текст

https://eloquentjavascript.net/17_canvas.html

Создайте приложение для рисования

http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/

Если вам понравилась эта статья, подпишитесь на меня в Твиттере @nadyaprimak или, если вам нужны дополнительные советы о том, как проникнуть в технологическую отрасль, вы можете прочитать мою книгу «Foot in the Door» в мягкой обложке или на Kindle прямо сейчас.

Первоначально опубликовано на https://www.nadyaprimak.com 15 мая 2020 г.