An implementation of Classic pong using HTML5 and JavaScript. :)
Created
April 1, 2014 19:39
-
-
Save dragfire/9921456 to your computer and use it in GitHub Desktop.
A Pen by devasem.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <canvas id="display" width="600" height="480" style="border:1px solid black;"></canvas> | |
| <div id="control"> | |
| <p>Use <u>UP</u> Right Paddle Up</p> | |
| <p>Use <u>DOWN </u>Right Paddle Down</p> | |
| <p>Use <u>W</u> Left Paddle Up</p> | |
| <p>Use <u>S</u> Left Paddle Down</p> | |
| <p>Click <u>Toggle AI</u> To Play with Computer</p> | |
| <p>Click <u>Restart</u> To restart the Game</p> | |
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| window.onload = function () { | |
| var canvas = document.getElementById("display"), | |
| frame = canvas.getContext("2d"); | |
| CanvasRenderingContext2D.prototype.draw_image = function(image,center,srcSize,dstPos,dstSize,angle){ | |
| if(angle == undefined || angle == null || angle ===0) | |
| this.drawImage(image,Math.floor(center[0]/srcSize[0])*srcSize[0],Math.floor(center[1]/srcSize[1])*srcSize[1],srcSize[0],srcSize[1],dstPos[0],dstPos[1],dstSize[0],dstSize[1]); | |
| else{ | |
| this.save(); | |
| var x = dstPos[0], | |
| y = dstPos[1], | |
| halfWidth = srcSize[0]/2, | |
| halfHeight = srcSize[1]/2, | |
| angleInRads = angle * Math.PI / 180; | |
| this.translate(x+halfWidth,y+halfHeight); | |
| this.rotate(angleInRads); | |
| this.drawImage(image,Math.floor(center[0]/srcSize[0])*srcSize[0],Math.floor(center[1]/srcSize[1])*srcSize[1],srcSize[0],srcSize[1],-halfWidth,-halfHeight,dstSize[0],dstSize[1]); | |
| this.restore(); | |
| } | |
| }; | |
| //Line Drawing | |
| CanvasRenderingContext2D.prototype.drawLine = function(start,end,width,color){ | |
| this.strokeStyle= color; | |
| this.lineWidth = width; | |
| this.lineCap = "square"; | |
| this.beginPath(); | |
| this.moveTo(start[0],start[1]); | |
| this.lineTo(end[0],end[1]); | |
| this.stroke(); | |
| this.closePath(); | |
| }; | |
| var div = document.createElement("div"), | |
| b1= document.createElement("button"); | |
| document.body.appendChild(div); | |
| b1.innerHTML = "Restart"; | |
| b1.setAttribute("class","controls"); | |
| div.appendChild(b1); | |
| b1.onclick = function(){ | |
| restart(); | |
| }; | |
| var b2 = document.createElement("button"); | |
| b2.innerHTML="Toggle AI"; | |
| b2.setAttribute("class","controls") | |
| div.appendChild(b2); | |
| b2.onclick = function(){ | |
| toggle_ai(); | |
| if(comp_AI) h1.innerHTML = "Computer AI : ON"; | |
| else h1.innerHTML = "Computer AI : OFF"; | |
| }; | |
| var h1 = document.createElement("h1"); | |
| document.body.appendChild(h1); | |
| h1.innerHTML = "Computer AI : OFF"; | |
| var width = 600, | |
| height = 480, | |
| speed_increase = 0.1, | |
| i= 0, | |
| soundPool=[], | |
| MAX_SOUNDS= 6, | |
| resLoaded = 0, | |
| totalRes = 5, | |
| keydown=false, | |
| max_particles = 10; | |
| var paddle = { | |
| width: 20, | |
| height: 80, | |
| half_width: 10, | |
| half_height: 40, | |
| speed: 5 | |
| }; | |
| var ball = { | |
| width:40, | |
| height:40, | |
| radius: 20, | |
| max_speed: 600 | |
| }, | |
| ball_vel=[0,0], | |
| ball_pos=[0,0], | |
| pad1_pos=0, | |
| pad2_pos=0, | |
| pad1_vel= 0, | |
| pad2_vel= 0, | |
| score1= 0, | |
| score2= 0, | |
| isRunning = false | |
| ; | |
| var ball_effects = { | |
| effect1_width: 120, | |
| effect1_height: 120, | |
| effect2_width: 600, | |
| effect2_height: 600 | |
| }; | |
| var comp_AI = false, | |
| gutter_count = 0, | |
| collision_effect_timer, | |
| trail_timer, | |
| image_load_timer, | |
| trail = []; | |
| var images = { | |
| background: "https://dl.dropboxusercontent.com/u/8367729/codeskulptor/pong/background.png", | |
| pad: "https://dl.dropboxusercontent.com/u/224687431/paddle.png", | |
| ball: "https://dl.dropboxusercontent.com/u/8367729/codeskulptor/pong/ball.png", | |
| b_eff1: "https://dl.dropboxusercontent.com/u/8367729/codeskulptor/pong/ball_effect1.png", | |
| b_eff2: "https://dl.dropboxusercontent.com/u/8367729/codeskulptor/pong/ball_effect2.png", | |
| loader: "https://dl.dropboxusercontent.com/u/224687431/spinner_large_sprites36%402x.png" | |
| }, | |
| background_img = new Image(), | |
| paddle_img = new Image(), | |
| ball_img = new Image(), | |
| ball_eff1_img = new Image(), | |
| ball_effect2_img = new Image(), | |
| animLoader = new Image(), | |
| images_loaded = false; | |
| background_img.src = images.background; | |
| paddle_img.src = images.pad; | |
| ball_img.src = images.ball; | |
| ball_eff1_img.src = images.b_eff1; | |
| ball_effect2_img.src = images.b_eff2; | |
| animLoader.src = images.loader; | |
| // sounds | |
| var electric_sound = "https://dl.dropboxusercontent.com/u/8367729/codeskulptor/pong/zap.ogg", | |
| beepSound = "https://dl.dropboxusercontent.com/u/8367729/codeskulptor/pong/beep.ogg"; | |
| // loading onload | |
| background_img.onload = function(){loading();}; | |
| paddle_img.onload = function(){loading();}; | |
| ball_img.onload = function(){loading();}; | |
| ball_eff1_img.onload = function(){loading();}; | |
| ball_effect2_img.onload = function(){loading();}; | |
| // electric_sound.onload = function(){loading();}; | |
| // beepSound.onload = function(){loading();}; | |
| function playSound(sound,volume){ | |
| var soundFound = false; | |
| var soundIndex =0; | |
| var tempSound; | |
| if(soundPool.length>0){ | |
| while(!soundFound && soundIndex<soundPool.length){ | |
| var tSound = soundPool[soundIndex]; | |
| if(tSound.ended){ | |
| soundFound = true; | |
| } | |
| else{ | |
| soundIndex++; | |
| } | |
| } | |
| } | |
| if(soundFound){ | |
| tempSound = soundPool[soundIndex]; | |
| tempSound.setAttribute("src",sound); | |
| tempSound.loop = false; | |
| tempSound.volume = volume; | |
| tempSound.play(); | |
| } | |
| else if(soundPool.length<MAX_SOUNDS){ | |
| tempSound = document.createElement("audio"); | |
| tempSound.setAttribute("src",sound); | |
| tempSound.volume = volume; | |
| tempSound.loop = false; | |
| tempSound.play(); | |
| soundPool.push(tempSound); | |
| } | |
| } | |
| function loading(){ | |
| resLoaded++; | |
| if(resLoaded===totalRes) { | |
| images_loaded=true; | |
| render(); | |
| } | |
| } | |
| window.addEventListener("keydown",keyDown,false); | |
| window.addEventListener("keyup",keyUp,false); | |
| function keyDown(e){ | |
| var key = e.keyCode; | |
| keydown=true; | |
| if (!comp_AI){ | |
| if(key == 87) pad1_vel = - paddle.speed; | |
| else if(key == 83) pad1_vel = paddle.speed; | |
| } | |
| if(key ==38) pad2_vel = -paddle.speed; | |
| else if(key ==40 ) pad2_vel = paddle.speed; | |
| } | |
| function keyUp(e){ | |
| var key = e.keyCode; | |
| keydown=false; | |
| if(!comp_AI){ | |
| if(key == 87) pad1_vel =0; | |
| else if (key == 83) pad1_vel=0; | |
| } | |
| if(key == 38) pad2_vel=0; | |
| else if(key == 40) pad2_vel=0; | |
| } | |
| function ballInit(right){ | |
| trail=[]; | |
| ball_pos = [width / 2-ball.radius, height / 2-ball.radius]; | |
| ball_vel = [random(140, 280) / 60.0, -random(80, 200) / 60.0]; | |
| // console.log((random(120, 240) / 60.0)+" "+(-random(60, 180) / 60.0)); | |
| if (!right) | |
| ball_vel[0] = -ball_vel[0]; | |
| pad1_pos = height / 2; | |
| pad2_pos = height / 2; | |
| } | |
| function newGame() { | |
| pad1_vel = 0.0; | |
| pad2_vel = 0.0; | |
| score1 = 0; | |
| score2 = 0; | |
| var result = randomSelect(); | |
| ballInit(result); | |
| //timer | |
| trail_timer = setInterval(trail_timer_tick,500); | |
| console.log("New game started."); | |
| } | |
| function random(min, max) { | |
| return Math.floor(Math.random() * max + min - 1); | |
| } | |
| function randomSelect() { | |
| var sel = random(1, 2); | |
| if (sel === 1) return true; | |
| else return false; | |
| } | |
| function start_electric_effect() { | |
| //timer | |
| isRunning = true; | |
| collision_effect_timer = setInterval(Collision_effect_tick,200); | |
| } | |
| function clamp(value, min_val, max_val) { | |
| return Math.max(Math.min(value, max_val), min_val); | |
| } | |
| function magnitude(vel) { | |
| return Math.sqrt(Math.pow(vel[0], 2) + Math.pow(vel[1], 2)); | |
| } | |
| function angle(x1, y1, x2, y2) { | |
| return Math.atan2(y2 - y1, x2 - x1); | |
| } | |
| function set_ball_angle(angle) { | |
| var speed = magnitude(ball_vel); | |
| ball_vel[0] = speed * Math.cos(angle); | |
| ball_vel[1] = speed * Math.sin(angle); | |
| } | |
| function speed_up_ball() { | |
| ball_vel[0] = clamp(ball_vel[0] + ball_vel[0] * speed_increase, -ball.max_speed, ball.max_speed) | |
| ball_vel[1] = clamp(ball_vel[1] + ball_vel[1] * speed_increase, -ball.max_speed, ball.max_speed) | |
| } | |
| function updateBall() { | |
| ball_pos[0] += ball_vel[0]; | |
| ball_pos[1] += ball_vel[1]; | |
| // check for top or bottom collision | |
| if (ball_pos[1] < 0) { | |
| ball_pos[1] = ball.radius; | |
| ball_vel[1] = -ball_vel[1]; | |
| } else if (ball_pos[1] > height - ball.radius) { | |
| ball_pos[1] = height - ball.radius; | |
| ball_vel[1] = -ball_vel[1]; | |
| } | |
| //check left gutter | |
| if (ball_pos[0] < paddle.width ) { | |
| // check for paddle | |
| if (pad1_pos-paddle.half_height-ball.radius<= ball_pos[1] && ball_pos[1]<= pad1_pos + paddle.half_height+ball.radius) { | |
| // p2 score | |
| // bounce | |
| console.log("Hit paddle"); | |
| ball_pos[0] = paddle.width ; | |
| speed_up_ball(); | |
| // set ball angle equal to the angle from the paddle to the ball | |
| //if the ball is nearer the edge of the paddle it gets deflected at a greater angle | |
| set_ball_angle(angle(paddle.half_width, pad1_pos, ball_pos[0], ball_pos[1])); | |
| start_electric_effect(); | |
| } | |
| else { | |
| console.log("!Hit"); | |
| score2 += 1; | |
| playSound(beepSound,0.9); | |
| ballInit(ball_pos[0] < width / 2); | |
| } | |
| } | |
| // | |
| // check right gutter | |
| else if (ball_pos[0]> width-paddle.width-ball.width) { | |
| // check for paddle | |
| if (pad2_pos-paddle.half_height-ball.radius<= ball_pos[1] && ball_pos[1]<= pad2_pos + paddle.half_height+ball.radius){ | |
| // bounce | |
| console.log("Hit paddle"); | |
| ball_pos[0] = width - paddle.width-ball.width; | |
| speed_up_ball(); | |
| set_ball_angle(angle(width - paddle.half_width, pad2_pos, ball_pos[0], ball_pos[1])); | |
| start_electric_effect(); | |
| } else { | |
| console.log("!hit"); | |
| score1 += 1; | |
| playSound(beepSound,0.9); | |
| ballInit(ball_pos[0] < width / 2); | |
| } | |
| } | |
| } | |
| function update_paddles() { | |
| if (comp_AI) //and ball_pos[0] < WIDTH / 2: | |
| if (pad1_pos + paddle.half_height < ball_pos[1]) pad1_vel = paddle.speed; | |
| else if (pad1_pos - paddle.half_height > ball_pos[1]) pad1_vel = -paddle.speed; | |
| else pad1_vel = 0; | |
| // paddle 1 | |
| pad1_pos += pad1_vel; | |
| if(pad1_pos < paddle.height/2) pad1_pos = paddle.height/2; | |
| else if (pad1_pos > height-paddle.height/2) pad1_pos = height-paddle.height/2; | |
| // paddle 2 | |
| pad2_pos += pad2_vel; | |
| if (pad2_pos < paddle.height/2) pad2_pos =paddle.height/2; | |
| else if (pad2_pos > height-paddle.height/2 ) pad2_pos = height-paddle.height/2; | |
| } | |
| function render(){ | |
| if(images_loaded){ | |
| frame.drawImage(background_img,0,0,width,height,0,0,width,height); | |
| update_paddles(); | |
| //mid lines | |
| frame.drawLine([width/2,gutter_count],[width/2,gutter_count+10],1,"red"); | |
| frame.drawLine([width/2,height-gutter_count],[width/2,height-gutter_count-10],1,"red"); | |
| //gutter left | |
| frame.drawLine([paddle.width,gutter_count],[paddle.width,gutter_count+10],1,"red"); | |
| frame.drawLine([width - paddle.width,gutter_count],[width-paddle.width,gutter_count+10],1,"red"); | |
| frame.drawLine([width - paddle.width,gutter_count],[width-paddle.width,gutter_count+10],1,"red"); | |
| // gutter right | |
| frame.drawLine( [paddle.width,height-gutter_count],[paddle.width,height-gutter_count-10],1,"red"); | |
| frame.drawLine([width - paddle.width,height-gutter_count],[width-paddle.width,height-gutter_count-10],1,"red"); | |
| frame.drawLine([width - paddle.width,height-gutter_count],[width-paddle.width,height-gutter_count-10],1,"red"); | |
| // frame.drawLine([10,60],[100,240],4,"red"); | |
| gutter_count = (gutter_count+10)%height; | |
| // draw paddles | |
| frame.draw_image(ball_eff1_img,[0,0], [ball_effects.effect1_width, ball_effects.effect1_height],[ball_pos[0]-40,ball_pos[1]-40], [ball_effects.effect1_width, ball_effects.effect1_height],random(1,360)); | |
| frame.drawImage(paddle_img,0,0,paddle.width,paddle.height,0,pad1_pos-paddle.height/2,paddle.width,paddle.height); | |
| frame.draw_image(paddle_img,[0,0],[paddle.width,paddle.height],[width-paddle.width,pad2_pos-paddle.height/2],[paddle.width,paddle.height],180); | |
| updateBall(); | |
| // var item; | |
| // for(var j=trail.length-1;j>=0;j--){ | |
| // item = trail[j]; | |
| // frame.draw_image(ball_eff1_img,[0,0],[ball_effects.effect1_width,ball_effects.effect1_height],item,[ball_effects.effect1_width/2,ball_effects.effect1_height/2]); | |
| // frame.draw_image(ball_effect2_img,[0,0],[ball_effects.effect2_width,ball_effects.effect1_height],item,[ball_effects.effect1_width/2,ball_effects.effect1_height/2]); | |
| // console.log(item); | |
| // } | |
| if(isRunning){ | |
| playSound(electric_sound,0.9); | |
| frame.draw_image(ball_effect2_img,[0,0], [ball_effects.effect2_width, ball_effects.effect2_height],[ball_pos[0]-280,ball_pos[1]-280], [ball_effects.effect2_width, ball_effects.effect2_height],random(1,360)); | |
| } | |
| frame.draw_image(ball_img,[0,0],[ball.radius*2,ball.radius*2],ball_pos,[ball.radius*2,ball.radius*2],random(1,360)); | |
| frame.draw_image(ball_img,[0,0],[ball.radius*2,ball.radius*2],ball_pos,[ball.radius*2,ball.radius*2],random(45,360)); | |
| // scores | |
| frame.font = "18px digitek"; | |
| frame.fillStyle="red"; | |
| frame.fillText("SCORE : "+score1,60,30); | |
| frame.fillText("SCORE : "+score2,380,30); | |
| frame.fill(); | |
| } | |
| else{ | |
| frame.save(); | |
| frame.fillStyle = "black"; | |
| frame.fillRect(0,0,canvas.width,canvas.height); | |
| frame.drawImage(animLoader,72*i,0,72,72,canvas.width/2-36,canvas.height/2-36,72,72); | |
| frame.fillStyle = "red"; | |
| frame.font="24px slicker"; | |
| frame.shadowOffsetX=0; | |
| frame.shadowOffsetY=1; | |
| frame.shadowBlur=0; | |
| frame.shadowColor="grey"; | |
| frame.beginPath(); | |
| frame.fillText("Please wait....",250-20,250+60); | |
| frame.fillStyle = "aqua"; | |
| frame.shadowColor="darkgreen"; | |
| frame.fillText("Loading Resources : "+resLoaded+"/"+totalRes,170,250+90); | |
| frame.font="34px potter"; | |
| frame.fillStyle="red"; | |
| frame.fillText("PONG CLASSIC",190,130); | |
| frame.restore(); | |
| i++; | |
| if(i>9) i=0; | |
| } | |
| } | |
| function restart(){ | |
| comp_AI = false; | |
| h1.innerHTML = "Computer AI : OFF"; | |
| newGame(); | |
| } | |
| function toggle_ai(){ | |
| comp_AI = !comp_AI; | |
| // set Text | |
| if(comp_AI)keydown=true; | |
| else keydown = false; | |
| newGame(); | |
| } | |
| // timers | |
| function Collision_effect_tick(){ | |
| window.clearInterval(collision_effect_timer); | |
| isRunning = false; | |
| } | |
| function trail_timer_tick(){ | |
| randomVal = random(1,5); | |
| if(randomVal<3){ | |
| if(trail.length<max_particles) trail.push(ball_pos); | |
| } | |
| else if(trail.length>0){ | |
| trail.splice(random(0,trail.length),1); | |
| } | |
| } | |
| newGame(); | |
| window.setInterval(render,33); | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @font-face { | |
| font-family: digitek; | |
| src: url("https://dl.dropboxusercontent.com/u/224687431/DIGITEK.TTF"); | |
| } | |
| @font-face { | |
| font-family: slicker; | |
| src: url("https://dl.dropboxusercontent.com/u/224687431/Slicker.ttf"); | |
| } | |
| @font-face { | |
| font-family: potter; | |
| src: url("https://dl.dropboxusercontent.com/u/224687431/Harry%20Potter.ttf"); | |
| } | |
| h1{ | |
| font-family: slicker; | |
| color:crimson; | |
| font-size:20px; | |
| position: absolute; | |
| width:250px; | |
| height: 50px; | |
| text-align: center; | |
| top:500px; | |
| left: 225px; | |
| } | |
| .controls{ | |
| background:linear-gradient(to left,black,grey,black); | |
| box-shadow: none; | |
| color:black; | |
| font: 20px bold, slicker; | |
| width: 120px; | |
| height: 50px; | |
| border: 1px solid grey; | |
| text-shadow: 0px 1px 0px white; | |
| } | |
| .controls:hover{ | |
| color: aqua; | |
| background:linear-gradient(to top,black,grey,black); | |
| text-shadow: 0px 1px 0px black; | |
| cursor: pointer; | |
| } | |
| #control{ | |
| top: 120px; | |
| font-weight:bold; | |
| font-family: Tahoma "Gloria Hallelujah" !important; | |
| float:right; | |
| position: absolute; | |
| left:710px; | |
| border: 1px solid black; | |
| border-radius:30px 0px 30px 0px ; | |
| padding:10px; | |
| box-shadow: 2px 3px 18px black; | |
| } | |
| #control:hover{ | |
| cursor: crosshair; | |
| border-radius: 40px; | |
| border:2px dashed crimson; | |
| } | |
| div p u{ | |
| color:green; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment