2017-01-20 22:42:49 +00:00
|
|
|
<!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>
|
|
|
|
var socket = io();
|
|
|
|
var pid;
|
|
|
|
socket.on('handshake', function(id, state, c_width, c_height, sp, fn){
|
|
|
|
$('id').text(id);
|
|
|
|
pid = id;
|
|
|
|
gameState = state;
|
|
|
|
cube_width = c_width;
|
|
|
|
cube_height = c_height;
|
|
|
|
speed = sp;
|
|
|
|
fn($(window).width(), $(window).height());
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('size', function(w, h){
|
|
|
|
width = w;
|
|
|
|
height = h;
|
|
|
|
c.width = w;
|
|
|
|
c.height = h;
|
|
|
|
text = 'Starting in 5';
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('gamestate', function(state){
|
|
|
|
gameState = state;
|
|
|
|
});
|
|
|
|
|
2017-01-20 23:50:32 +00:00
|
|
|
socket.on('shootball', function(){
|
|
|
|
shootBall();
|
|
|
|
});
|
|
|
|
|
2017-01-20 22:42:49 +00:00
|
|
|
/** Make Countdown serversided No cheating possible **/
|
|
|
|
socket.on('countdown', function(i){
|
|
|
|
text = 'Starting in ' + i;
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('start', function(){
|
|
|
|
leftOr = 'none';
|
|
|
|
rightOr = 'none';
|
|
|
|
leftY = 0;
|
|
|
|
rightY = 0;
|
|
|
|
leftV = 0;
|
|
|
|
rightV = 0;
|
2017-01-20 23:50:32 +00:00
|
|
|
ballX = cube_width+ball_size;
|
|
|
|
ballY = cube_height/2;
|
|
|
|
ball_owner = 1;
|
|
|
|
ball_VX = 0;
|
|
|
|
ball_VY = 0;
|
2017-01-20 22:42:49 +00:00
|
|
|
gameState = 2;
|
|
|
|
});
|
|
|
|
|
2017-01-20 23:50:32 +00:00
|
|
|
socket.on('changeV', function(pos, val, y){
|
|
|
|
if(pos == 1){
|
|
|
|
leftV = val;
|
|
|
|
leftY = y;
|
2017-01-20 22:42:49 +00:00
|
|
|
}
|
2017-01-20 23:50:32 +00:00
|
|
|
if(pos == 2){
|
|
|
|
rightV = val;
|
|
|
|
rightY = y;
|
2017-01-20 22:42:49 +00:00
|
|
|
}
|
2017-01-20 23:50:32 +00:00
|
|
|
console.log(leftY);
|
2017-01-20 22:42:49 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('test', function(arg){
|
|
|
|
console.log(arg);
|
|
|
|
});
|
|
|
|
/** Initialize Canvas **/
|
|
|
|
var c = document.getElementById("test");
|
|
|
|
var ctx = c.getContext("2d");
|
|
|
|
c.width = $(window).width();
|
|
|
|
c.height = $(window).height();
|
|
|
|
|
|
|
|
|
|
|
|
var leftY = 0;
|
|
|
|
var rightY = 0;
|
|
|
|
var leftV = 0;
|
|
|
|
var rightV = 0;
|
|
|
|
var leftColor = 'red';
|
|
|
|
var rightColor = 'blue';
|
|
|
|
var gameState = 0; // 0 == Waiting for Players, 1 == Countdown, 2 == inGame, 3 == ??Win??
|
|
|
|
|
|
|
|
/** FPS Vars **/
|
|
|
|
var lastCalledTime,
|
|
|
|
lastFPSshown,
|
|
|
|
fps;
|
|
|
|
|
|
|
|
/** Global Configuration **/
|
2017-01-20 23:50:32 +00:00
|
|
|
var localcoop = false, // Local Coop in 1 window
|
2017-01-20 22:42:49 +00:00
|
|
|
width = $(window).width(),
|
|
|
|
height = $(window).height();
|
|
|
|
|
|
|
|
/** Prop-Config **/
|
|
|
|
var cube_width = 50,
|
|
|
|
cube_height = 200,
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
/** Messaure 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 = 'Waiting for Players',
|
|
|
|
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 for displaying 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Clear Drawing-Region **/
|
|
|
|
ctx.fillStyle = 'white';
|
|
|
|
ctx.fillRect(0, 0, width, height);
|
|
|
|
|
|
|
|
|
|
|
|
if(gameState < 2){
|
|
|
|
ctx.fillStyle = 'black';
|
|
|
|
ctx.font = '30px monospace';
|
|
|
|
ctx.fillText(text, width/2-text.length*8, height/2-30);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(gameState == 2 && pid > 2){
|
|
|
|
var specstr = "Spectator-Mode"
|
|
|
|
ctx.fillStyle = 'black';
|
|
|
|
ctx.font = '15px monospace';
|
|
|
|
ctx.fillText(specstr, width/2-specstr.length*8, 50);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(leftY > height-cube_height || leftY < 0)
|
|
|
|
leftV = 0;
|
|
|
|
if(rightY > height-cube_height || rightY < 0)
|
|
|
|
rightV = 0;
|
|
|
|
if(leftY < 0 ) leftY = 0;
|
|
|
|
if(rightY < 0 ) rightY = 0;
|
|
|
|
if(leftY > height-cube_height) leftY = height-cube_height;
|
|
|
|
if(rightY > height-cube_height) rightY = height-cube_height;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Draw Cube **/
|
|
|
|
/** Note: Get position by multiplying it with the "Render-Delta" - for same speed on every system **/
|
|
|
|
|
|
|
|
leftY -= leftV*Math.round((1*delta)*60*speed);
|
|
|
|
rightY -= rightV*Math.round((1*delta)*60*speed);
|
|
|
|
|
|
|
|
|
|
|
|
ctx.fillStyle = leftColor;
|
|
|
|
ctx.fillRect(0, leftY, cube_width, cube_height);
|
|
|
|
ctx.fillStyle = rightColor;
|
|
|
|
ctx.fillRect(width-cube_width, rightY, cube_width, cube_height);
|
|
|
|
|
|
|
|
/** 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);
|
|
|
|
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);
|
|
|
|
ball_VX = ball_speed*Math.cos(intersect*ball_maxAngle);
|
|
|
|
ball_VY = ball_speed*Math.sin(intersect*ball_maxAngle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** TODO INCREASE SPEED ON PADDLE HIT (weiter außen = schneller) **/
|
|
|
|
|
|
|
|
/** Collide with walls **/
|
|
|
|
if(ballY <= 0 || ballY >= height-ball_size){
|
|
|
|
ball_VY = -ball_VY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 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) {
|
2017-01-20 23:50:32 +00:00
|
|
|
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;
|
2017-01-20 22:42:49 +00:00
|
|
|
case 37 :
|
|
|
|
case 39 : if(ball_owner != 1) break; shootBall(); break;
|
|
|
|
case 65 :
|
|
|
|
case 68 : if(ball_owner != 2) break; shootBall(); break;
|
|
|
|
}
|
2017-01-20 23:50:32 +00:00
|
|
|
} else if(pid == 1 || pid == 2){
|
|
|
|
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; socket.emit('ballshoot'); shootBall(); break;
|
|
|
|
}
|
|
|
|
}
|
2017-01-20 22:42:49 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
$(window).on('keyup', function(e) {
|
|
|
|
if(localcoop){
|
|
|
|
switch(e.keyCode) {
|
|
|
|
case 38 :
|
2017-01-20 23:50:32 +00:00
|
|
|
case 40 :
|
|
|
|
//TODO (maybe) add Smooth stopping to Paddles
|
2017-01-20 22:42:49 +00:00
|
|
|
// case 40 : var i = 100;
|
|
|
|
// var interval = setInterval(function(){
|
|
|
|
// i--;
|
|
|
|
// leftV = i/100*(0,002-i/100);
|
|
|
|
// if(i == 0) clearInterval(interval);
|
|
|
|
// }, Math.round((1*delta)*60)*10);
|
2017-01-20 23:50:32 +00:00
|
|
|
setV(1, 0);
|
|
|
|
break;
|
2017-01-20 22:42:49 +00:00
|
|
|
case 87 :
|
2017-01-20 23:50:32 +00:00
|
|
|
case 83 : setV(2, 0); break;
|
|
|
|
}
|
|
|
|
}else if(pid == 1 || pid == 2){
|
|
|
|
switch(e.keyCode) {
|
|
|
|
case 38 :
|
|
|
|
case 87 : if(pid == 1){
|
|
|
|
setV(1, 0);
|
|
|
|
} else if(pid == 2) {
|
|
|
|
setV(2, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 40 :
|
|
|
|
case 83 : if(pid == 1){
|
|
|
|
setV(1, 0);
|
|
|
|
} else if(pid == 2){
|
|
|
|
setV(2, 0);
|
|
|
|
}
|
|
|
|
break;
|
2017-01-20 22:42:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2017-01-20 23:50:32 +00:00
|
|
|
function setV(pos, val){
|
|
|
|
console.log(leftY);
|
|
|
|
if(pos == 1){
|
|
|
|
leftV = val;
|
|
|
|
if(!localcoop)
|
|
|
|
socket.emit('vchange', pos, val, leftY);
|
|
|
|
}
|
|
|
|
if(pos == 2){
|
|
|
|
rightV = val;
|
|
|
|
if(!localcoop)
|
|
|
|
socket.emit('vchange', pos, val, rightY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-20 22:42:49 +00:00
|
|
|
function shootBall() {
|
|
|
|
/** TODO Tewak and Add MAX_Speed **/
|
|
|
|
if(ball_owner == 1)
|
|
|
|
ball_VX = ball_speed;
|
|
|
|
else if(ball_owner == 2)
|
|
|
|
ball_VX = -ball_speed;
|
|
|
|
ball_owner = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function animloop() {
|
|
|
|
draw();
|
|
|
|
window.requestAnimationFrame(animloop);
|
|
|
|
}
|
|
|
|
animloop();
|
|
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|