415 lines
13 KiB
HTML
415 lines
13 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<title>🏓 Pong</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { height: 100vh; width: 100vw; overflow: hidden; }
|
|
canvas {border: black solid 1px; top:-1px; left:-1px; position: absolute;}
|
|
div { position: fixed; top: 0; right: 0; width: 130px; height: 60px; background: rgba(0, 0, 0, .5); color: white; padding: 5px; font-family: monospace; z-index: 100; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="debug">FPS: <fps>...</fps><br/>Ping: <ping>...</ping><br />ID: <id>...</id></div>
|
|
<canvas id="test"></canvas>
|
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
|
<script src="/socket.io/socket.io.js"></script>
|
|
<script>
|
|
/** Initialize Canvas **/
|
|
var c = document.getElementById("test");
|
|
var ctx = c.getContext("2d");
|
|
c.width = $(window).width();
|
|
c.height = $(window).height();
|
|
|
|
/** FPS Vars **/
|
|
var lastCalledTime,
|
|
lastFPSshown,
|
|
fps;
|
|
|
|
/** Global Configuration **/
|
|
var localcoop = false, // Local Coop in 1 window CURRENTLY BORKEN [WONTFIX]
|
|
width = $(window).width(),
|
|
height = $(window).height();
|
|
|
|
/** Prop-Config **/
|
|
var cube_width = 50,
|
|
cube_height = 200,
|
|
cube_speed = 7,
|
|
ball_size = 15,
|
|
ball_speed = 8,
|
|
ballX = cube_width+ball_size,
|
|
ballY = cube_height/2,
|
|
ball_owner = 1,
|
|
ball_VX = 0,
|
|
ball_VY = 0,
|
|
ball_maxAngle = 5*Math.PI/12;
|
|
|
|
/** Game Variables **/
|
|
var uid, // Unique-ID
|
|
pid, // PlayerID 0 == Specator, 1 == Player 1, 2 == Player 2
|
|
pwin = 0,
|
|
pwinanim = 0;
|
|
leftY = 0,
|
|
rightY = 0,
|
|
leftV = 0,
|
|
rightV = 0,
|
|
leftColor = 'red',
|
|
rightColor = 'blue',
|
|
score = [0,0],
|
|
pressedkeys = [],
|
|
loggedkeys = [38, 40, 83, 87],
|
|
gameState = 0; // 0 == Waiting for Players, 1 == Countdown, 2 == inGame, 3 == ??Win??
|
|
|
|
var Localization = {
|
|
player : 'Spieler',
|
|
spectator : 'Zuschauer',
|
|
spectatormode : 'Zuschauer-Modus',
|
|
countdown : 'Starte in ',
|
|
countdownsuf : ' Sekunden',
|
|
waiting : 'Warte auf Spieler',
|
|
win : ' hat gewonnen!'
|
|
}
|
|
|
|
/** Initalize Socketio **/
|
|
var socket = io();
|
|
|
|
/** ############## **/
|
|
/** SERVER EVENTS **/
|
|
/** ############# **/
|
|
socket.on('handshake', function(id, state, c_width, c_height, sp, score, fn){
|
|
$('id').text(id);
|
|
uid = id;
|
|
gameState = state;
|
|
cube_width = c_width;
|
|
cube_height = c_height;
|
|
cube_speed = sp;
|
|
score = score;
|
|
fn($(window).width(), $(window).height());
|
|
});
|
|
|
|
socket.on('setpid', function(id){
|
|
pid = id;
|
|
});
|
|
|
|
socket.on('size', function(w, h){
|
|
width = w;
|
|
height = h;
|
|
c.width = w;
|
|
c.height = h;
|
|
text = Localization.countdown + 5 + Localization.countdownsuf;
|
|
});
|
|
|
|
socket.on('gamestate', function(state){
|
|
// console.log('new GameState: ' + state);
|
|
gameState = state;
|
|
});
|
|
|
|
socket.on('score', function(val){
|
|
score = val;
|
|
});
|
|
|
|
/** Serversided Countdown to prevent desync **/
|
|
socket.on('countdown', function(i){
|
|
text = Localization.countdown + i + Localization.countdownsuf;
|
|
});
|
|
|
|
socket.on('win', function(id, data){
|
|
pwin = id;
|
|
console.log('Additional Data: ' + data);
|
|
});
|
|
|
|
socket.on('paddlepos', function(pos, val, y){
|
|
if(pos == 1){
|
|
leftV = val;
|
|
leftY = y;
|
|
}
|
|
if(pos == 2){
|
|
rightV = val;
|
|
rightY = y;
|
|
}
|
|
(leftY);
|
|
});
|
|
|
|
socket.on('ballpos', function(data){
|
|
ballX = data['x'];
|
|
ballY = data['y'];
|
|
ball_VX = data['vx'];
|
|
ball_VY = data['vy'];
|
|
ball_speed = data['speed'];
|
|
ball_owner = data['owner'];
|
|
});
|
|
|
|
/** Print text in local console **/
|
|
socket.on('debug', function(arg){
|
|
console.log(arg);
|
|
});
|
|
|
|
|
|
/** ############ **/
|
|
/** DEBUG TOOLS **/
|
|
/** ########### **/
|
|
/** Meassure Ping and show **/
|
|
setInterval(function(){
|
|
var connTime = Date.now();
|
|
socket.emit('appping', function(data){
|
|
$('ping').text(Date.now()-connTime + 'ms');
|
|
});
|
|
}, 1000);
|
|
|
|
var texti = 0,
|
|
nativtext = Localization.waiting,
|
|
text = nativtext;
|
|
function draw() {
|
|
|
|
|
|
/** FPS Calculation **/
|
|
if(!lastCalledTime) {
|
|
lastCalledTime = Date.now();
|
|
fps = 0;
|
|
}
|
|
delta = (Date.now() - lastCalledTime)/1000;
|
|
lastCalledTime = Date.now();
|
|
fps = 1/delta;
|
|
|
|
/** Show FPS in Debug-Window **/
|
|
if(!lastFPSshown) {
|
|
lastFPSshown = Date.now();
|
|
$('fps').text('...');
|
|
}
|
|
if(lastFPSshown+1000 < Date.now()) {
|
|
lastFPSshown = Date.now();
|
|
$('fps').text(Math.round(fps));
|
|
|
|
/** Use this timer to display loading indicator **/
|
|
if(gameState == 0)
|
|
switch(texti){
|
|
case 0: text = nativtext; texti++; break;
|
|
case 1 :
|
|
case 2 : text+='.'; texti++; break;
|
|
case 3 : text+='.'; texti = 0; break;
|
|
}
|
|
}
|
|
|
|
/** ############## **/
|
|
/** CLIENT RENDER **/
|
|
/** ############# **/
|
|
/** Clear Drawing-Region **/
|
|
ctx.fillStyle = 'white';
|
|
ctx.fillRect(0, 0, width, height);
|
|
|
|
|
|
/** Points **/
|
|
ctx.fillStyle = 'black';
|
|
ctx.textAlign = 'center';
|
|
var scoretxt = score[0] + ' : ' + score[1];
|
|
if(pwin != 0 && gameState == 3){
|
|
ctx.font = '80px monospace';
|
|
ctx.fillText(Localization.player + ' ' + pwin + Localization.win, width/2, 310);
|
|
ctx.font = '80px monospace';
|
|
ctx.fillText(scoretxt, width/2, 190);
|
|
} else {
|
|
ctx.font = '40px monospace';
|
|
ctx.fillText(scoretxt, width/2, 70);
|
|
}
|
|
|
|
if(gameState < 2){
|
|
ctx.fillStyle = 'black';
|
|
ctx.font = '30px monospace';
|
|
ctx.textAlign = 'center';
|
|
ctx.fillText(text, width/2, height/2-30);
|
|
}
|
|
|
|
|
|
ctx.fillStyle = 'black';
|
|
ctx.font = '15px monospace';
|
|
ctx.textAlign = 'center';
|
|
if(gameState == 2 && pid == 0){
|
|
ctx.fillText(Localization.spectatormode, width/2, 25);
|
|
}
|
|
if(pid == 1 || pid == 2){
|
|
ctx.fillText(Localization.player + ' ' + pid, width/2, 25);
|
|
}
|
|
|
|
|
|
/** Draw Cube **/
|
|
/** Note: Get position by multiplying it with the "Render-Delta" - for same speed on every system **/
|
|
// console.log(leftV*delta*60*cube_speed);
|
|
// leftY -= leftV*delta*60*cube_speed;
|
|
// rightY -= rightV*delta*60*cube_speed;
|
|
// NOTE Server is calcualating Position -> Client is only drawing
|
|
|
|
|
|
ctx.fillStyle = leftColor;
|
|
ctx.fillRect(0, leftY, cube_width, cube_height);
|
|
ctx.fillStyle = rightColor;
|
|
ctx.fillRect(width-cube_width, rightY, cube_width, cube_height);
|
|
|
|
|
|
// NOTE Physics moved to Server
|
|
// /** Move Ball with Paddle if owned by owner (@roundstart) **/
|
|
// if(ball_owner == 1){
|
|
// ballX = cube_width+ball_size,
|
|
// ballY = leftY+cube_height/2;
|
|
// }
|
|
// else if(ball_owner == 2){
|
|
// ballX = width-cube_width-ball_size,
|
|
// ballY = rightY+cube_height/2;
|
|
// }
|
|
//
|
|
// /** ############ **/
|
|
// /** Ball Physics **/
|
|
// /** ############ **/
|
|
//
|
|
// /** Collide with right Paddle or get point and respawn **/
|
|
// if(ballX > width - cube_width){
|
|
// if(ballY < rightY || ballY > rightY+cube_height){
|
|
// console.log('Point for Left');
|
|
// ball_VX = 0;
|
|
// ball_VY = 0;
|
|
// ball_owner = 1;
|
|
// } else {
|
|
// var intersect = ((rightY+cube_height/2)-ballY)/(cube_height/2);
|
|
// console.log('Intersect with right at:');
|
|
// console.log(intersect);
|
|
// console.log('###########################');
|
|
// ball_VX = -ball_speed*Math.cos(intersect*ball_maxAngle);
|
|
// ball_VY = -ball_speed*Math.sin(intersect*ball_maxAngle);
|
|
// }
|
|
// }
|
|
//
|
|
// /** Collide with left Paddle or get point and respawn **/
|
|
// if(ballX < cube_width){
|
|
// if(ballY < leftY || ballY > leftY+cube_height){
|
|
// console.log('Point for Right');
|
|
// ball_VX = 0;
|
|
// ball_VY = 0;
|
|
// ball_owner = 2;
|
|
// } else {
|
|
// var intersect = ((leftY+cube_height/2)-ballY)/(cube_height/2);
|
|
// console.log('Intersect with left at:');
|
|
// console.log(intersect);
|
|
// console.log('###########################');
|
|
// ball_VX = ball_speed*Math.cos(intersect*ball_maxAngle);
|
|
// ball_VY = ball_speed*Math.sin(intersect*ball_maxAngle);
|
|
// }
|
|
// }
|
|
//
|
|
//
|
|
// /** Collide with walls **/
|
|
// if(ballY <= 0 || ballY >= height-ball_size){
|
|
// ball_VY = -ball_VY;
|
|
// console.log(ballX + ' : ' + ballY);
|
|
// }
|
|
|
|
/** Draw Ball **/
|
|
ctx.fillStyle = 'black';
|
|
// ballX += Math.round((1*delta)*60)*ball_VX;
|
|
// ballY += Math.round((1*delta)*60)*ball_VY;
|
|
ctx.beginPath();
|
|
ctx.arc(ballX, ballY, ball_size, 0, 2*Math.PI);
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
/** ############## **/
|
|
/** Input Handlers **/
|
|
/** ############## **/
|
|
|
|
$(window).on('keydown', function(e) {
|
|
if(localcoop){
|
|
switch(e.keyCode) {
|
|
case 38 : if(leftY <= 0) break; setV(1, 1); break;
|
|
case 40 : if(leftY >= height-cube_height) break; setV(1, -1); break;
|
|
case 87 : if(rightY <= 0) break; setV(2, 1); break;
|
|
case 83 : if(rightY >= height-cube_height) break; setV(2, -1); break;
|
|
case 37 :
|
|
case 39 : if(ball_owner != 1) break; shootBall(); break;
|
|
case 65 :
|
|
case 68 : if(ball_owner != 2) break; shootBall(); break;
|
|
}
|
|
} else if(pid == 1 || pid == 2){
|
|
if(gameState == 3) return;
|
|
if(loggedkeys.indexOf(e.keyCode) > -1 && pressedkeys.indexOf(e.keyCode) < 0)
|
|
pressedkeys.push(e.keyCode);
|
|
switch(e.keyCode) {
|
|
case 38 :
|
|
case 87 : if(pid == 1){
|
|
if(leftY <= 0) break; setV(1, 1);
|
|
} else if(pid == 2) {
|
|
if(rightY <= 0) break; setV(2, 1);
|
|
}
|
|
break;
|
|
case 40 :
|
|
case 83 : if(pid == 1){
|
|
if(leftY >= height-cube_height) break; setV(1, -1);
|
|
} else if(pid == 2){
|
|
if(rightY >= height-cube_height) break; setV(2, -1);
|
|
}
|
|
break;
|
|
case 37 :
|
|
case 39 :
|
|
case 65 :
|
|
case 68 : if(ball_owner != pid)break; if(ball_owner == 1) socket.emit('ballshoot', leftY); else if(ball_owner == 2) socket.emit('ballshoot', rightY); break;
|
|
}
|
|
}
|
|
});
|
|
|
|
$(window).on('keyup', function(e) {
|
|
if(localcoop){
|
|
switch(e.keyCode) {
|
|
case 38 :
|
|
case 40 : setV(1, 0); break;
|
|
case 87 :
|
|
case 83 : setV(2, 0); break;
|
|
}
|
|
}else if(pid == 1 || pid == 2){
|
|
if(gameState == 3) return;
|
|
if(loggedkeys.indexOf(e.keyCode) > -1 && pressedkeys.indexOf(e.keyCode) > -1){
|
|
pressedkeys.splice(pressedkeys.indexOf(e.keyCode), 1);
|
|
if(pressedkeys.length == 0) // Only send v=0 if all keys are up
|
|
setV(pid, 0);
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
function setV(pos, val){
|
|
var time = Date.now();
|
|
if(pos == 1){
|
|
leftV = val;
|
|
if(!localcoop)
|
|
socket.emit('vchange', pos, val, leftY, time);
|
|
}
|
|
if(pos == 2){
|
|
rightV = val;
|
|
if(!localcoop)
|
|
socket.emit('vchange', pos, val, rightY, time);
|
|
}
|
|
}
|
|
|
|
function shootBall() {
|
|
// NOTE moved to Server
|
|
// console.log('### Ball shoot event ###');
|
|
// /** TODO Tewak and Add MAX_Speed **/
|
|
// if(ball_owner == 1){
|
|
// ball_VX = ball_speed;
|
|
// console.log('owner: left - at:' + leftY);
|
|
//
|
|
// }
|
|
// else if(ball_owner == 2){
|
|
// ball_VX = -ball_speed;
|
|
// console.log('owner: right - at:' + rightY);
|
|
// if(!localcoop)
|
|
// socket.emit('ballshoot', rightY);
|
|
// }
|
|
// ball_owner = 0;
|
|
}
|
|
|
|
function animloop() {
|
|
draw();
|
|
window.requestAnimationFrame(animloop);
|
|
}
|
|
animloop();
|
|
</script>
|
|
</body>
|
|
</html>
|