Контакты

Как сделать сетевую игру

Для начала установите зависимости. Создайте папку проекта, перейдите в неё и запустите следующий код:

Npm init npm install --save express socket.io

Для быстрой настройки сервера целесообразно использовать фреймворк Express , а для обработки веб-сокетов на сервере - пакет socket.io . В файл server.js поместите следующий код:

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

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

// Зависимости var express = require("express"); var http = require("http"); var path = require("path"); var socketIO = require("socket.io"); var app = express(); var server = http.Server(app); var io = socketIO(server); app.set("port", 5000); app.use("/static", express.static(__dirname + "/static")); // Маршруты app.get("/", function(request, response) { response.sendFile(path.join(__dirname, "index.html")); }); // Запуск сервера server.listen(5000, function() { console.log("Запускаю сервер на порте 5000"); });

Это довольно типичный код для сервера на связке Node.js + Express. Он устанавливает зависимости и основные маршруты сервера. Для этого демонстрационного приложения используется только один файл index.html и папка static . Создайте их в корневой папке проекта. Файл index.html довольно простой:

Наконец, противник будет уничтожен через 10 секунд. Теперь вы можете попробовать играть в игру теперь с врагами. Обратите внимание, что игра все еще не имеет обработки столкновений. Таким образом, вы не сможете стрелять в врагов. Последнее, что мы собираемся добавить в нашу игру, - это способность ударить врагов и, к сожалению, умереть им.

Первый используется для определения начального состояния объекта. Второй используется для обнаружения столкновений. Например, вражеский таг для кораблей будет «Врагом», а противником для врагов будет «Пуля». Таким образом, мы можем заставить корабли сталкиваться только с врагами, а враги сталкиваются только с пулями. Последний параметр будет использоваться для определения того, будет ли объект повторно респарирован или уничтожен после смерти. Вас отправят в комнату, где вы сможете увидеть других игроков, любые игры, доступные для участия, а также пообщаться с другими игроками.

Наша многопользовательская игра

Ваш пользовательский интерфейс может содержать куда больше элементов, поэтому для более крупных проектов CSS-стили лучше помещать в отдельный файл. Для простоты я оставлю CSS в коде HTML. Обратите внимание, что я включил в код скрипт socket.io.js . Он автоматически заработает в рамках пакета socket.io при запуске сервера.

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

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

Теперь нужно настроить веб-сокеты на сервере. В конец файла server.js добавьте:

// Обработчик веб-сокетов io.on("connection", function(socket) { });

Пока что в игре нет никаких функций, поэтому в обработчик веб-сокетов ничего добавлять не нужно. Для тестирования допишите следующие строки в конец файла server.js:

SetInterval(function() { io.sockets.emit("message", "hi!"); }, 1000);

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

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

Эта функция будет отправлять сообщение с именем message и содержимым hi всем подключенным веб-сокетам. Позже не забудьте удалить эту часть кода, так как она предназначена только для тестирования.

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

Вернитесь в диалоговое окно «Параметры сборки», которое должно быть открыто, и выберите «Сборка и запуск». На этом сайте Кристофер Ла Полло. . В этом уроке не будет слишком подробно рассказываться о том, как работают элементы управления, как игра знает, что вы закончили схему, или как поворачивается машина, поскольку это было охвачено двумя вышеупомянутыми учебниками, и это не имеет прямого отношения к вашей цели превратить это в многопользовательскую игру.

Пока вы тестируете игру на своем устройстве, вы также можете увидеть, что может предложить многопользовательский вариант. Ну, это было, по меньшей мере, недоумение. Но хорошая новость в том, что вы все исправите! Хотя переговоры о подключении к сети между устройствами за различными брандмауэрами могут показаться забавными для некоторых, большинство разработчиков предпочитают использовать сторонние библиотеки для обработки многих «сложных, но не забавных» задач для запуска многопользовательских игр.

Var socket = io(); socket.on("message", function(data) { console.log(data); });

Запустите сервер командой node server.js и в любом браузере перейдите по ссылке http://localhost:5000 . Если вы откроете окно разработчика (нажать правую кнопку мыши → Проверить (Inspect)), то увидите, как каждую секунду приходит новое сообщение:

Как правило, socket.emit(name, data) отправляет сообщение с заданным именем и данными серверу, если запрос идет от клиента, и наоборот, если запрос идет от сервера. Для получения сообщений по конкретному имени используется следующая команда:

  • Это бесплатно.
  • Плагин также бесплатный.
Да, скептически зрелый человек, это правда. Вы не будете реализовывать какие-либо из этих функций прямо сейчас, за исключением многопользовательского режима в режиме реального времени. Но держитесь со мной - как только вы пройдете эту часть, вы можете вернуться к написанию славного кода! Это идентифицирует ваше приложение с сервисом и позволяет выполнять важные действия, такие как вход в систему.

Создайте и запустите приложение; несколько раз проверьте процесс входа и выхода, чтобы убедиться, что он работает последовательно. Вариант выхода будет весьма полезен, так как вы тестируете свое приложение с несколькими игроками по дороге. Конечным результатом всей вашей тяжелой работы является то, что большая часть работы по настройке выполняется, и вы можете, наконец, обойти забавные части создания многопользовательской игры.

