node.js多人html5游戏

时间:2015-05-10 03:30:36

标签: javascript node.js html5 express socket.io

我使用node.js创建了一个网页,表达了js,socket.io和jquery。这是一个简单的游戏,其中玩家加入,给自己一个名字,然后在页面上的画布周围移动自己(正方形)。我注意到的是:当更多的人加入游戏并四处走动时,就会有足够的滞后让游戏无法播放(只有三个人连接。两个人并没有多少时间)。我无法弄清楚这是服务器端延迟还是客户端(这是我的第一个处理多人游戏的项目)。我正在服务器上进行所有位置计算,并将所有玩家对象的数组发送回每个套接字,以便每个客户端都可以呈现所有玩家。客户端仅发送输入并绘制玩家。

这是游戏的客户端脚本。这是我处理输入和渲染的地方。

$(document).ready(function()
{
	var socket = io.connect();

	var canvas = document.getElementById("canvas_html");
	var ctx = canvas.getContext("2d");
  
	canvas.width = 512;
	canvas.height = 480;
  
	document.body.appendChild(canvas);

	var player = 
	{
		id: '',
		is_it: false,
		x: canvas.width / 2,
		y: canvas.height / 2,
		velx: 0,
		vely: 0
	}
    
    //tell the server to initialize this client as a new player
	socket.emit('init_client', player);
    
	var client_player_list = [];
    
    //receive a list of the player objects from the server
	socket.on('load_players', function(players)
	{
		client_player_list = players;
	});

	var keysDown = {};

	addEventListener('keydown', function(e)
	{
		keysDown[e.keyCode] = true;
	}, false);

	addEventListener('keyup', function(e)
	{
		delete keysDown[e.keyCode];
	}, false);

	//take input from keys and send input to server
	var update = function()
	{
		if(87 in keysDown)//player holding w
			socket.emit('up');
		if(83 in keysDown)//player holding s
			socket.emit('down');
		if(65 in keysDown)//player holding a
			socket.emit('left');
		if(68 in keysDown)//player holding d
			socket.emit('right');
		if(74 in keysDown)//player holding j
			socket.emit('tag');
	};

	//render all players, update players when server updates
	var render = function()
	{
		//ctx.clearColor = "rgba(0, 0, 0, .3)";
		ctx.clearRect( 0, 0, canvas.width, canvas.height);
		ctx.fillStyle = "#079641";
		ctx.textAlign = 'center';
      
        //loop through the players array and render each one
		for(var i = 0; i < client_player_list.length; i++)
		{
            //if the player is_it, render them as red
			if(client_player_list[i].is_it)
			{	
				ctx.fillStyle = "#8F0E0E";
				ctx.fillRect(client_player_list[i].x, client_player_list[i].y, 20, 20);
                
                //draw the players name above them
				ctx.fillStyle = "#FFF";
				ctx.font="15px Arial";
				ctx.fillText(client_player_list[i].name, client_player_list[i].x + 8, client_player_list[i].y - 3);
				continue;
			}
            
            //if the player !is_it, render them as green
            ctx.fillStyle = "#079641";
			ctx.fillRect(client_player_list[i].x, client_player_list[i].y, 20, 20);
            
            //draw the players name above them
			ctx.fillStyle = "#FFF";
			ctx.font="15px Arial";
			ctx.fillText(client_player_list[i].name, client_player_list[i].x + 8, client_player_list[i].y - 3);
		}
         
        //when the server sends an update, replace the current players array with the one that the server just sent
		socket.on('sv_update', function(players)
		{
			client_player_list = players;
		});
	};

	//main loop
	var main = function()
	{
		setInterval(function()
		{
			update();
			render();
		}, 1000/60);
	};

	main();

	//trigger the disconnect event when a page refreshes or unloads
	$(window).bind('beforeunload', function()
	{
		socket.emit('disconnect');
	});
});

下面,我已经为我的游戏添加了app.js文件(服务器端脚本)。我不确定问题究竟在哪里,所以我想我只是发布了整个事情。

var express = require('express');
var http = require('http');
var io = require('socket.io', { rememberTransport: false, transports: ['WebSocket', 'Flash Socket', 'AJAX long-polling'] });

var app = express();
var server = http.createServer(app);
server.listen(8080);

app.use(express.static('public'));

io = io.listen(server);

//Declare variables for working with the client-side
var player_speed = 5;
var player_size = 20;
var vel_increment = 0.5;
var canvas_height = 480;
var canvas_width = 512;

//Declare list of players connected
var players = [];

io.sockets.on('connection', function(socket)
{
  var socket_id = socket.id;
  var player_index, player_exists = false;
  var this_player;

  var check_bounds = function()
  {
    //Keep player in the canvas
    if(this_player.y < 0)
        this_player.y = 0;
    if(this_player.y + player_size > canvas_height)
        this_player.y = canvas_height - player_size;

    if(this_player.x < 0)
      this_player.x = 0;
    if(this_player.x + player_size > canvas_width)
      this_player.x = canvas_width - player_size;

    //Keep velocity between -5 and 5
    if(this_player.vely > player_speed)
      this_player.vely = player_speed;
    if(this_player.velx > player_speed)
      this_player.velx = player_speed;

    if(this_player.vely < -player_speed)
      this_player.vely = -player_speed;
    if(this_player.velx < -player_speed)
      this_player.velx = -player_speed;
  };

  var sv_update = function()
  {
    io.sockets.emit('sv_update', players);
    if(player_exists)
    {
      if(players.length == 1)
        this_player.is_it = true;
      check_bounds();
    }
  };
  
  //When a client connects, add them to players[]
  //Then update all clients
  socket.on('init_client', function(player)
  {
    player.id = socket.id;
    players.push(player);

    for(var i = 0; i < players.length; i++)
      if(players[i].id == socket_id)
        player_index = i;
    player_exists = true;
    this_player = players[player_index];

    sv_update();
    socket.emit('load_players', players);

    console.log(players);
  });

  //====================CHAT==========================//
  var address = socket.request.connection.remoteAddress;

  socket.on('new user', function(data, callback)
  {
    if(player_exists)
    {
      this_player.name = data;
      console.log(address + " has connected as '" + data + "'.");
      
      callback();
    }
  });

  socket.on('send message', function(data)
  {
    io.sockets.emit('broadcast', this_player.name, data);
  });
  //====================CHAT==========================//
  
  //if player is_it and is within another player, hitting 'j' will make the other player is_it. 
  socket.on('tag', function()
  {
    if(player_exists)
    {
      for(var i = 0; i < players.length; i++)
      {
        if((this_player.x + player_size >= players[i].x && this_player.x + player_size <= players[i].x + player_size )|| 
          (this_player.x <= players[i].x + player_size && this_player.x + player_size >= players[i].x))
          if((this_player.y + player_size >= players[i].y && this_player.y + player_size <= players[i].y + player_size )|| 
          (this_player.y <= players[i].y + player_size && this_player.y + player_size >= players[i].y))
          {
            if(this_player.is_it)
            {
              this_player.is_it = false;
              players[i].is_it = true;
            }
          }
      }
      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('up', function()
  {
    if(player_exists)
    {
      this_player.y -= player_speed;

      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('down', function()
  {
    if(player_exists)
    {
      this_player.y += player_speed;

      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('left', function()
  {
    if(player_exists)
    {
     this_player.x -= player_speed;

      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('right', function()
  {
    if(player_exists)
    {
      this_player.x += player_speed;

      sv_update();
    }
  });

  //When a player disconnects, remove them from players[]
  //Then update all clients
  socket.on('disconnect', function()
  {
    for(var i = 0; i < players.length; i++)
    {
      if(players[i].id == socket.id)
      {
        players.splice(i, 1);
      }
    }

    sv_update();
  });
});

----------------------------------------------- ----------编辑--------------------------------------- ---------------------------我采用了RuslanasBalčiūnas的建议,将客户端中的sv_update处理程序移出render函数。这会阻止玩家滞后,但新问题是服务器没有为所有玩家发送足够的更新,以便在任何给定的客户端上流畅地移动。每个客户似乎只为自己运行顺利,但其他客户认为它们不稳定/滞后。

以下是更新后的代码:

客户端:

$(document).ready(function()
{
	var socket = io.connect();

	var canvas = document.getElementById("canvas_html");
	var ctx = canvas.getContext("2d");
	canvas.width = 512;
	canvas.height = 480;

	document.body.appendChild(canvas);

	var player = 
	{
		id: '',
		name: '',
		is_it: false,
		x: canvas.width / 2,
		y: canvas.height / 2,
		velx: 0,
		vely: 0
	};

	var client_player_list = [];
	socket.on('load_players', function(players)
	{
		client_player_list = players;
	});

	var keysDown = {};

	addEventListener('keydown', function(e)
	{
		keysDown[e.keyCode] = true;
	}, false);

	addEventListener('keyup', function(e)
	{
		delete keysDown[e.keyCode];
	}, false);

	//take input from keys and send input to server
	var update = function()
	{
		if(87 in keysDown)//player holding w
			socket.emit('input', 'up');
		if(83 in keysDown)//player holding s
			socket.emit('input', 'down');
		if(65 in keysDown)//player holding a
			socket.emit('input', 'left');
		if(68 in keysDown)//player holding d
			socket.emit('input', 'right');
		if(74 in keysDown)
			socket.emit('input', 'tag');
	};

	//render all players, update players when server updates
	var render = function()
	{
		//ctx.clearColor = "rgba(0, 0, 0, .3)";
		ctx.clearRect( 0, 0, canvas.width, canvas.height);
		ctx.fillStyle = "#079641";
		ctx.textAlign = 'center';

		for(var i = 0; i < client_player_list.length; i++)
		{
			if(client_player_list[i].is_it)
				ctx.fillStyle = "#8F0E0E";
			else
				ctx.fillStyle = "#079641";

			ctx.fillRect(client_player_list[i].x, client_player_list[i].y, 25, 25);

			ctx.fillStyle = "#FFF";
			ctx.font="15px Arial";
			ctx.fillText(client_player_list[i].name, client_player_list[i].x + 8, client_player_list[i].y - 3);
		}
	};

	socket.on('sv_update', function(players)
	{
		client_player_list = players;
	});

	//main loop
	var main = function()
	{
		setInterval(function()
		{
			update();
			render();
		}, 1000/60);
	};

	main();

	//---- Chat stuff ----
	var toggle = 1;

	$('#users').fadeIn(1000);
	$('#name_field').focus();

	$('#user_form').submit(function(e)
	{
		console.log('ezpz');
		e.preventDefault();
		socket.emit('init_client', player, $('#name_field').val(), function()
		{
			$('#users').fadeOut('slow');
			window.setTimeout(function(){$('#chat').fadeIn('slow');$('#canvas_html').fadeIn('slow');$('#info').fadeIn('slow')}, 1000);
		});
	});

	$('#desk').submit(function(e)
	{
		e.preventDefault();
		socket.emit('send message', $('#message').val());
		$('#message').val('');
	});

	socket.on('broadcast', function(name, data)
	{
		$('#message_window').append('<p class="p' + toggle + '">' + name + ": " + data + '</p>');
		$('#message_window')[0].scrollTop = $('#message_window')[0].scrollHeight;

		if(toggle == 1)
			toggle = 2;
		else
			toggle = 1;
	});

	//trigger the disconnect event when a page refreshes or unloads
	$(window).bind('beforeunload', function()
	{
		socket.emit('disconnect');
	});
});

SERVER:

var express = require('express');
var http = require('http');
var io = require('socket.io', { rememberTransport: false, transports: ['WebSocket', 'Flash Socket', 'AJAX long-polling'] });

var app = express();
var server = http.createServer(app);
server.listen(8080);

app.use(express.static('public'));

io = io.listen(server);

//Declare variables for working with the client-side
var player_speed = 5;
var player_size = 25;
var canvas_height = 480;
var canvas_width = 512;

//Declare list of players connected
var players = [];

io.sockets.on('connection', function(socket)
{
  var socket_id = socket.id;
  var player_index, this_player, player_exists = false;

  //When a client connects, add them to players[]
  //Then update all clients
  socket.on('init_client', function(player, name, callback)
  {
    player.id = socket.id;
    players.push(player);

    for(var i = 0; i < players.length; i++)
      if(players[i].id == socket_id)
        player_index = i;
    player_exists = true;
    this_player = players[player_index];

    this_player.name = name;
    callback();

    sv_update();
    socket.emit('load_players', players);
  });

  socket.on('send message', function(data)
  {
    io.sockets.emit('broadcast', this_player.name, data);
  });

  //Gather key input from users...
  socket.on('input', function(key)
  {
    if(player_exists)
    {
      if(key == 'up')
        this_player.y -= player_speed;
      else if(key == 'down')
        this_player.y += player_speed;
      else if(key == 'left')
        this_player.x -= player_speed;
      else if(key == 'right')
        this_player.x += player_speed;
      else if(key == 'tag')
        for(var i = 0; i < players.length; i++)
          if(can_tag(players[i]))
          {
            this_player.is_it = false;
            players[i].is_it = true;
          }

      sv_update();
    }
  });

  //When a player disconnects, remove them from players[]
  //Then update all clients
  socket.on('disconnect', function()
  {
    for(var i = 0; i < players.length; i++)
      if(players[i].id == socket.id)
        players.splice(i, 1);

    sv_update();
  });

  var check_bounds = function()
  {
    //Keep player in the canvas
    if(this_player.y < 0)
        this_player.y = 0;
    if(this_player.y + player_size > canvas_height)
        this_player.y = canvas_height - player_size;

    if(this_player.x < 0)
      this_player.x = 0;
    if(this_player.x + player_size > canvas_width)
      this_player.x = canvas_width - player_size;
  };

  var sv_update = function()
  {
    io.sockets.emit('sv_update', players);
    if(player_exists)
    {
      if(players.length == 1)
        this_player.is_it = true;
      check_bounds();
    }
  };

  var can_tag = function(target)
  {
    if(this_player.x < target.x + player_size && this_player.x + player_size > target.x && this_player.y < target.y + player_size && player_size + this_player.y > target.y && this_player.is_it)
      return true;
  }

  sv_update();
});

非常感谢您的帮助:)

1 个答案:

答案 0 :(得分:0)

从客户端的渲染功能移动下面的代码。

//when the server sends an update, replace the current players array with the one that the server just sent
socket.on('sv_update', function(players)
{
    client_player_list = players;
});