在移动(多人游戏)

时间:2015-12-09 19:25:05

标签: javascript node.js canvas socket.io

计划

我决定开始尝试Node.js和Socket.io,并期待制作一个游戏(或者只是一个将绘制并将每个玩家显示为一个块的大厅)。每次客户端加入时,服务器都应该使用该类并使用服务器给出的播放器详细信息实例化一个新对象(随机)。

然后服务器将使用socket.io并将新对象发送到连接的客户端,以便他们可以拥有该对象,然后能够使用它(在这种情况下移动)。

客户端应该做的第一件事是将所有用户绘制到画布上,然后将当前客户端绘制到画布上。

问题

我已经到了事情正在发挥作用的地步,服务器创建一个新的播放器对象,然后将其发送到客户端,然后客户端将能够将自己绘制到具有属性的画布,但我似乎无法客户端被发送到其他用户的画布上,直到我移动播放器。

当我打开多个浏览器标签时,它按预期工作,绘制客户端,如下所示:

enter image description here

但是当我移动其中一个然后一个客户端确实被成功吸引到其他客户端但其他客户端都失去了那个时刻给他们的对象,直到他们移动然后它在其他地方成功移动但是然后另一个客户都失去了屏幕状态,你可以看到它们的位置。简而言之,它们都在屏幕上,并相应地移动到所有视图中的正确位置,但是当它发生时你永远不会看到所有玩家它一次只能显示一个玩家,那就是移动的那个(在你第一次加入你的地方之后)也只有看到自己,直到有人移动,但是你完成后才看到所有的球员只有最后一个移动,直到有人或你移动)。 查看下面的图片

enter image description here

上面显示我关注中间标签并移动,日志显示发生了什么(注意其他玩家全部消失,只有移动的玩家显示)

守则

我只有2个我正在使用的文件,它们的填充方式如下。

服务器 - nodejs

var express = require('express'),
    app = express(),
    http = require('http').createServer(app),
    io = require('socket.io').listen(http);

http.listen(8000);

//server connections and routing
app.use(express.static(__dirname + '/public'), function(request, response){
    if(request.url !== '/public') {
        response.sendFile( __dirname +'/error/index.html');
        console.log('Error 404 request, User tried accessing: ' + __dirname + request.url);
    }
});

var players = [];

//Lets create a function which will help us to create multiple players
function newPlayer() {
    this.name;
    this.id = 1;
    this.x = Math.random() * 500;
    this.y =  Math.random() * 500;
    //Random colors
    var r = Math.random()*255>>0;
    var g = Math.random()*255>>0;
    var b = Math.random()*255>>0;
    this.color = "rgba(" + r + ", " + g + ", " + b + ", 0.5)";

    //Random size
    this.radius = Math.random()*20+20;
    this.speed =  5;

    return {'name' : this.name,"x" : this.x,"y" : this.y,"color" : this.color, "radius" : this.radius,"speed" : this.speed}
}

//calls to the server and tracking connection of each new user
io.sockets.on('connection', function(socket){
    var currentPlayer = new newPlayer(); //new player made
    players.push(currentPlayer); //push player object into array

    //create the player
    socket.broadcast.emit('createUsers', players);
    socket.emit('currentUser', currentPlayer);

    //user moved
    socket.on('moved', function(data){
        console.log(data);
        socket.broadcast.emit('moving', data);
    });

    //disconnected
    socket.on('disconnect', function(){
        var i = players.indexOf(currentPlayer);
        players.splice(i, 1);
        socket.broadcast.emit('user left','User: ' + currentPlayer.name + ' Left');
        console.log(players);
    });

});

console.log('NodeJS Server started on port 8000...');

客户端 - HTML

<!doctype html>
<html>
<head>
    <title>Game Dev JS - 1</title>
    <link href="./ui.css" rel="stylesheet">
</head>
<body>
    <canvas id="canvas" width="500" height="500">
        Your Browser Does Not Support Canvas and HTML5
    </canvas>
</body>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
    <script type="text/javascript" src="/js/engine.js"></script>

</html>

客户端 - CSS

body{
    margin: 0 auto;
    width: auto;
    background: #fff;
    text-align: center;
}

#canvas {
    margin: 15px;
    background: #000;
}

客户端 - Javascript

var socket = io.connect(); //add socket object

//initializing the canvas
var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext('2d'),
    W = window.innerWidth,
    H = window.innerHeight;

var keys = {};

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

//check if ke is not being pressed or has lifted up
window.addEventListener('keyup', function(e){
    delete keys[e.keyCode];
}, false);

socket.on('currentUser', function(newUser){
    ctx.fillStyle = newUser.color;
    ctx.fillRect(newUser.x, newUser.y, 25, 25);

    function update(){
        //moving player
        if(keys[38]){
            newUser.y -= newUser.speed;
            socket.emit('moved', newUser);
            console.clear();
            console.log('You Moving');
            console.log(newUser);
            ctx.clearRect(0, 0, W, H);
            ctx.fillStyle = newUser.color;
            ctx.fillRect(newUser.x, newUser.y, 25, 25);
        } 
        if(keys[40]){
            newUser.y += newUser.speed;
            socket.emit('moved', newUser);
            console.clear();
            console.log('You Moving');
            console.log(newUser);
            ctx.clearRect(0, 0, W, H);
            ctx.fillStyle = newUser.color;
            ctx.fillRect(newUser.x, newUser.y, 25, 25);
        } 
        if(keys[37]){
            newUser.x -= newUser.speed;
            socket.emit('moved', newUser);
            console.clear();
            console.log('You Moving');
            console.log(newUser);
            ctx.clearRect(0, 0, W, H);
            ctx.fillStyle = newUser.color;
            ctx.fillRect(newUser.x, newUser.y, 25, 25);
        } 
        if(keys[39]){
            newUser.x += newUser.speed;
            socket.emit('moved', newUser);
            console.clear();
            console.log('You Moving');
            console.log(newUser);
            ctx.clearRect(0, 0, W, H);
            ctx.fillStyle = newUser.color;
            ctx.fillRect(newUser.x, newUser.y, 25, 25);
        }
        window.requestAnimationFrame(update);
    }
    update();
});

//moving player and updating on other clients
socket.on('moving', function(moveTo){
    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = moveTo.color;
    ctx.fillRect(moveTo.x, moveTo.y, 25, 25);
    console.clear();
    console.log('A Player Moved');
    console.log(moveTo);
});

备注

我会把它托管在一个小提琴上供你玩,但我不知道任何可以让我用node.js服务器进行测试并允许你同时查看代码JS小提琴。我还是Node和Socket.io的新手,这是我的第一次尝试,任何帮助对我来说都是奖金。一旦主要功能正常,我将努力重构代码。

如果您安装了npm并安装了socket.io,那么您应该可以复制并粘贴它,它应该可以正常工作。我的文件夹结构如下 - 这没有任何问题,因为一切正确加载

enter image description here

提前致谢

2 个答案:

答案 0 :(得分:1)

moving事件的客户端侦听器只允许客户端一次绘制一个玩家的位置。您清除整个屏幕,删除所有其他位置,但之后只重新绘制移动的玩家。

我建议阅读Robert Nystrom的优秀游戏编程模式电子书http://gameprogrammingpatterns.com/contents.html。它没有涵盖这个特定的情况,但你可以从观察者,游戏循环和更新的部分推断出很多。在您的情况下,您可能希望服务器运行权威的游戏循环和状态,使用观察者来更新客户端/服务器之间的状态,以及使用他们自己的独立游戏循环向用户显示所述状态的客户端。

答案 1 :(得分:1)

因为我从未得到过回复来帮助验证我的问题。我设法阅读了这本书(或上面@JCD的部分内容)并设法查看了其他应用程序以及他们如何使用节点管理他们的应用程序。

我知道在节点服务器上进行任何计算都不是好习惯,因此我重新查看了我的客户端和服务器的代码并进行了更新。

<强>判决

客户端:

我决定让客户端只显示当前客户端及其属性以及连接到服务器的其他客户端。使用循环,客户端将循环遍历由服务器生成的JSON对象数组(连接到服务器的所有套接字)。然后,客户端将使用画布将对象绘制到画布。当新客户端加入此阵列时,阵列也将自动更新,使客户端自动出现在正确位置的其他客户端上。我正在使用请求动画帧来检查客户端是否按下了一个键然后它发送并更新了客户端属性,而服务器只是相应地广播(客户端将循环绘制到画布的更新数组)。

服务器端:

服务器将负责等待客户端向服务器发送密钥代码,如果满足条件,它将使用新属性更新分配给该客户端的对象(当正确的密钥到来时移动through - 通过RequestAnimationFrame()发送到服务器。

<强>代码:

客户端(HTML - 无变化):

<!doctype html>
<html>
<head>
    <title>Game Dev JS - 1</title>
    <link href="./ui.css" rel="stylesheet">
</head>
<body>
    <canvas id="canvas" width="500" height="500">
        Your Browser Does Not Support Canvas and HTML5
    </canvas>
</body>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
    <script type="text/javascript" src="/js/engine.js"></script>
</html>

客户端(JS - 更新):

var socket = io.connect(); //add socket object

//initializing the canvas
var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext('2d'),
    W = window.innerWidth,
    H = window.innerHeight;

var keys = {};

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

//check if key is not being pressed or has lifted up
window.addEventListener('keyup', function(e){
    delete keys[e.keyCode];
}, false);

//game loop to make the game smoother
function gameLoop() {
    if(keys[38]) {
        socket.emit('pressed', 38);
        console.log('You are UP');
    }
    if(keys[40]) {
        socket.emit('pressed', 40);
        console.log('You are DOWN');
    }
    if(keys[37]) {
        socket.emit('pressed', 37);
        console.log('You are LEFT');
    }
    if(keys[39]) {
        socket.emit('pressed', 39);
        console.log('You are RIGHT');
    }
    window.requestAnimationFrame(gameLoop);
}
window.requestAnimationFrame(gameLoop);

//the connected user joins and gets all the players on server
socket.on('welcome', function(currentUser, currentUsers){
    console.log(currentUser);

    ctx.globalCompositeOperation = "source-over";
    //Lets reduce the opacity of the BG paint to give the final touch
    ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
    ctx.fillRect(0, 0, W, H);

    //Lets blend the particle with the BG
    ctx.globalCompositeOperation = "lighter";

    //players in lobby
    for(var i = 0; i < currentUsers.length; i++){

        ctx.beginPath();

        //Time for some colors
        var gradient = ctx.createRadialGradient(currentUsers[i].x, currentUsers[i].y, 0, currentUsers[i].x, currentUsers[i].y, currentUsers[i].radius);
        gradient.addColorStop(0, "white");
        gradient.addColorStop(0.4, "white");
        gradient.addColorStop(0.4, currentUsers[i].color);
        gradient.addColorStop(1, "black");

        ctx.fillStyle = gradient;
        ctx.arc(currentUsers[i].x, currentUsers[i].y, currentUsers[i].radius, Math.PI*2, false);
        ctx.fill();
    }

    //player
    ctx.beginPath();
    //Time for some colors
    var gradient = ctx.createRadialGradient(currentUser.x, currentUser.y, 0, currentUser.x, currentUser.y, currentUser.radius);
    gradient.addColorStop(0, "white");
    gradient.addColorStop(0.4, "white");
    gradient.addColorStop(0.4, currentUser.color);
    gradient.addColorStop(1, "black");

    ctx.fillStyle = gradient;
    ctx.arc(currentUser.x, currentUser.y, currentUser.radius, Math.PI*2, false);
    ctx.fill();
});

//other users get updated with new players when teh new player joins
socket.on('currentUsers', function(currentUsers){
    ctx.globalCompositeOperation = "source-over";
    //Lets reduce the opacity of the BG paint to give the final touch
    ctx.fillStyle = "rgba(0, 0, 0, 0.3)";

    //Lets blend the particle with the BG
    ctx.globalCompositeOperation = "lighter";

    for(var i = 0; i < currentUsers.length; i++){

        ctx.beginPath();

        //Time for some colors
        var gradient = ctx.createRadialGradient(currentUsers[i].x, currentUsers[i].y, 0, currentUsers[i].x, currentUsers[i].y, currentUsers[i].radius);
        gradient.addColorStop(0, "white");
        gradient.addColorStop(0.4, "white");
        gradient.addColorStop(0.4, currentUsers[i].color);
        gradient.addColorStop(1, "black");

        ctx.fillStyle = gradient;
        ctx.arc(currentUsers[i].x, currentUsers[i].y, currentUsers[i].radius, Math.PI*2, false);
        ctx.fill();
    }
    console.log('A new User has joined');
});

//if a player leaves, everyone gets new set of players
socket.on('playerLeft', function(currentUsers){
    ctx.fillRect(0, 0, W, H);
    ctx.globalCompositeOperation = "source-over";
    //Lets reduce the opacity of the BG paint to give the final touch
    ctx.fillStyle = "rgba(0, 0, 0, 0.3)";

    //Lets blend the particle with the BG
    ctx.globalCompositeOperation = "lighter";

    for(var i = 0; i < currentUsers.length; i++){

        ctx.beginPath();

        //Time for some colors
        var gradient = ctx.createRadialGradient(currentUsers[i].x, currentUsers[i].y, 0, currentUsers[i].x, currentUsers[i].y, currentUsers[i].radius);
        gradient.addColorStop(0, "white");
        gradient.addColorStop(0.4, "white");
        gradient.addColorStop(0.4, currentUsers[i].color);
        gradient.addColorStop(1, "black");

        ctx.fillStyle = gradient;
        ctx.arc(currentUsers[i].x, currentUsers[i].y, currentUsers[i].radius, Math.PI*2, false);
        ctx.fill();
    }
    console.log('A Player Has left');
});


socket.on('PlayersMoving', function(players){
    ctx.globalCompositeOperation = "source-over";
    //Lets reduce the opacity of the BG paint to give the final touch
    ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
    ctx.fillRect(0, 0, W, H);

    //Lets blend the particle with the BG
    ctx.globalCompositeOperation = "lighter";

    var players = players;
    var i = 0;
    function allPlayers(){
        for(i; i < players.length; i ++) {

        ctx.beginPath();

        //Time for some colors
        var gradient = ctx.createRadialGradient(players[i].x, players[i].y, 0, players[i].x, players[i].y, players[i].radius);
        gradient.addColorStop(0.5, "white");
        gradient.addColorStop(0.5, players[i].color);
        gradient.addColorStop(1, "black");

        ctx.fillStyle = gradient;
        ctx.arc(players[i].x, players[i].y, players[i].radius, Math.PI*2, false);
        ctx.fill();
        }
    }
    allPlayers();

});

服务器端(NodeJS - 更新):

var express = require('express'),
    app = express(),
    http = require('http').createServer(app),
    io = require('socket.io').listen(http);

http.listen(3000);

//server connections and routing
app.use(express.static(__dirname + '/public'), function(request, response){
    if(request.url !== '/public') {
        response.sendFile( __dirname +'/error/index.html');
        console.log('Error 404 request, User tried accessing: ' + __dirname + request.url);
    }
});

var players = [];

//Lets create a function which will help us to create multiple players
function newPlayer() {
    this.name;
    this.id = 1;
    this.x = Math.random() * 500;
    this.y =  Math.random() * 500;
    //Random colors
    var r = Math.random()*255>>0;
    var g = Math.random()*255>>0;
    var b = Math.random()*255>>0;
    this.color = "rgba(" + r + ", " + g + ", " + b + ", 0.5)";

    //Random size
    this.radius = Math.random()*20+20;
    this.speed =  5;

    return {'name' : this.name,"x" : this.x,"y" : this.y,"color" : this.color, "radius" : this.radius,"speed" : this.speed}
}


//calls to the server and tracking connection of each new user
io.sockets.on('connection', function(socket){
    var currentPlayer = new newPlayer(); //new player made
    players.push(currentPlayer); //push player object into array

    //create the players Array
    socket.broadcast.emit('currentUsers', players);
    socket.emit('welcome', currentPlayer, players);

        //disconnected
    socket.on('disconnect', function(){
        players.splice(players.indexOf(currentPlayer), 1);
        console.log(players);
        socket.broadcast.emit('playerLeft', players);
    });

    socket.on('pressed', function(key){
        if(key === 38){
            currentPlayer.y -= currentPlayer.speed;
            socket.emit('PlayersMoving', players);
            socket.broadcast.emit('PlayersMoving', players);
        } 
        if(key === 40){
            currentPlayer.y += currentPlayer.speed;
            socket.emit('PlayersMoving', players);
            socket.broadcast.emit('PlayersMoving', players);
        } 
        if(key === 37){
            currentPlayer.x -= currentPlayer.speed;
            socket.emit('PlayersMoving', players);
            socket.broadcast.emit('PlayersMoving', players);
        } 
        if(key === 39){
            currentPlayer.x += currentPlayer.speed;
            socket.emit('PlayersMoving', players);
            socket.broadcast.emit('PlayersMoving', players);
        }
    });
});

console.log('NodeJS Server started on port 3000...');

示例

enter image description here

我知道这不是最干净的做事方法,但这是一个原型,我希望它可以帮助一个人在不久的将来成为一个踏脚石,设法遇到同样的问题而不必经历同样的事情麻烦。

由于