Как сделать игру жизнь js
В данной статье мы продолжим создавать трехмерную браузерную игру лабиринт на чистом html, css и javascript. В предыдущей части мы сделали простой 3-мерный мир, реализовали движение, управление, столкновения игрока со статическими объектами. В этой части мы будем добавлять гравитацию, статическое солнечное освещение (без теней), загружать звуки и делать меню. Увы, как и в первой части, демок здесь не будет.
Вспомним код, который мы сделали в предыдущей части. У нас имеются 3 файла:
1. Реализация гравитации и физики прыжка
У нас есть несколько переменных, которые создаются в разных частях файла javascript. Будет лучше, если мы перенесем их в одно место:
Добавим ускорение свободного падения к ним:
В конструктор player добавим 3 переменные — vx, vy и vz:
Это переменные скорости движения. Меняя их, мы можем изменять скорость бега и начальную скорость прыжка игрока. Пока применим новые переменные в update():
Теперь игрок движется быстрее. Но он не падает и не прыгает. Нужно разрешить прыжок тогда, когда он на чем-то стоит. А стоять он будет тогда, когда столкнется с горизонтальной (или почти) поверхностью. Как определить горизонтальность? Нужно найти нормаль плоскости прямоугольника. Делается это просто. Относительно координат прямоугольника нормаль направлена вдоль оси z. Тогда в мировых координатах нормаль имеет преобразованные координаты. Найдем нормаль (добавим локальную переменную normal):
Чтобы поверхность была горизонтальной, скалярное произведение нормали на ось y в мировых координатах должно равняться 1 или -1, а почти горизонтальная плоскость – близко к 1 или -1. Зададим условие почти горизонтальной плоскости:
Не забудем, что при отсутствии столкновений игрок точно не будет на земле, поэтому по умолчанию в начале функции collision() зададим onGround = false:
Однако, если игрок столкнется с поверхностью снизу, то он тоже окажется как бы на земле. Чтобы предотвратить это, проверим игрока на нахождение сверху плоскости (point3[1] должна быть меньше point2[1]):
Что мы делаем? взгляните на картинку:
Красный крест должен находиться ниже оранжевого в мировой системе координат (или y-координата должна быть больше). Это мы и проверяем в point3[1] > point2[1]. А point3 – есть как раз координаты красной точки. Перенесем инициализацию point2 внутрь условии коллизии:
Перенесемся в update(). Здесь мы тоже сделаем изменения. Во первых, добавим гравитацию и уберем смещение по y при нажатии на пробел:
Во вторых, если игрок находится на земле, запрещаем гравитацию, запрещаем смещения по y (иначе после хождения по наклонной поверхности игрок будет взлетать) и добавляем возможность прыжка (условие if (onGround)):
Естественно, сразу после произведения прыжка запрещаем повторный прыжок, переведя параметр onGround в false. В условии нажатия пробела правдивость этого параметра больше не нужна:
Для проверки изменений изменим мир:
Если мы запустим игру, то увидим, что игрок может взбираться по почти вертикальной зеленой стене. Запретим это, добавив else dy = y1 — y0:
Итак, столкновения с сильно вертикальными стенками не изменяют смещения по y. Поэтому разгон на таких стенках теперь полностью исключается. Попробуем взобраться на зеленую стену. У нас это теперь не получится. Итак, мы разобрались с гравитацией и прыжками и теперь мы можем достаточно реалистично взбираться по слабо наклоненным поверхностям. Проверим код:
2. Создадим меню
Меню создадим в виде html-панелей и html-блоков. Оформление у всего меню будет примерно одинаковым: фон и стиль кнопок можно задать общими для всех. Итак, зададим три панели меню: главное меню, инструкция и вывод результатов по завершению игры. Переходы между меню, переход в мир и обратно будет выполняться скриптами javascript. Чтобы не нагромождать файл script.js, для переходов меню создадим новый файл menu.js, а в index.html подключим его:
В контейнере создадим 3 элемента, которые будут панелями меню:
Оформим их, добавив в style.css свойства для класса “menu”:
В меню (в файле index.html) добавим кнопки с соответствующими надписями:
Для кнопок тоже зададим стили в style.css:
Но мы не видим меню, так как у них задан стиль display:none, При запуске же игры один из пунктов меню должен быть виден, поэтому в html для 1-го меню добавим запись style = “display:block;”, а выглядеть это будет следующим образом:
Меню стало выглядеть вот так:
Отлично. Но если мы нажмем на кнопку, то курсор у нас захватится. Значит нам нужно разрешить захват мыши только в случае игры. Для этого введем в script.js переменную canlock и добавим ее в пункт создадим переменные:
Теперь мы можем щелкать меню. Настроим переходы с помощью скриптов в файле menu.js:
Теперь все кнопки меню, за исключением “начать игру”, работают. Настроим теперь кнопку button1. Если вы помните, в файле script.js функции CreateNewWorld() и setInterval() запускаются при загрузке веб-страницы. Удалим их оттуда. Вызывать их будем только при нажатии кнопки button1. Сделаем это:
Меню мы создали. Да, оно еще некрасивое, но это легко поправляется.
3. Создадим предметы и переход уровней.
А содержимое createNewWorld() изменим:
Строка нужна для того, чтобы задавать имя id. Игра пока ничуть не изменилась. Теперь добавим 3 массива: монеты (things), ключи (keys) и финиш (finish). Вставим их сразу после массива карты:
А в menu.js применим функцию CreateSquares() внутри обработчика нажатия кнопки “button1”:
Теперь настроим исчезновение предметов. В menu.js создадим функцию проверки расстояний от игрока до предметов:
Также в этом же файле создадим функцию repeatFunction() и добавим в нее команды:
А ее циклический вызов запустим в setInterval внутри button1:
Теперь предметы исчезают, когда мы к ним подходим. Однако они ровно ничего не делают. А мы хотим, чтобы при взятии желтых квадратов нам добавлялись очки, при взятии красных – появлялась возможность взять синий и закончить игру. Модифицируем функцию interact():
Изменим входные параметры для вызовов этой функции:
А в начале файла добавим четыре новые переменные:
Вы спросите, почему мы создали массивы из одного элемента а не просто переменные? Дело в том, что мы хотели передать эти переменные в interact() по ссылке, а не по значению. В javascript обычные переменные передаются только по значению, а массивы по ссылке. Если мы передадим в interact() просто переменную, то num будет копией переменной. Изменение num не приведет к изменению k или m. А если мы передаем массив, то num будет ссылкой на массив k или m, и когда мы будем менять num[0], то будет меняться k[0] и m[0]. Можно было, конечно, создать 2 почти одинаковые функции, но лучше обойтись одной, чуть более универсальной.
Для финиша все-таки придется создать отдельную функцию:
А clearWorld() настроим в script.js:
Как видите, очистка мира проводится довольно просто. В repeatFunction() добавим finishInteract():
Что происходит в finishInteract()? Если мы не взяли ключ (k[0] == 0), то пока ничего не происходит. Если взяли, то игра заканчивается, а происходит следующее: очищается мир, останавливается функция repeatFunction(), курсор перестает быть захваченным, счетчик ключей обнуляется, а мы переходим в главное меню. Проверим, запустив игру. Все работает. Однако после нажатия снова на игру, мы оказываемся сразу на финише, а некоторые предметы исчезают. Все потому что мы не ввели место первоначального спауна игрока, а массивы изменяются в течение игры. Давайте добавим в button1 точку спауна для игрока, а именно, приравняем его координаты к элементам массива start[0]:
Теперь игрок появляется в начале координат. Но вот вопрос: а если уровней в игре будет несколько? Добавим переменную уровней в menu.js:
Переделаем переменные map, things, keys, start, finish внутри script.js в массивы, слегка изменив их название:
Добавим 2-й уровень:
А сами массивы инициализируем перед уровнями:
функцию CreateNewWorld() придется изменить, добавив туда аргумент:
Изменим вызов CreateNewWorld() в файле menu.js:
Теперь при запуске консоль выдаст ошибку. Верно, ведь мы переименовали переменные map, things, keys и finish, теперь javascript не может понять, что это за переменные. Заново их инициализируем в script.js:
А в button1 (в menu.js) этим переменным присвоим копии элементов массивов mapArray, thingsArray, keysArray и finishArray (для лучшей читабельности поставим комментарии):
Где userSlice() – функция, которая копирует массив:
Если бы мы просто написали, к примеру, keys = keysArray[level], то в переменные были бы переданы не копии массивов, а указатели на них, а значит, они изменялись бы в процессе игры, что недопустимо, ибо при повторном запуске ключа на исходном месте уже не было бы. Вероятно, вы спросите, почему я не применил просто keysArray[level].slice(), а изобрел свои функции? Ведь slice() тоже копирует массивы. Я пробовал так сделать, однако он копировал именно ссылку на массив, а не сам массив, в результате чего изменение keys приводило к изменению keysArray[level], что означало пропадание ключа при повторном запуске. Дело в том, что в документации написано, что в одних случаях он воспринимает массивы как массивы и копирует их, в других же он воспринимает массивы как объекты и копирует лишь указатели на них. Как он это определяет, для меня загадка, поэтому если мне кто-нибудь подскажет, почему slice() не работает как планировалось, то я буду ему сильно благодарен.
Сделаем переход уровней. Это довольно просто. Изменим finishInteract(), добавив внутрь else следующие строки:
То есть, значение уровня прибавляется на 1, а если все уровни пройдены (у нас их 2), то уровни сбрасываются и очки score сбрасываются. Проверить это трудно, так как наши уровни сейчас ничем не отличаются. Изменим тогда mapArray[1]:
Вышедшая в 2015 году Agar.io стала прародителем нового жанра игр .io, популярность которого с тех пор сильно возросла. Рост популярности игр .io я испытал на себе: за последние три года я создал и продал две игры этого жанра..
На случай, если вы никогда раньше не слышали о таких играх: это бесплатные многопользовательские веб-игры, в которых легко участвовать (не требуется учётная запись). Обычно они сталкивают на одной арене множество противоборствующих игроков. Другие знаменитые игры жанра .io: Slither.io и Diep.io.
В этом посте мы будем разбираться, как с нуля создать игру .io. Для этого достаточно будет только знания Javascript: вам нужно понимать такие вещи, как синтаксис ES6, ключевое слово this и Promises. Даже если вы знаете Javascript не в совершенстве, то всё равно сможете разобраться в большей части поста.
Пример игры .io
Для помощи в обучении мы будем ссылаться на пример игры .io. Попробуйте в сыграть в неё!
Игра довольно проста: вы управляете кораблём на арене, где есть другие игроки. Ваш корабль автоматически стреляет снарядами и вы пытаетесь попасть в других игроков, в то же время избегая их снарядов.
1. Краткий обзор/структура проекта
В примере используется следующее:
-
— самый популярный веб-фреймворк для Node.js, управляющий веб-сервером игры. — библиотека websocket для обмена данными между браузером и сервером. — менеджер модулей. О том, зачем использовать Webpack, можно прочитать здесь.
Вот как выглядит структура каталога проекта:
public/
Всё в папке public/ будет статически передаваться сервером. В public/assets/ содержатся используемые нашим проектом изображения.
Весь исходный код находится в папке src/ . Названия client/ и server/ говорят сами за себя, а shared/ содержит файл констант, импортируемый и клиентом, и сервером.
2. Сборки/параметры проекта
Как сказано выше, для сборки проекта мы используем менеджер модулей Webpack. Давайте взглянем на нашу конфигурацию Webpack:
Самыми важными здесь являются следующие строки:
- src/client/index.js — это входная точка клиента Javascript (JS). Webpack будет начинать отсюда и станет рекурсивно искать другие импортированные файлы.
- Выходной JS нашей сборки Webpack будет располагаться в каталоге dist/ . Я буду называть этот файл нашим пакетом JS.
- Мы используем Babel, и в частности конфигурацию @babel/preset-env для транспиляции (transpiling) нашего кода JS для старых браузеров.
- Мы используем плагин для извлечения всех CSS, на которые ссылаются файлы JS, и для объединения их в одном месте. Я буду называть его нашим пакетом CSS.
Вы могли заметить странные имена файлов пакетов '[name].[contenthash].ext' . В них содержатся подстановки имён файлов Webpack: [name] будет заменён на имя входной точки (в нашем случае это game ), а [contenthash] будет заменён на хеш содержимого файла. Мы делаем это, чтобы оптимизировать проект для хеширования — можно приказать браузерам бесконечно кешировать наши пакеты JS, потому что если пакет изменяется, то меняется и его имя файла (изменяется contenthash ). Готовым результатом будет имя файла вида game.dbeee76e91a97d0c7207.js .
webpack.dev.js
Для эффективности мы используем в процессе разработки webpack.dev.js , и переключается на webpack.prod.js , чтобы оптимизировать размеры пакетов при развёртывании в продакшен.
Локальная настройка
Рекомендую устанавливать проект на локальной машине, чтобы вы могли следовать за этапами, перечисленными в этом посте. Настройка проста: во-первых, в системе должны быть установлены Node и NPM. Далее нужно выполнить
и вы готовы к работе! Для запуска сервера разработки достаточно выполнить
и зайти в веб-браузере на localhost:3000. Сервер разработки будет автоматически пересобирать заново пакеты JS и CSS в процессе изменения кода — просто обновите страницу, чтобы увидеть все изменения!
Давайте приступим к самому коду игры. Для начала нам потребуется страница index.html , при посещении сайта браузер будет загружать её первой. Наша страница будет довольно простой:
index.html
Этот пример кода слегка упрощён для понятности, то же самое я сделаю и со многими другими примерами поста. Полный код всегда можно посмотреть на Github.
Дипломный проект курса JavaScript
В рамках дипломного проекта вам необходимо реализовать ключевые компоненты игры. Игра будет запускаться и работать в браузере.
Игра после реализации имеет следующий интерфейс:
На иллюстрации вы можете видеть:
- Белые стены препятствий.
- Красные огненные шары и лаву.
- Желтые монетки.
- Игрока бордового цвета, потому что в данный момент он умер от столкновения с огненным шаром.
Игрок управляется стрелками с клавиатуры. Основная цель каждого уровня — собрать все монетки.
Реализовать проект и предоставить его на проверку можно двумя способами:
- Локально и публиковать код в ваш репозиторий GitHub или BitBucket
- В онлайн-песочнице CodePen или Repl.it
Сама игра будет функционировать, когда вы окончательно реализуете все компоненты. Но чтобы понять, правильно ли реализован каждый из них, для каждого компонента дан пример кода и результат его работы, по которому вы можете проверить, правильно ли вы его реализовали. Сам код примеров в итоговом решении оставлять не рекомендуется.
Реализация в репозитории
Итогом будет наличие папки на локальном компьютере, в которой инициализирован git-репозиторий и настроена связь с репозиторием на GitHub или BitBucket.
- Разверните архив проекта в папку, созданную при подготовке репозитория.
- Ваш код пишите в файле ./game.js .
- Для запуска игры откройте в браузере файл ./index.html .
- Для запуска тестов откройте в браузере файл ./test/index.html .
Менять остальные файлы не рекомендуется.
Публикация промежуточных версий
- Добавьте к коммиту файл game.js командой git add game.js .
- Сделайте коммит git commit .
- Опубликуйте изменения с помощью команды git push .
Создание локального сервера (необязательно)
Все компоненты игры будут работать локально, кроме функции loadLevels , действия которой будут заблокированы политикой безопасности бразуера.
Один из вариантов обойти это — запустить локальный веб-сервер.
Локальный сервер на php
Локальный сервер на NodeJS
При использовании NodeJS тесты и игра будут обновляться автоматически при изменении файлов.
Данная статья планируется как пошаговый обзор создания простой JavaScript-игры класса “Ball and Paddle” на Canvas. Примерами такой игры могут послужить старые DOS-е игры наподобие таких - Ball and Paddle.
Пример кода из этой статьи взят из видео-курса достаточно известного Интернет-ресурса, посвященного фронтенд-разработке - Udemy.
Почему Canvas и почему игра? Лично для меня процесс познания JavaScript сильно облегчается благодаря Canvas - так интереснее. А создание игры на Canvas - это еще интереснее!
Итак, с чего начнем? Дальше в меру своих сил буду стараться детально пошагово рассказывать, что делает тот или иной кусок кода. И начнем с базового набора - создания Canvas.
Базовый Canvas
HTML-разметка страницы будет предельно простой:
В JavaScript’е создадим две глобальные переменные - одну для элемента Canvas, вторую - для 2d-контекста Canvas. Когда parser браузера построит DOM-дерево документа (событие ), инициализируем обе переменные, выполним проверку удачного получения 2d-контекста Canvas и если проверка будет пройдена успешно, то динамически зададим размеры Canvas:
Базовые элементы игры
Основа Canvas была создана в предыдущем шаге. В этом шаге создадим три фигуры, которые будут учавствовать в игре. Таковыми фигурами будут:
- фон игры
- мячик (ball)
- площадка (paddle)
Ниже я приведу JavaScript-код создания всех трех элементов, но сам код комментировать не буду, так как он очень простой и относится к основам Canvas:
Живой результат вышеприведенного кода можно посмотреть на этой странице - Lesson1-1. Это то, что должно получиться и что послужит заготовкой для игры.
Анимация мячика
В этом шаге предстоит сделать более интересные вещи. Во-первых, мы сделаем так, чтобы мячик начал двигаться как по-горизонтали, так и по-вертикали. А во-вторых, сделаем так, чтобы он вел себя как настоящий резиновый мячик - при ударе о стену отскакивал от нее и мчался в противоположном направлении.
Сделать это достаточно просто. Для этого нам понадобится одна из так называемых тайминговых функций JavaScript - . А также немного воображения.
Анимация мячика будем делать по-простому принципу, по которому делается любой мультфильм или кино - мячик будет отрисовываться с заданной частотой ( ), но каждый раз в новой позиции. В результате будет создаваться иллюзия его движения. Каждая новая позиция мячика - это его координата по оси X или Y с новым значением соответственно.
Чтобы мячик двигался достаточно быстро, изменять значения координат ( и ) мячика по оси X и Y будем с определенным шагом ( и ) - допустим, со значениями 5 или 6:
Эффект отскакивания от стенок (как резиновый мячик) обеспечивает проверка условий в участке кода:
Здесь все просто - при выполнении условия знак переменной или будет меняться на противоположный. В результате значение переменной или будет возрастать или уменьшаться. Как следствие, мячик будет двигаться в одну или в другую сторону.
Живой пример приведенного выше кода можно посмотреть и изучить на этой странице - Lesson1-2.
Двигаем paddle
В этом шаге нужно заставить двигаться paddle при помощи мыши. Для этого по событию внутри элемента Canvas будем получать значение X-координаты курсора мыши. И передавать это значение элементу paddle, его X-координате левого верхнего угла. Тем самым мы заставим paddle двигаться. За все эти действия будет отвечать функция :
Обратите внимание на последнюю строку функции - . Переменная необходима для того, чтобы при выходе за границы Canvas элемент paddle скрывался ровно на половину своей ширины.
Также не забудем создать переменные для paddle и передать их в код для отрисовки фигуры:
Живой пример приведенного выше кода можно посмотреть и изучить на этой странице - Lesson1-3. Подвигайте курсором мыши право-влево, чтобы увидеть эффект.
Мячик отскакивает от paddle
На этом этапе нужно сделать так, чтобы мячик отскакивал от paddle, когда последний оказывается на его пути. Выполнить эту задачу просто - ведь мячик уже отскакивает от “стен” Canvas. Следовательно, нужно научить мячик “видеть” еще и paddle.
Для этого сначала нужно опеределить внешние границы paddle - все его четыре стороны:
Когда значения всех сторон будут определены, то можно будет подставить эти значения в условие - и дело сделано:
Живой пример приведенного выше кода можно посмотреть и изучить на этой странице - Lesson1-4. Подвигайте курсором мыши право-влево и постарайтесь поймать мячик с помощью paddle, чтобы увидеть эффект.
Угол отскока мячика
В этом шаге сделаем так, чтобы наша игра смотрелась более правильной с точки зрения физики и обычной природы. То есть, при разном угле попадания на paddle мячик должен отскакивать от него с разной скоростью. Чем острее угол падения, тем с большей скоростью отскакивает от paddle мячик.
Решается эта задача несколькими строками кода:
В первой строке находится X-координата середины paddle. В строке определяется расстояние, на котором мячик соприкоснулся с paddle относительно его середины. В строке полученная дистанция присваивается шагу приращения по оси Х мячика - .
Логично предположить, что чем больше величина дистанции точки соприкосновения мячика относительно середины paddle, тем выше новая скорость движения мячика по-горизонтали. Чтобы эта скорость не была слишком высокой, ее необходимо уменьшить, умножив на 0.35, к примеру.
Живой пример приведенного выше кода можно посмотреть и изучить на этой странице - Lesson1-5.
Оптимизация кода
На данный момент наша задача по построению игры практически решена. Но остался один организационный момент.
Заключается он в том, что код необходимо реорганизовать в отдельные функции. Такой код будет читаться и поддерживаться значительно лучше.
Одна из таких функций уже была создана ранее - это функция . Давайте преобразуемся и весь оставшийся код подобным образом:
Готовый пример преобразованного в функции кода можно посмотреть на этой странице - Lesson1-6.
CMUS - изменить тему оформления
После того, как [CMUS][1] успешно установлен, хорошо было бы - изменить тему оформления - с той, которая есть по умолчанию.Это легко сдел. … Continue reading
Читайте также: