Создание сетевой игры с помощью Collagen_2, Node.js и библиотеки socket.js

a03ae6b0924494ecaa59761225b4a477.png

В данной статье будет описан простой способ создания сетевой онлайн мини игры на подобии небольшой чат комнаты. Игроки могут передвигаться по полю игры, прятаться за деревьями, также есть возможность управлять камерой вида. Для тестирования игры необходимо скачать редактор зайти в папку collagen_2/games/game_3, ввести в командной строке forever start app.js. Для работы игры требуются модули socket.js и forever (глобальная инсталяция).

Создание сцены игры

Для создания сцены необходимо подготовить все спрайты: фоновой подложки, деревьев, персонажа с покадровой анимацией, затем сохранить их. Далее перейти в редактор сцены, для это нажать кнопку code синего цвета, затем кнопку test. Загрузить фоновое изображение размером которым предполагается размер сцены, загрузить все спрайты. Разместить спрайты бекгроунда, деревьев и персонажа на сцене. Перемещение камеры кнопками a, w, s, d. Далее добавить спрайты бекгроуда кнопкой add tile bg — синего цвета, спрайты сцены и персонажа кнопкой add tile.

Один и тот-же спрайт можно использовать для нескольких одинаковых объектов сцены, например деревьев, для этого переместить спрайт в новое положение и снова нажать кнопку add tile или add tile bg. Id объекта сцены можно изменить, также можно удалить ненужный объект из массивов tile_common и tilt_bg, после чего нажать на кнопку update зеленого цвета. В тестовом редакторе также можно писать код для тестирования анимации непосредственно в браузере. После подготовки сцены — сохранить ее в папке для игр collagen_2/games/game_name в json файл нажав кнопку save project — в низу панелей с кнопками.

Создание основной логики игры клиентской части

В файле index.html указываем путь к основному скрипту игры — …/…/…/collagen_2/games/game_name/game_name.js, в файле game_name.js указываем путь к json файлу сцены в переменной gameUrl, также меняем название папки игры в файле app.js.

Далее создаем логику игры в файле game_name.js:

//////////////переопределяем размер карты смещения камеры вида
 var maxTranslate = [-1000, -0];
//адрес json файла спрайтов
var gameUrl = "http://localhost:3000/collagen_2/games/game_1/game_1.json";
var personageId = "personage";


//анимация сцены
function apply_code(){
	        //обновляем переменные для вколючения анимации
	  			mode = "animation";
				if (modules.animation)modules.animation.isOff = true;
				updateCommonTiles(HM.$props().sprites);
				updateBgTiles(HM.$props().sprites);
				//режим code чтобы обновлять только Tiles
				HM.$$("emiter-operation-with").set("code");
				

///создаем анимацию персонажа				
modules.personage = null;

//находим объект персонажа в массиве tiles_common созданном в test 
for(var i=0; i arg1 &&  modules.personage.frame_index < arg2){ 
            modules.personage.nextFrame();}else{
            modules.personage.nextFrame(arg3);
		}
		modules.personage.move(arg4, arg5);		
}

//запуск цикла анимации обновления фоновых и основных спрайтов, в том числе персонажа
modules.animation=animationLopLayer(tiles_bg,tiles_common, 40);
				
}


///функция загрузки json файла, создания спрайтов и объектов сцены - Tile
fetch(gameUrl)
  .then((response) => {
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return response.json();
  })
  .then((json) => {	  
	  //console.log(json)
	    var dataURL = 'data:image/png;base64,' + json.backImg;
	    var context  = HM; //ссылка на корень приложения
                       /// tiles_bg_save, tiles_common_save - временные массивы 
                      ///для хранения промежуточных данных - файл js/games/main_games.js
	                    tiles_common_save = JSON.parse(json.tiles_common_save);				
				        tiles_bg_save = JSON.parse(json.tiles_bg_save); 						
						mainImgScale_x = 1;
						mainImgScale_y = 1; 
						img.src = dataURL;
                        ///обновляем фоновую картинку
						img.onload = function(){ 		
							startImg();	
						}
                        ///создание спрайтов на которые ссылаются объекты сцены и 
                        ///бекгроунда при включении анимации
						for(var key in  json.sprites){
							   var sprite = createFromPC(key, context, false, json.sprites[key]);
							   if(sprite)context.$$("emiter-create-sprite").set(key);									
						}
                //создание бекгроунда               
				tiles_bg = [];		
				for(var i=0; i console.error(`Fetch problem: ${err.message}`));

Создание основной логики анимации сцены и персонажа закончена, протестировать результат можно в папке collagen_2/games/game_1.

Создание серверной составляющей игры

Создание сервера.

Для создания серверной части была использована библиотека socket.js и модуль forever для автоматического запуска сервера.

//при выходе игрока сервер перезагружается 
/// для корректной работы использовать модуль forever.  Запуск сервера:   forever start app.js	

var users = {}; ///координаты и текущий кадр анимации пользователей
var socketjs = require('socket.js'); 

///////связь с другими клиентами
////данные анимации всех игроков
socketjs(server, function(socket, reconnectData) {
	
   console.log(reconnectData);
   
  //подключение нового пользователя
  if (reconnectData === null) {
    console.log('A user connected.');
  } else {
    console.log('A user reconnected with: ', reconnectData);
  }
 
  ///новый игрок обновляем users
   socket.receive('newuser', function(message) {
	users[message.id] = message;
	///console.log(users);
  });
  
  
  // сообщения с клиента
  socket.receive('coord', function(message) {
	users[message.id] = message;  
    //console.log('Received:', message);
  });

  //сообщения игрокам каждые 100ms
  var interval = setInterval(function() {
		socket.send('coord', users);
  }, 100);




  // if the client disconnects, stop sending messages to it
  socket.close(function(data) {
	console.log(data);  
    console.log('A user disconnected.');
    clearInterval(interval);
	return data;
  });
});

Добавление соккет составляющей в файл клиента.

//добавляем переменные
//уникальное id для передачи данных на сервер (т.к. персонажи одинаковые для всех)
var userId = "user_" + Math.floor(Math.random() * 1000);
///изначальное количество объектов сцены
var numTiles = 0;
///начальное количество игроков
var numUsers = 1;

///добавляем запуск функций в fetch функцию загрузки json сцены
                ///обновляем количество объектов сцены				
				numTiles = tiles_common.length;
				///включение анимации
				apply_code();
				 createSocket();
				//console.log(tiles_bg, tiles_common);




///создаем логику сокет соединения
function createSocket(){			
///соединение с другими игроками
  if (socketjs.isSupported()) {
  // connect to the server
  var socket = socketjs.connect();
  
  ///первое сообщение   на сервер с координатами при загрузке 
  socket.send('newuser', {id: userId, point: modules.personage.point, frame_index: modules.personage.frame_index});

  // log a message if we get disconnected
  socket.disconnect(function(data) {
    console.log('Temporarily disconnected.');
  });

  // log a message when we reconnect
  socket.reconnect(function() {
    console.log('Reconnected.');

    // whatever we return here is sent back to the server
    return 'reconnected';
  });
  
 /////////////////////////////////////////////////////////////////////////////////////////// 
    // корординаты игроков с сервера 
  socket.receive('coord', function(data) {
	 //обновляем объекты сцены в случае добавления или выхода игрока 
	 if(Object.keys(data).length != numUsers){
		//удаляем все объекты сцены
		tiles_common.splice(0, tiles_common.length);
		//создаем исходные обекты
		 for(var i=0; i

Серверная часть готова, рабочий пример можно протестировать в папке collagen_2/games/game_2.

Создание чат сообщений

Создадим новый спрайт-диалог для отображения сообщений

3132ea62bc4bff0e754e4baf5e82532e.png

Назовем его message, далее откроем редактор в тестовом режиме и добавим спрайт в предыдущий проект кнопкой add tile, изменим id на msg, нажмем update, затем сохраним проект в папке иры game_3.

Отредактируем пути к ресурсам проекта в файлах index.htm, app.js, game_3.js. Далее добавим html разметку для формы сообщений в файл index.html.


Добавим javascript код в файл game_3.js

  //отправляем координаты и текущий кадр на сервер каждые  100ms
  ///отредактируем метод:
  var interval = setInterval(function() {
                                                                                                                  
    socket.send('coord', {id: userId, point: modules.personage.point,
                           frame_index: modules.personage.frame_index,
                          ///добавляем сообщение игрокам
                          msg: modules.personage.message});
  }, 100);





///функция обновления координат и текущего кадра персонажа
 function updateUsers(users){
	/// console.log(users);
	 for(var i=0; i< tiles_common.length; i++ ){
		 if(users[tiles_common[i].id] && tiles_common[i].id != userId ){
			tiles_common[i].point[0] = users[tiles_common[i].id].point[0];
			tiles_common[i].point[1] = users[tiles_common[i].id].point[1];
			tiles_common[i].nextFrame(users[tiles_common[i].id].frame_index);
			
			///добавляем сообщение от игроков
			tiles_common[i].message = users[tiles_common[i].id].msg;
		 }		 
	 }	
}





/////////////////////////////////////Сообщения игороков
///Переопределяем метод pender из файла /test/tiles.js
Tile.prototype.render_ = function(){
	if(!this.show)return;				
    ctx.drawImage(this.frame, this.point[0], this.point[1], this.width, this.height);
	
	//отображаем спрайт с сообщением
    if(this.message){

		var spiteMsg = HM.$props().sprites["message"];
		spiteMsg.show = true;
		///отображаем сообщение справа вверху от персонажа
		spiteMsg.point[0] = this.point[0]+50; 
		spiteMsg.point[1] = this.point[1]-70;
		///нижняя правая точка спрайта
		spiteMsg.point2[0] = spiteMsg.point[0]+spiteMsg.width;
		spiteMsg.point2[1] = spiteMsg.point[1]+spiteMsg.height;
		//console.log()
	    //настраиваем параметры текстового сообщения - отступы, шрифт, размер	
		spiteMsg.textParam = {
			text: this.message, 
			lineHeight: 15, 
			font: "15px Balsamiq Sans",
			fillStyle: "black",
			padding_x_l: 5, 
			padding_x_r: 5,		
			padding_x: false, 
			padding_y: 5, 
			max_width: false, 
			textArr: false,		
		}
	    spiteMsg.render_();
	}	
}
///переопределяем метод render_ спрайта для отрисовки сообщений /js/sprites
///убираем все лишнее для более быстрой работы, добаляем функцию для отображения текста.

CollageSprite.prototype.render_ = function(){
	if(!this.show)return;			
	ctx.drawImage(this.frame, this.point[0], this.point[1], this.width, this.height);
	if(this.textParam)this.fillText(this.point[0], this.point[1]);	
}

///добавляем контейнер с формой в для отправки сообщений в объект StateMap  /js/games/inteface_games.js
 StateMap.user_message = { //канвас	
		container: "user_message",
		props: [["user_msg_btn", "mousedown", "[name='user_msg_btn']"], 
                ["user_msg", "inputvalue", "[name='user_msg']"],				 
		       ],
		methods: {
			user_msg_btn: function(){
			    var text  = this.props("user_msg").getProp(); 
				////тестовое сообщение
                modules.personage.message = text;
				//console.log(text);
            }
		}			
		
 }

Мини чат готов, рабочий пример можно протестировать в папке collagen_2/games/game_3. Чат работает только на транслите, т.к. socket.js не поддерживает кирилицу.

© Habrahabr.ru