Last active
December 1, 2017 13:30
-
-
Save lilgreenland/1759716b325910e98b4afb62fcddafd9 to your computer and use it in GitHub Desktop.
How to Code Pong with JavaScript
This file contains 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
<h1>How to Code Pong with JavaScript</h1> | |
<p class='section-title'><strong>HTML</strong></p> | |
<pre><code class="language-javascript"><canvas id="canvas-pong"></canvas> | |
</code></pre> | |
<p class='section-title'><strong>CSS</strong></p> | |
<pre><code class="language-css-extras">canvas { | |
border: 1px solid grey; | |
margin: auto; | |
display: block; | |
} | |
</code></pre> | |
<p class='code-title'> | |
<p class='section-title'><strong>JavaScript</strong></p> | |
//setup the canvas</p> | |
<pre><code class="language-javascript">var canvas = document.getElementById('canvas-pong'); | |
var ctx = canvas.getContext("2d"); | |
canvas.width = 600; | |
canvas.height = 300; | |
</code></pre> | |
<p class='code-title'>//keep track of which keys are up or down</p> | |
<pre><code class="language-javascript">var keys = []; | |
document.onkeydown = function(e){ | |
keys[e.keyCode] = true; | |
//console.log(e.keyCode); | |
} | |
document.onkeyup = function(e){ | |
keys[e.keyCode] = false; | |
} | |
</code></pre> | |
<p class='code-title'>//game objects</p> | |
<pre><code class="language-javascript">const p1 = { | |
x: 20, | |
y: canvas.height / 2 - 25, | |
h: 50, | |
w: 3, | |
speed: 2, | |
color: "#f0a", | |
score: 0 | |
}; | |
const p2 = { | |
x: canvas.width - 20, | |
y: canvas.height / 2 - 25, | |
h: 50, | |
w: 3, | |
speed: 2, | |
color: "#0af", | |
score: 0 | |
}; | |
const ball = { | |
x: 0, | |
y: 0, | |
r: 3, | |
Vx: 0, | |
Vy: 0, | |
speed: 2, | |
color: "#000" | |
}; | |
</code></pre> | |
<p class='code-title'>//reset the ball's position and randomize the velocity<br>//called at the start and after each score</p> | |
<pre><code class="language-javascript">function resetBall() { | |
ball.x = canvas.width / 2; | |
ball.y = canvas.height / 2; | |
if (Math.random() < 0.5) { | |
ball.Vx = ball.speed; | |
} else { | |
ball.Vx = -ball.speed; | |
} | |
if (Math.random() < 0.5) { | |
ball.Vy = ball.speed; | |
} else { | |
ball.Vy = -ball.speed; | |
} | |
} | |
resetBall(); | |
</code></pre> | |
<p class='code-title'>//draw paddle and ball</p> | |
<pre><code class="language-javascript">function draw() { | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.fillStyle = p1.color; | |
ctx.fillRect(p1.x-p1.w, p1.y, p1.w, p1.h); | |
ctx.fillStyle = p2.color; | |
ctx.fillRect(p2.x, p2.y, p2.w, p2.h); | |
ctx.beginPath(); | |
ctx.arc(ball.x, ball.y, ball.r, 0, 2 * Math.PI); | |
ctx.fillStyle = ball.color; | |
ctx.fill(); | |
} | |
</code></pre> | |
<p class='code-title'>//move paddles if keys are down</p> | |
<pre><code class="language-javascript">function keyChecks() { | |
//player 1: left side | |
if (keys[81]) { // q | |
p1.y -= p1.speed; | |
} | |
if (keys[65]) { // a | |
p1.y += p1.speed; | |
} | |
//player 2: right side | |
if (keys[80]) { // p | |
p2.y -= p2.speed; | |
} | |
if (keys[76]) { // l | |
p2.y += p2.speed; | |
} | |
} | |
</code></pre> | |
<p class='code-title'>//motion of ball and wall behavior</p> | |
<pre><code class="language-javascript">function ballMove() { | |
ball.x += ball.Vx; | |
ball.y += ball.Vy; | |
//vertical wall collision | |
if (ball.y - ball.r < 0) { | |
ball.Vy = ball.speed; | |
} | |
if (ball.y + ball.r > canvas.height) { | |
ball.Vy = -ball.speed; | |
} | |
//horizontal wall collision, scoring | |
if (ball.x - ball.r < 0) { | |
p2.score++; | |
resetBall(); | |
} | |
if (ball.x + ball.r > canvas.width) { | |
p1.score++; | |
resetBall(); | |
} | |
} | |
</code></pre> | |
<p class='code-title'>//ball bounces off paddles</p> | |
<pre><code class="language-javascript">function paddleBallCollision() { | |
//left paddle collisions | |
if (ball.x - ball.r < p1.x && | |
ball.x - ball.r > p1.x - ball.speed*2 && | |
ball.y > p1.y && | |
ball.y < p1.y + p1.h) { | |
ball.Vx = ball.speed; | |
} | |
//right paddle collisions | |
if (ball.x + ball.r > p2.x && | |
ball.x + ball.r < p2.x + ball.speed*2 && | |
ball.y > p2.y && | |
ball.y < p2.y + p2.h) { | |
ball.Vx = -ball.speed; | |
} | |
} | |
</code></pre> | |
<p class='code-title'>//animation loop (all active code goes here as a function call) | |
<br></p> | |
<pre><code class="language-javascript">function cycle() { | |
//activate these function calls | |
//draw() | |
//keyChecks() | |
//ballMove() | |
//paddleBallCollision() | |
requestAnimationFrame(cycle); | |
} | |
requestAnimationFrame(cycle); | |
</code></pre> | |
<div class='code-title'> | |
<h1 class='section-title'>improvement ideas</h1> | |
change default settings: size, colors, speeds, starting positions | |
<br>randomize: play and ball values | |
<br>add a score board | |
<br>increase ball speed after each goal | |
<br>go full screen | |
<br>don't let paddles scroll off screen | |
<br>around the world wall wrap | |
<br>shrink/grow paddles after each goal | |
<br>graphics for: ball, paddle, background, goals | |
<br>change colors after hitting the ball | |
<br>sound effects | |
<br>ball bounces off of the paddle at different angles | |
<br>pick a winner after enough points | |
<br>power ups (speed, paddle size) | |
<br>paddles move horizontally | |
<br>pause the game | |
<br>multiple balls | |
<br>paddles can fire extra balls | |
<br>paddles hold balls until they fire them | |
<br>ball also bounces off top and bottom of paddes, not just front | |
<br>worm holes in the middle of the field | |
<br>replace one player with blocks to make breakout | |
<br>add more players: on the sides, top and bottom, in middle | |
<br>change player inputs: mouse, keys, microphone, ... | |
<br>phone specific inputs: GPS, orientation, acceleration | |
<br>computer player(s) | |
</div> |
This file contains 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
var canvas = document.getElementById("canvas-1"); | |
var ctx = canvas.getContext("2d"); | |
//_____get keyboard input______ | |
var keys = []; | |
document.onkeydown = function(e) { | |
keys[e.keyCode] = true; | |
console.log(e.keyCode); | |
}; | |
document.onkeyup = function(e) { | |
keys[e.keyCode] = false; | |
}; | |
//_____animation loop _________ | |
function cycle() { | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
draw(); | |
keyChecks(); | |
requestAnimationFrame(cycle); | |
} | |
requestAnimationFrame(cycle); | |
//_______player objects________ | |
const p1 = { | |
x: 5, | |
y: 0, | |
h: 20, | |
w: 5, | |
color: "#f0a", | |
score: 0 | |
}; | |
const p2 = { | |
x: canvas.width - 10, | |
y: 0, | |
h: 20, | |
w: 5, | |
color: "#0af", | |
score: 0 | |
}; | |
const ball = { | |
x:0, | |
y:0, | |
r:5, | |
velocity: 0, | |
color: "#000" | |
} | |
function draw() { | |
ctx.fillStyle = p1.color; | |
ctx.fillRect(p1.x, p1.y, p1.w, p1.h); | |
ctx.fillStyle = p2.color; | |
ctx.fillRect(p2.x, p2.y, p2.w, p2.h); | |
} | |
function keyChecks() { | |
//player 1: left side | |
if (keys[81]) { | |
//81 => q key up | |
p1.y -= 1; | |
} | |
if (keys[65]) { | |
//65 => a key down | |
p1.y += 1; | |
} | |
//player 2: right side | |
if (keys[80]) { | |
//80 => o key up | |
p2.y -= 1; | |
} | |
if (keys[76]) { | |
//76 => l key down | |
p2.y += 1; | |
} | |
} |
This file contains 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
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/464612/prism.js"></script> |
This file contains 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
body { | |
font-family: "Helvetica", "Arial", sans-serif; | |
color: #000; | |
background-color: #fff; | |
line-height:1.4; | |
padding: 0.1em; | |
margin: 0 auto; | |
max-width: 550px; | |
} | |
.code-title{ | |
font-size: 120%; | |
padding: 1.5em 0em 0em 0em; | |
margin: 0; | |
} | |
.section-title{ | |
text-align: center; | |
font-size: 150%; | |
} | |
/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript&plugins=toolbar */ | |
/** | |
* okaidia theme for JavaScript, CSS and HTML | |
* Loosely based on Monokai textmate theme by http://www.monokai.nl/ | |
* @author ocodia | |
*/ | |
code[class*="language-"], | |
pre[class*="language-"] { | |
color: #f8f8f2; | |
background: none; | |
text-shadow: 0 1px rgba(0, 0, 0, 0.3); | |
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; | |
text-align: left; | |
white-space: pre; | |
word-spacing: normal; | |
word-break: normal; | |
word-wrap: normal; | |
line-height: 1.5; | |
-moz-tab-size: 4; | |
-o-tab-size: 4; | |
tab-size: 4; | |
-webkit-hyphens: none; | |
-moz-hyphens: none; | |
-ms-hyphens: none; | |
hyphens: none; | |
} | |
/* Code blocks */ | |
pre[class*="language-"] { | |
padding: 1em; | |
margin: .5em 0; | |
overflow: auto; | |
border-radius: 0.3em; | |
} | |
:not(pre) > code[class*="language-"], | |
pre[class*="language-"] { | |
background: #272822; | |
} | |
/* Inline code */ | |
:not(pre) > code[class*="language-"] { | |
padding: .1em; | |
border-radius: .3em; | |
white-space: normal; | |
} | |
.token.comment, | |
.token.prolog, | |
.token.doctype, | |
.token.cdata { | |
color: slategray; | |
} | |
.token.punctuation { | |
color: #f8f8f2; | |
} | |
.namespace { | |
opacity: .7; | |
} | |
.token.property, | |
.token.tag, | |
.token.constant, | |
.token.symbol, | |
.token.deleted { | |
color: #f92672; | |
} | |
.token.boolean, | |
.token.number { | |
color: #ae81ff; | |
} | |
.token.selector, | |
.token.attr-name, | |
.token.string, | |
.token.char, | |
.token.builtin, | |
.token.inserted { | |
color: #a6e22e; | |
} | |
.token.operator, | |
.token.entity, | |
.token.url, | |
.language-css .token.string, | |
.style .token.string, | |
.token.variable { | |
color: #f8f8f2; | |
} | |
.token.atrule, | |
.token.attr-value, | |
.token.function { | |
color: #e6db74; | |
} | |
.token.keyword { | |
color: #66d9ef; | |
} | |
.token.regex, | |
.token.important { | |
color: #fd971f; | |
} | |
.token.important, | |
.token.bold { | |
font-weight: bold; | |
} | |
.token.italic { | |
font-style: italic; | |
} | |
.token.entity { | |
cursor: help; | |
} | |
pre.code-toolbar { | |
position: relative; | |
} | |
pre.code-toolbar > .toolbar { | |
position: absolute; | |
top: .3em; | |
right: .2em; | |
transition: opacity 0.3s ease-in-out; | |
opacity: 0; | |
} | |
pre.code-toolbar:hover > .toolbar { | |
opacity: 1; | |
} | |
pre.code-toolbar > .toolbar .toolbar-item { | |
display: inline-block; | |
} | |
pre.code-toolbar > .toolbar a { | |
cursor: pointer; | |
} | |
pre.code-toolbar > .toolbar button { | |
background: none; | |
border: 0; | |
color: inherit; | |
font: inherit; | |
line-height: normal; | |
overflow: visible; | |
padding: 0; | |
-webkit-user-select: none; /* for button */ | |
-moz-user-select: none; | |
-ms-user-select: none; | |
} | |
pre.code-toolbar > .toolbar a, | |
pre.code-toolbar > .toolbar button, | |
pre.code-toolbar > .toolbar span { | |
color: #bbb; | |
font-size: .8em; | |
padding: 0 .5em; | |
background: #f5f2f0; | |
background: rgba(224, 224, 224, 0.2); | |
box-shadow: 0 2px 0 0 rgba(0,0,0,0.2); | |
border-radius: .5em; | |
} | |
pre.code-toolbar > .toolbar a:hover, | |
pre.code-toolbar > .toolbar a:focus, | |
pre.code-toolbar > .toolbar button:hover, | |
pre.code-toolbar > .toolbar button:focus, | |
pre.code-toolbar > .toolbar span:hover, | |
pre.code-toolbar > .toolbar span:focus { | |
color: inherit; | |
text-decoration: none; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment