Monday, January 9, 2012

Nowjs Multiplayer Map

A wonderful example now on http://nowjs.com/examples/map it is slightly wonky on firefox 11, but the core concept is there:

A multiplayer map with individual controllable points, the basis for a multiplayer game.

This is a simple and elegant breakthrough.  We can now couple this with any scenegraph and we're almost golden in terms of an onscreen sprite tied to an individual with the ability to move it by keyboard.  Wonderful.

The example is at the link above, code below.

The big key is in the server side code:
 
actors[this.user.clientId]

We have an array of actors with the clientId as the key.  So simple and so powerful.

Client Side


var context;
var viewport = {
  width: 500,
  height: 400,
  x: 0,
  y: 0,
}

$(document).ready(function() {
  context = $('#map')[0].getContext('2d');
  $('#map').keydown(function(e) {
    e.preventDefault();
    switch(e.which) {
      //left
      case 37:
        viewport.x -= 5;
        now.updateActor(viewport.x, viewport.y);
        break;
      //right
      case 39:
        viewport.x += 5;
        now.updateActor(viewport.x, viewport.y);
        break;
      //up
      case 38:
        viewport.y -= 5;
        now.updateActor(viewport.x, viewport.y);
        break;
      //down
      case 40:
        viewport.y += 5;
        now.updateActor(viewport.x, viewport.y);
        break;  
    }
  });
  now.ready(function() {
    now.updateActor(viewport.x, viewport.y);
  });
});

now.drawActors = function(actors) {  
  context.clearRect(0, 0, 500, 400);
  context.beginPath();

  for(var i in actors) {
    if(i == now.core.clientId) {
      context.fillStyle = 'red';
      context.fillRect(viewport.width / 2 + actors[i].x - viewport.x, viewport.height / 2 + actors[i].y - viewport.y, 5, 5);
      for (var x = -actors[i].x % 40; x < 500; x += 40) {
        context.moveTo(x, 0);
        context.lineTo(x, 400);
      }
      for (var y = -actors[i].y % 40; y < 400; y += 40) {
        context.moveTo(0, y);
        context.lineTo(500, y);
      }
      context.strokeStyle = "#eee";
      context.stroke();   
    } else {
      context.fillStyle = 'black';
      context.fillRect(viewport.width / 2 + actors[i].x - viewport.x, viewport.height / 2 + actors[i].y - viewport.y, 5, 5);
    }
  }
} 
 
 

Server Side


var actors = [];
nowjs.on('connect', function() {
  actors[this.user.clientId] = {x: 0, y: 0};
});

nowjs.on('disconnect', function() {
  for(var i in actors) {
    if(i == this.user.clientId) {
      delete actors[i];
      break;
    }
  }
});

everyone.now.updateActor = function(x, y) {
  actors[this.user.clientId].x = x;
  actors[this.user.clientId].y = y;
  var toUpdate = {};
  for(var i in actors) {
    if(Math.abs(x - actors[i].x) < 310 && Math.abs(y - actors[i].y) < 210) {
        toUpdate[i] = {x: actors[i].x, y: actors[i].y};
    }
  }
  for(var i in toUpdate) {
    nowjs.getClient(i, function(err) {
      this.now.drawActors(toUpdate);
    });
  }
}



-----------------------------------


That is all well and good, but how do we jump into making this work?
Ok, in your now/examples/ folder create another folder called "map" or something like that.
Create one file called:
mp_server.js
and one called:
mp.html

Open the mp_server.js and paste in:

var fs = require('fs');
var server = require('http').createServer(function(req, response){
  fs.readFile(__dirname+'/mp.html', function(err, data){
    response.writeHead(200, {'Content-Type':'text/html'});
    response.write(data); 
    response.end();
  });
});
server.listen(8080);


var nowjs = require("now");
var everyone = nowjs.initialize(server);


var actors = [];
nowjs.on('connect', function() {
  actors[this.user.clientId] = {x: 0, y: 0};
  console.log("nowjs.on Connect");
});

nowjs.on('disconnect', function() {
  for(var i in actors) {
    if(i == this.user.clientId) {
      delete actors[i];
console.log("nowjs.on disconnect: " + i);
      break;
    }
  }
});

everyone.now.updateActor = function(x, y) {
  actors[this.user.clientId].x = x;
  actors[this.user.clientId].y = y;
  var toUpdate = {};
  for(var i in actors) {
    if(Math.abs(x - actors[i].x) < 310 && Math.abs(y - actors[i].y) < 210) {
        toUpdate[i] = {x: actors[i].x, y: actors[i].y};
    }
  }
  for(var i in toUpdate) {
    nowjs.getClient(i, function(err) {
      this.now.drawActors(toUpdate);
    });
  }
}




Save and close, now open the mp.html and paste in:


<!DOCTYPE html>
<html lang="en">
<head>
<title>nowjs test</title>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>




<script src="/nowjs/now.js"></script>


<script>
var context;
var viewport = {
  width: 500,
  height: 400,
  x: 0,
  y: 0,
}

$(document).ready(function() {
  context = $('#map')[0].getContext('2d');
  $('#map').keydown(function(e) {
    e.preventDefault();
    switch(e.which) {
      //left
      case 37:
        viewport.x -= 5;
        now.updateActor(viewport.x, viewport.y);
        break;
      //right
      case 39:
        viewport.x += 5;
        now.updateActor(viewport.x, viewport.y);
        break;
      //up
      case 38:
        viewport.y -= 5;
        now.updateActor(viewport.x, viewport.y);
        break;
      //down
      case 40:
        viewport.y += 5;
        now.updateActor(viewport.x, viewport.y);
        break; 
    }
  });
  now.ready(function() {
    now.updateActor(viewport.x, viewport.y);
  });
});

now.drawActors = function(actors) { 
  context.clearRect(0, 0, 500, 400);
  context.beginPath();


  for(var i in actors) {
    if(i == now.core.clientId) {
      context.fillStyle = 'red';
      context.fillRect(viewport.width / 2 + actors[i].x - viewport.x, viewport.height / 2 + actors[i].y - viewport.y, 5, 5);
     
      
    for (var x = -actors[i].x % 40; x < 500; x += 40) {
      context.moveTo(x, 0);
      context.lineTo(x, 400);
    }


    for (var y = -actors[i].y % 40; y < 400; y += 40) {
      context.moveTo(0, y);
      context.lineTo(500, y);
    }
   
    context.strokeStyle = "#eee";
    context.stroke();  
    } else {
      context.fillStyle = 'black';
      context.fillRect(viewport.width / 2 + actors[i].x - viewport.x, viewport.height / 2 + actors[i].y - viewport.y, 5, 5);
    }
  }
}
</script>


</head>

<body>

  <canvas id='map' width='500' height='400' tabindex='1' style="border:solid 1px #000000;"></canvas>

</body>
</html>


Now just execute mp_server.js like you have done with the other nowjs examples. Then point multiple browsers to localhost:8080 or whatever your testing medium might be.  You should have a fully working example of a multiplayer map.   We are well on our way.  Awesome.

No comments:

Post a Comment