Showing posts with label nowjs. Show all posts
Showing posts with label nowjs. Show all posts

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.

Wednesday, December 7, 2011

Install nowjs in Windows

First you have to go through the process of installing node.js in windows. I have detailed the process there.

There's two requirements for nowjs to run on windows:

Microsoft Visual C++ Runtime:


Go ahead and install it if you don't already have it.

Install Microsoft Visual C++ Runtime (4.8 MB)

socket.io:

So we can open a CMD window and type:

C:\progra~1\nodejs\npm install socket.io

If your node.js install is in a different directory then change the path accordingly.

NPM will install it to:

C:\Program Files\nodejs\node_modules\socket.io



Good.  Now we need to obtain the nowjs windows build here:

https://github.com/Flotype/now/zipball/windows

The file will read something like Flotype-now-0.7.6-3-gb41b2b2.zip

Unzip it to a folder.  Inside that folder you'll have another folder with a similar name.

So:  ~\Downloads\Flotype-now-0.7.6-3-gb41b2b2\Flotype-now-b41b2b2

Let's rename the second folder to now

So: ~Downloads\Flotype-now-0.7.6-3-gb41b2b2\now

Copy out the now folder and paste it to:

C:\Program Files\nodejs\node_modules\

So: C:\Program Files\nodejs\node_modules\now

Inside of that now folder there should be subfolders like bin, doc, examples and other items.


You now have nowjs installed within windows as a module for node.js.  It looks like quite a bit of steps, but  in reality it is quite painless and if you followed the steps you've seen that.

Time to test it!

The nowjs people have supplied us with three example projects:

They are located in: C:\Program Files\nodejs\node_modules\now\examples
express_example
helloworld_example
multiroomchat_example

Let's jump to the fun stuff and try and get multiroomchat_example going.

Firstly close down any node.js server CMD window instances you have running.

Open up a new CMD window and type:

C:\Program Files\nodejs\node.exe C:\Program Files\nodejs\node_modules\now\examples\multiroomchat_example\multiroomchat_server.js

Hit enter.

You now have node.js running the multiroom chat example server.

Take a look by pointing a few browsers to http://localhost:8080/ it will prompt you for a name.

You'll end up with:



And if everything went perfectly you have now installed and tested nowjs.

A multi-room chat example in under 10 minutes, less than that if you're quick!  From this point we have the ability to communicate in real-time to multiple browsers and a server, the basis for a MMORPG.



Thursday, December 1, 2011

Node.js Hosting

Just stumbled on Nodester

From their main page:


Deploy your Node.JS applications on Nodester.com for FREE or deploy your apps to your own private cloud instance of Nodester running in your own datacenter or on Amazon EC2, Rackspace, or GoGrid!
Nodester is an open source Node.JS Platform-as-a-Service written in Node.JS with a RESTful API designed to run in any cloud without a VPS! It was the first of it's kind and 100% open sourced under the GNU Affero license on GitHub. -> Watch the video!

Ok, that's a lot of crazy-go-nuts terms.

To sum it up:
Host your game here for free.

Benefits we are looking for:
node.js [check]
nowjs [check]
github [check]
FOSS [check]

Really something to keep in mind.

Html5 Webworkers Introduction

The idea:
Multi-threaded html5 games. Sounds easy enough...

A little bit more detail.
So just about any game can benefit from being multi-threaded.
As an example:
1 thread for handling client side physics. (box2d.js)
1 thread for updating the server (nowjs / socket.io / node.js)
1 thread for managing the scene graph (scenejs, amino, cake, etc)
1 thread for rendering background (ground, buildings, etc)
1 thread for rendering foreground (player, baddies, items, etc)
A main thread for pushing from the backbuffer to the front. (canvas, webgl, etc)

The list can go on and on, not that it should.  If a function is simple, quick, and not constantly running, chances are it shouldn't have it's own thread.  The benefit of running multiple threads is the holy grail of games: consistent frames per second under variable load.  That right there is a hallmark of great game designers.  So many times I'll pop in a game that is professionally made for a closed source single system (ps3, xbox) with very known hardware specs and at points in the game they're will be a frame per second drop (offline).  This could be debated and I'm sure it is other places.

And here is a little FPS loop (javascript):

var fps = 0, now, lastUpdate = (new Date)*1 - 1;

// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;

function drawFrame(){
  // ... draw the frame ...

  var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
  fps += (thisFrameFPS - fps) / fpsFilter;
  lastUpdate = now;

  setTimeout( drawFrame, 1 );
}

var fpsOut = document.getElementById('fps');
setInterval(function(){
  fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);

I'm sure somewhere someone has come up with a hard fast rule about what a good fps is.  I just like to think that anything above noticeably inconsistent and slow.  My goal is almost always 30+ FPS for the composited flip, less than that and people will notice, less than 24 and people will really notice.

Here is a wonderful js file for FPS https://github.com/mrdoob/stats.js as always mrdoob is saving our butts.

So on to webworkers: a nice little tutorial: http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html and here: http://www.codediesel.com/javascript/introducing-html5-web-workers/

What are Web Workers

Web Workers are basically a API specification that lets you create background JavaScript threads to process CPU intensive tasks. Normally in browsers a single thread is created to handle all the JavaScript code. So whatever JavaScript code is run in the browser is executed in that single thread; whether you are doing some calculation or updating page elements. The downside of this is that some CPU intensive piece of JavaScript can render the page unresponsive or slow it to a crawl.
Web Workers alleviate this problem by letting you create multiple JavaScript threads that will run independent of each other, this will prevent one CPU intensive piece of JavaScript from interfering with the UI code.
And there you have it.  This post is just an introduction.  The eventual goal being an explanation of how to apply webworkers to just about everything.  But first I have to figure that out. ha.