Socket.on("name", function(data) { // аргумент data может содержать любые отправляемые данные });

С помощью socket.emit() вы можете отправить любое сообщение. Можно также передавать объекты JSON, что для нас очень удобно. Это позволяет мгновенно передавать информацию в игре от сервера к клиенту и обратно, что является основой многопользовательской игры.

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

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

Теперь пусть клиент отправляет некоторые состояния клавиатуры. Поместите следующий код в конец файла static/game.js:

Var movement = { up: false, down: false, left: false, right: false } document.addEventListener("keydown", function(event) { switch (event.keyCode) { case 65: // A movement.left = true; break; case 87: // W movement.up = true; break; case 68: // D movement.right = true; break; case 83: // S movement.down = true; break; } }); document.addEventListener("keyup", function(event) { switch (event.keyCode) { case 65: // A movement.left = false; break; case 87: // W movement.up = false; break; case 68: // D movement.right = false; break; case 83: // S movement.down = false; break; } });

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

У вас есть варианты того, как это должно работать. Либо это время, либо когда определенный процент игроков нажали продолжить, он будет автоматически продолжен. Так, например, в случае 3-х человек, когда 2 нажали, он будет продолжен.



Убедитесь, что вы закрыли ненужные вещи. Хорошая «скорость загрузки» важна.

Это стандартный код, который позволяет отслеживать нажатие клавиш W , A , S , D . После этого добавьте сообщение, которое оповестит сервер о том, что в игре появился новый участник, и создайте цикл, который будет сообщать серверу о нажатии клавиш.

Socket.emit("new player"); setInterval(function() { socket.emit("movement", movement); }, 1000 / 60);

Эта часть кода позволит отправлять на сервер информацию о состоянии клавиатуры клиента 60 раз в секунду. Теперь необходимо прописать эту ситуацию со стороны сервера. В конец файла server.js добавьте следующие строки:

Выбор размера базы и лиг. При запуске игры вы можете выбрать меньшее количество лиг и игроков, чем меньше база данных, тем быстрее будет игра. Обычно загрузка только в лиге вашей игры достаточно для некоторых. Но многие по-прежнему хотят получить большое количество игроков в базе данных. Затем вы можете просто изменить базу данных, загрузить игроков из определенных лиг или стран.

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

Var players = {}; io.on("connection", function(socket) { socket.on("new player", function() { players = { x: 300, y: 300 }; }); socket.on("movement", function(data) { var player = players || {}; if (data.left) { player.x -= 5; } if (data.up) { player.y -= 5; } if (data.right) { player.x += 5; } if (data.down) { player.y += 5; } }); }); setInterval(function() { io.sockets.emit("state", players); }, 1000 / 60);

Давайте разберёмся с этим кодом. Вы будете хранить информацию о всех подключенных пользователях в виде объектов JSON. Так как у каждого подключённого к серверу сокета есть уникальный id, клавиша будет представлять собой id сокета подключённого игрока. Значение же будет другим объектом JSON, содержащим координаты x и y .

Лучший способ присоединиться или оставить игру. Рекомендуется установить дату возврата на «неопределенно», а затем использовать опцию «Отключить», чтобы выйти из игры. Нужна такая же патч. Не находясь на той же версии «Патч», которую вы не сможете подключить.

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

Когда сервер получит сообщение о том, что присоединился новый игрок, он добавит новый вход в объект игроков при помощи id сокета, который будет в этом сообщении. Когда сервер получит сообщение о движении, то обновит информацию об игроке, который связан с этим сокетом, если он существует.

io.sockets.emit() - это запрос, который будет отправлять сообщение и данные ВСЕМ подключённым сокетам. Сервер будет отправлять это состояние всем подключённым клиентам 60 раз в секунду.

Это означает, что ваши объекты должны сериализоваться на то, что может быть представлено в потоке байтов и записано в сокет. Запись в сокет легко; если вы можете записать в файл, который вы можете записать в сокет, это действительно не сложно. Итак, теперь вы можете отправлять данные, отлично, что теперь? Каждый раз, когда изменяется состояние игры, вы должны каким-то образом отправить обновление другим машинам. Здесь возникает архитектура клиент-сервер или одноранговое соединение, если вы хотите.

Клиент-сервер проще реализовать. Таким образом, ответственность сервера заключается в том, чтобы «владеть» всем состоянием игры. Только сервер может окончательно сказать, в каком состоянии находится объект. Если вы хотите переместить объект, вы сообщаете серверу, что вы хотите переместить, однако сервер сообщает вам, что вы должны перемещать объект, вы просто не делаете этого.

На данном этапе клиент ещё ничего не делает с этой информацией, поэтому добавьте со стороны клиента обработчик, который будет отображать данные от сервера в Canvas .

Var canvas = document.getElementById("canvas"); canvas.width = 800; canvas.height = 600; var context = canvas.getContext("2d"); socket.on("state", function(players) { context.clearRect(0, 0, 800, 600); context.fillStyle = "green"; for (var id in players) { var player = players; context.beginPath(); context.arc(player.x, player.y, 10, 0, 2 * Math.PI); context.fill(); } });

Итак, вы упомянули пошаговую игру, не так ли? Как только этот клиент делает то, что они хотят сделать, отправьте результаты этого перехода на сервер. Как только сервер обновляется, он отправляет сообщения каждому другому клиенту с новым состоянием мира, и эти клиенты применяют эти обновления. это в то же время, что и мировое обновление состояния на предыдущем шаге, все будет хорошо. Именно поэтому сервер обладает полномочиями по всему миру; если клиент пытается обмануть, сервер может повредить их.

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

Этот код обращается к id Canvas (#canvas) и рисует там. Каждый раз, когда от сервера будет поступать сообщение о состоянии, данные в Canvas будут обнуляться, и на нём в виде зеленых кружков будут заново отображаться все игроки.

Теперь каждый новый игрок сможет видеть состояние всех подключенных игроков на Canvas . Запустите сервер командой node server.js и откройте в браузере два окна. При переходе по ссылке http://localhost:5000 вы должны будете увидеть нечто похожее:

Таким образом, если вы отправляете два 100-байтовых пакета с двумя вызовами для отправки, вы можете получить один 200-байтовый пакет на один вызов для приема. Это нормально, поэтому вам нужно как-то справиться с этим. Один трюк состоит в том, чтобы сделать каждый пакет одинакового размера, а затем просто прочитать, что многие байты из сокета каждый раз, когда вы проверяете ввод. Имейте в виду, что вы также можете получать частичные сообщения. Например, если вы отправляете два сообщения по 100 байт, их можно объединить в одно 200-байтовое сообщение.

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

Вот и всё! Если у вас возникли проблемы, посмотрите архив с исходным кодом .

Некоторые тонкости

Когда будете разрабатывать более функциональную игру, целесообразно разделить код на несколько файлов.

Такие многопользовательские игры - отличный пример архитектуры MVC (модель-представление-контроллер). Вся логическая часть должна обрабатываться на сервере, а всё, что должен делать клиент - это отправлять входные пользовательские данные на сервер и отображать информацию, которую получает от сервера.

Однако в этом демо-проекте есть несколько недостатков. Обновление игры связано со слушателем сокета. Если бы я хотел повлиять на ход игры, то мог бы написать в консоли браузера следующее:

While (true) { socket.emit("movement", { left: true }); }

Теперь данные о движении будут отправляться на сервер в зависимости от характеристик компьютера более 60 раз в секунду. Это приведёт к тому, что игрок будет передвигаться невероятно быстро. Так мы переходим к концепции определения полномочного сервера.

Ни на каком этапе клиент не должен контролировать какие-либо данные на сервере. Например, никогда не нужно размещать на сервере код, который позволит клиенту определять своё положение/здоровье на основе данных, которые передаются через сокет, так как пользователь сможет легко подделать сообщение, исходящее из сокета, как показано выше.

Когда я создавал свою первую многопользовательскую игру, я написал код так, что игрок мог стрелять тогда, когда отправлялось сообщение о стрельбе, которое со стороны клиента было связано с нажатием кнопки мыши. Умелый игрок мог воспользоваться этим, вставив строчку на JavaScript, очень похожую на ту, что упоминалась выше, чтобы заполучить почти неограниченную скорость стрельбы.

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

В идеале циклы обновлений как у клиента, так и на сервере не должны зависеть от сокетов. Попытайтесь сделать так, чтобы обновления игры находились за пределами блока socket.on() . В противном случае вы можете получить много странных нелогичных действий из-за того, что обновление игры будет связано с обновлением сокета.

Кроме того, старайтесь избегать такого кода:

SetInterval(function() { // код... player.x += 5; // код... }, 1000 / 60);

В этом отрезке кода обновление координаты х для игрока связано с частотой смены кадров в игре. SetInterval() не всегда гарантирует соблюдение интервала, вместо этого напишите нечто подобное:

Var lastUpdateTime = (new Date()).getTime(); setInterval(function() { // код... var currentTime = (new Date()).getTime(); var timeDifference = currentTime - lastUpdateTime; player.x += 5 * timeDifference; lastUpdateTime = currentTime; }, 1000 / 60);

Это не так изящно, зато обеспечит более плавную и последовательную работу. Усложните демо-проект и попробуйте сделать так, чтобы обновление осуществлялось согласно времени, а не частоте смены кадров. Если не захотите на этом останавливаться, попытайтесь создать на сервере физический движок, который будет управлять движениями игроков.

Также можно сделать так, чтобы из игры удалялись отключенные игроки. Когда сокет отключается, автоматически отправляется сообщение о разъединении. Это можно прописать так:

Io.on("connection", function(socket) { // обработчик событий... socket.on("disconnect", function() { // удаляем отключившегося игрока }); }); ,

Понравилась статья? Поделитесь ей