javascript - node.js 多人 html5 游戏中的延迟越来越高

标签 javascript node.js html express socket.io

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

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

$(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();
  });
});

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

这是更新后的代码:

客户:

$(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');
	});
});

服务器:

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();
});

非常感谢您的帮助:)

最佳答案

从客户端的渲染函数中移出以下代码。

//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;
});

关于javascript - node.js 多人 html5 游戏中的延迟越来越高,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30147421/

相关文章:

javascript - Foundation – 深层链接到 Accordion 选项卡?

javascript - 没有收到空白任务的警报

javascript - 为什么当我在最后一个 else 语句中更改 slaying 时,浏览器会崩溃

javascript - 如何在不丢失格式的情况下实现sql的代码折叠/折叠

javascript - 是否有可能确定特定算术运算是否导致溢出

angularjs - Passport 身份验证成功但不重定向并给出 500 错误

javascript - 修复响应式菜单父级切换 - jQuery/Javascript

javascript - 如何在 Node.js 中与 Net 建立持久连接?

node.js - 无法在不使用数据库触发器的情况下使用 firebase 云功能更新 firebase 数据库

html - 如何在媒体查询样式表旁边附加单独的样式表