Контакты

Создание игр на с по сети

Симулятор боёв на кораблях, который создал 17-летний Сергей Качмар.

Ученик ростовской школы не только запустил многопользовательскую игру, но и наладил прием платежей через «Единую кассу» Wallet One . Мы поговорили с ним о том, как найти время для создания собственной игры и как на этой игре можно заработать.

Привет! Где взять время на такой большой проект, когда есть куча других дел?

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

Чему нужно научиться, чтоб запустить свою игру?

Даже не знаю, могу ли я тут что-то советовать. Многие начинают с реализации как-то несложных игр, постепенно развивая свои проекты. Со временем люди получают более масштабные игры, как выросшие из маленьких, так и задуманные как крупные. С точки зрения конкретного обучения и того, что нужно осваивать - конечно же, языки программирования, причем серьезно. Я изучал Pascal, С# и HLSL. Движок написал самостоятельно. Стоит изучать дизайн, так как необходимо рисовать много элементов. Много чему приходится учиться, иногда в процессе.

Какой из этих вариантов твой? Расскажи, какие этапы были пройдены в создании и развитии игры?

Разработка этого проекта по сути являлась еще и обучением. Я открывал для себя новые возможности, постепенно создавая рендер, сетевую платформу, игровое ядро, работая с клиентской серверной частью. Разработку игры я начал в середине 2012 года, Первый прототип игры был создан в 2D, после началась работа над 3D-версией. И вот, 30 июня этого года началось бета-тестирование.

Кто тебе помогал?

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

Во что ты сам играешь? Изменились ли твои игровые предпочтения после запуска собственной игры?

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

Расскажи немного про то, как как можно зарабатывать на подобной игре?

Бизнес-модель - free-to-play, это значит, что скачать игру и играть можно абсолютно бесплатно, но в игре есть магазин различных платных предметов, причем в игре можно купить не столько платную валюту, сколько, например, различные дизайны для корабля и всякие полезные предметы.


Легко ли было разобраться с приемом платежей, учитывая что раньше у тебя опыта в этом не было?

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

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

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

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 довольно простой:

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

Ваш пользовательский интерфейс может содержать куда больше элементов, поэтому для более крупных проектов 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; } });

Это стандартный код, который позволяет отслеживать нажатие клавиш 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 вы должны будете увидеть нечто похожее:

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

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

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

Такие многопользовательские игры - отличный пример архитектуры 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() { // удаляем отключившегося игрока }); }); ,

Навигация по статье:

1. Введение. Основные понятия организации многопользовательских игр.

Как мы все знаем, для многопользовательской игры мы должны быть подключены к локальной сети или интернету. За счет которых клиент игры получает данные от сервера или от других игроков. Данные могут содержать информацию различного рода: позиция игрока, смена инвентаря, текст в чате, действия игроков и многое другое. Обычно данные отправляются "пачками", т.е. берутся определенные параметры (позиция игрока, угол поворота, выбранное оружие и т.д.) и располагаются в массиве в определенной последовательности, после чего отправляется весь массив. Это позволяет уменьшить пинг между игроками, т.к. частая отправка маленьких пакетов данных намного медленней чем редкая отправка массива данных. Для организации обмена данными между клиентами в основном используются две схемы: клиент-сервер, клиент-клиент.

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

Пример: к серверу подключено 10 игроков. Игрок 1 отправил серверу информацию, после чего сервер обработал ее и отправил эту информацию остальным 9-и игрокам.

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

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

Какой из представленных схем воспользоваться выбор ваш, но стоит помнить, что при использовании схемы клиент-сервер уменьшается нагрузка на клиент игры и позволяет серверу откорректировать данные (при необходимости) перед их отправкой остальным игрокам.

Для передачи данных есть несколько видов протоколов (TCP, UDP, IPX и т.д.), каждый из которых обладает своими особенностями. К примеру протокол UDP не гарантирует доставку данных и их порядок, поэтому придется проверять пришли данные или нет и корректировать их порядок. В свою очередь TCP протокол гарантирует доставку данных и их порядок, но имеет меньшую скорость чем UDP.

! Game Maker поддерживает следующие типы подключений: IPX, TCP/IP, Modem и Serial.

В данной статье будет рассмотрен TCP/IP набор сетевых протоколов, т.к. остальные виды, которые поддерживает Game Maker уже почти не используются или не используются вовсе. Сразу стоит оговориться, что он не создан для организации среднестатистических ММО игр, но для кооператива из 2-6 игроков или простой ММО игры вполне подойдет.

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