Skip to content

Instantly share code, notes, and snippets.

@lilgreenland
Last active December 1, 2017 13:30
Show Gist options
  • Save lilgreenland/1759716b325910e98b4afb62fcddafd9 to your computer and use it in GitHub Desktop.
Save lilgreenland/1759716b325910e98b4afb62fcddafd9 to your computer and use it in GitHub Desktop.
How to Code Pong with JavaScript
<h1>How to Code Pong with JavaScript</h1>
<p class='section-title'><strong>HTML</strong></p>
<pre><code class="language-javascript">&lt;canvas id="canvas-pong"&gt;&lt;/canvas&gt;
</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>
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;
}
}
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/464612/prism.js"></script>
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