node meetup feb_20_12
TRANSCRIPT
Hello I’m Jafar
http://jaf.ar.com
Multiplayer Worldswith
Node.js + HTML
RED INTERACTIVE
2009 - I Love Flash
2010 - Steve Jobs Hates Flash
2011 - Flash dies slow death... :(
http://FF0000
Inspired By
My Website is....
Not perfect....
Probably really hackable...
Still using globals....
Chat room needs filters...
Server not authoritave
Works well in Chrome 19 ! Download Chrome Canary
Worked well in Chrome 16
Chrome updates to 17......bugs come back
(image flicker)
http://www.eclipse.org/forums/index.php/mv/tree/262108/#page_top
Might be a cache issue....Might be a Mac thing...Maybe Wifi on Mac thing
OH NO BUGS IN CHROME!
So Screw IE9 or Lower
Although Socket.io does work on browsers without
web sockets
IE10 pretty good....
MICROSOFT SAYS NO WEBSOCKETS
and Opera and Safari
(too choppy)
Microsoft Changes Their Mind
STACK
Node.js - server
Socket.IO - cross browser sockets
Now.js - namespace, easy chat rooms
jQuery - front end stuff
Express - static files, pages
//ANIMATION LOOPwindow.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame })();
World.js
//MAIN LOOP(function animloop(){
moveCamera(); gravity(); floor(); myPlayer(); movePlayers(); controlPlayer(); moveLasers(); otherLasers();
requestAnimFrame(animloop);
})();
Canvas too slow for a large mapJUST USE HTML DIV’s
Because when you draw on canvas you must also clear each move and each pixel
3000 x 3000 = 9 Million px/sec
http://craftyjs.com/demos/tutorial/tutorial.html
“Tip: DOM is usually always faster than canvas....”
3000px 1920 x 1024
3000
px
#myPlayer
.player
in relation to the DOM WindowWhere is My Player?
.player .player
.player .player
var PosX = $('#myPlayer').offset().left - $(window).scrollLeft();
if( PosX < 50 || PosX > (winW - 50) )
window.scrollBy(x,y);
//within 50px of window
//scroll the window to keep player in view
//PULL PLAYERSeveryone.now.pullPlayers = function(id,x,y,dir,mov,flo,skin){
actors[id] = {x:x, y:y, dir:dir, mov:mov,flo:flo,skin:skin};
var playerUpdate = {}; playerUpdate = {id:id,x:x,y:y,dir:dir,mov:mov,flo:flo,skin:skin}; everyone.now.showPlayers(playerUpdate); };
var players = {};
CLIENT
SERVER
CLIENT
id, x, y, direction, moving, floor, skin
>>>> LOOP THE PLAYERS OBJECT
//SHOW PLAYERS now.showPlayers = function(plyr) { players[plyr.id] = {x:plyr.x, y:plyr.y, dir:plyr.dir, mov:plyr.mov,flo:plyr.flo,skin:plyr.skin}; }
//KEY DOWN function kdRight(){ now.pullPlayers(now.core.clientId,player.x, player.y, 0,1,floor_y,skin_num); }
Animation Loop
//SHOW PLAYERS now.showPlayers = function(plyr) { players[plyr.id] = {x:plyr.x, y:plyr.y, dir:plyr.dir, mov:plyr.mov,flo:plyr.flo,skin:plyr.skin}; }
//MOVE PLAYERSfunction movePlayers(){ //FOR LOOP for(var i in players) { //REMOVE $(".player[rel='" + i + "']").remove(); //ADD $('#Players').append('<div class="player" rel="' + i + '"></div>'); >>>>IF MOVING (next page) >>>>ELSE STANDING STILL (next page) } }
Animation Loop
//MOVING if(players[i].mov == 1) { //LEFT if(players[i].dir == 1) { players[i].x--; $(".player[rel='"+ i +"']").css('left', players[i].x ); $(".player[rel='"+ i +"']").css('top', players[i].y ); $(".player[rel='"+ i +"']").css('background-image',img_walk_l); //RIGHT } else if(players[i].dir == 0){ players[i].x ++; $(".player[rel='"+ i +"']").css('left', players[i].x ); $(".player[rel='"+ i +"']").css('top', players[i].y ); $(".player[rel='"+ i +"']").css('background-image',img_walk_r); } else { $(".player[rel='"+ i +"']").css('left', players[i].x ); $(".player[rel='"+ i +"']").css('top', players[i].y ); $(".player[rel='"+ i +"']").css('background-image',img_front); } //STANDING STILL } else { //LEFT if(players[i].dir == 1) { $(".player[rel='"+ i +"']").css('background-image',img_l); $(".player[rel='"+ i +"']").css('left', players[i].x ); $(".player[rel='"+ i +"']").css('top', players[i].y ); //RIGHT } else if (players[i].dir == 0){ $(".player[rel='"+ i +"']").css('background-image',img_r); $(".player[rel='"+ i +"']").css('left', players[i].x ); $(".player[rel='"+ i +"']").css('top', players[i].y ); //BACK } else if (players[i].dir == 'b'){ $(".player[rel='"+ i +"']").css('background-image',img_back); $(".player[rel='"+ i +"']").css('left', players[i].x ); $(".player[rel='"+ i +"']").css('top', players[i].y ); //FRONT } else { $(".player[rel='"+ i +"']").css('background-image',img_front); $(".player[rel='"+ i +"']").css('left', players[i].x ); $(".player[rel='"+ i +"']").css('top', players[i].y ); } //END MOV }
Wait What About My Player
Wait What About My Player
//MY PLAYERfunction myPlayer() { if ( movement && leftKey ) { player.x --; $('#player').css('left',player.x ); } else if ( movement && rightKey ) { player.x ++; $('#player').css('left',player.x ); } else { $('#player').css('left',player.x ); } //END FUNC }
//GLOBALS--movement --leftKey--rightKey
GRAVITY LOOPWe use the global object “players{}”
//GRAVITYfunction gravity() { //MY PLAYER if ( player.y !== floor_y ) {
player.y ++; player.y ++; $('#player').css('top',player.y ); } //FOR LOOP for (var i in players) { //IN THE AIR if( i === now.core.clientId && players[i].y !== floor_y ) {
players[i].y++; players[i].y++; $(".player[rel='"+ i +"']").css('top', players[i].y );
} else if ( i !== now.core.clientId && players[i].y !== players[i].flo) {
players[i].y++; players[i].y++; $(".player[rel='"+ i +"']").css('top', players[i].y );
} else { //do nothing }
}
}
now.pullPlayers(now.core.clientId,player.x, player.y, direction, movement, floor_y,skin_num)
//FLOORfunction floor(){
//FLOOR 1 if(player.y == floor1 && player.x <= 810 && player.x >= 100){ floor_y = floor1; player.y = floor1; } //FALLING if ( player.y == floor1 - 2 && player.x <= 810 && player.x >= 100) { floor_y = floor1; now.pullPlayers(); } //JUMPING if ( player.y == floor1 + 2) { now.pullPlayers(); }
//END FUNC}
FLOOR LOOPchanging players floor position
**PULL PLAYERS**
now.pullPlayers(now.core.clientId,player.x, player.y, direction, movement, floor_y,skin_num)
LASERS LOOP
//FIRE LASERSeveryone.now.fireLasers = function(id,x,y,dir){
var laserUpdate = {}; laserUpdate = {id:id,x:x,y:y,dir:dir}; everyone.now.showLasers(laserUpdate);
};
//SHOW LASERSnow.showLasers = function(laz) { if(laz.id != now.core.clientId) { other_lasers.push( [laz.x, laz.y, laz.dir] ); } }
var lasers = []; var other_lasers = [];var laser_v = 27;
$(window).keyup(function(e) {
case 191: if(!$(‘.placeHolder’).is(“:focus”)) { now.fireLasers(now.core.clientId,player.x + 12, player.y + 12, direction); lasers.push([player.x + 12, player.y + 12, direction]); e.preventDefault(); } break;}
VARIABLES
CLIENT
SERVER
CLIENT
>>>> MOVE AND DRAW LASERS
Function to Draw Our Lasers
//LASERS function drawLasers (x, y, dir) { if( dir == 0 || dir == 1) { game.clearRect(x,y,-65,2); game.clearRect(x, y,65,2); game.fillStyle = '#f00'; game.fillRect(x, y,10,1); } else { game.clearRect(x,y,-2,65); game.clearRect(x, y,2,65); game.fillStyle = '#f00'; game.fillRect(x, y,1,10); } }
function moveLasers() { for(var i = 0; i < lasers.length; i++) { //IF WITHIN BOUNDS if(lasers[i][0] < player.x + w && lasers[i][0] > player.x - w && lasers[i][1] > 1500 ) { if(lasers[i][2] == 1) { lasers[i][0] -= laser_v; } else if(lasers[i][2] == 0){ lasers[i][0] += laser_v; } else { lasers[i][1] -= laser_v; } drawLasers(lasers[i][0], lasers[i][1],lasers[i][2]); } else if(lasers[i][0] > player.x + w + 1 || lasers[i][0] < player.x - w +1){ lasers.splice(i,1); } else if(lasers[i][1] < 0 ){ lasers.splice(i,1); } else { //do nothing } } }
MOVE LASERS
function otherLasers() { for(var i = 0; i < other_lasers.length; i++) { if(other_lasers[i][0] < player.x + w && other_lasers[i][0] > player.x - w ) { if(other_lasers[i][2] == 1) { other_lasers[i][0] -= laser_v; } else if(other_lasers[i][2] == 0){ other_lasers[i][0] += laser_v; } else { other_lasers[i][1] -= laser_v; } drawLasers(other_lasers[i][0], other_lasers[i][1],other_lasers[i][2]); } else { other_lasers.splice(i,1); } } }
OTHER PLAYERS LASERS