Skip to content

Instantly share code, notes, and snippets.

@straker
Last active September 9, 2024 20:33
Show Gist options
  • Save straker/0d25ae9d235f6a62f8287fd36a097043 to your computer and use it in GitHub Desktop.
Save straker/0d25ae9d235f6a62f8287fd36a097043 to your computer and use it in GitHub Desktop.
Basic Classic Helicopter HTML and JavaScript Game

Basic Classic Helicopter HTML and JavaScript Game

This is a basic implementation of the classic Helicopter game, but it's missing a few things intentionally and they're left as further exploration for the reader.

Further Exploration

  • Score / Distance
    • As the helicopter moves right, the score / distance should increase. Use context.fillText() to display the score / distance to the screen
    • Display a high score using localSorage
  • Wall Gap Size
    • As the game progresses, the distance between the top and bottom wall should get smaller
  • Mobile and touchscreen support

Important note: I will answer questions about the code but will not add more features or answer questions about adding more features. This series is meant to give a basic outline of the game but nothing more.

License

(CC0 1.0 Universal) You're free to use this game and code in any project, personal or commercial. There's no need to ask permission before using these. Giving attribution is not required, but appreciated.

Other Basic Games

Support

Basic HTML Games are made possible by users like you. When you become a Patron, you get access to behind the scenes development logs, the ability to vote on which games I work on next, and early access to the next Basic HTML Game.

Top Patrons

  • Karar Al-Remahy
  • UnbrandedTech
  • Innkeeper Games
  • Nezteb
<!DOCTYPE html>
<html>
<head>
<title>Basic Helicopter HTML Game</title>
<meta charset="UTF-8">
<style>
html, body {
height: 100%;
margin: 0;
}
body {
background: black;
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<canvas width="800" height="550" id="game"></canvas>
<script>
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
const minTunnelWidth = 400;
const maxTunnelWidth = canvas.width;
const minHeight = 10;
const maxHeight = 100;
const obstacleWidth = 65;
const obstacleHeight = 135;
// how fast the background moves
const moveSpeed = 7;
// downward acceleration
const gravity = 0.35;
// keep track of the spacebar being pressed so we can move the
// helicopter up when pressed and down when not pressed
let spacePressed = false;
// clamp a number between min and max values
function clamp(num, min, max) {
return Math.min( Math.max(min, num), max);
}
// return a random integer between min (inclusive) and max (inclusive)
// @see https://stackoverflow.com/a/1527820/2124254
function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const helicopter = {
x: 200,
y: 100,
width: 100,
height: 60,
dy: 0, // velocity
ddy: 0 // acceleration
};
// just keep track of a tunnel wall current x position, width, start
// and end height. the top and bottom wall are mirrored, so we only
// need to keep track of one of them
let tunnels = [{
x: 0,
width: canvas.width,
start: 50,
end: 50
},
{
x: canvas.width,
width: randInt(minTunnelWidth, maxTunnelWidth),
start: 50,
end: randInt(minHeight, maxHeight)
}];
// for the obstacles in the path just need to keep track of the
// position as they are always the same size
let obstacles = [{
x: canvas.width,
y: canvas.height / 2
},
{
x: canvas.width * 2,
y: canvas.height / 2
}];
// tunnel wall color and rgb value
const wallColor = 'green';
context.fillStyle = wallColor;
context.fillRect(0, 0, 1, 1);
// getImageData returns a data object which is a flat array of every
// pixel of the canvas in the specified rect (x, y, width, height).
// every 4 indices of the array is a single pixel's r,g,b,a values
const wallData = context.getImageData(0, 0, 1, 1);
// destructure the image data array to get the rgb values of the wall
// @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const [ wallRed, wallGreen, wallBlue ] = wallData.data;
// game loop
let rAF;
function loop() {
rAF = requestAnimationFrame(loop);
context.clearRect(0,0,canvas.width,canvas.height);
if (spacePressed) {
helicopter.ddy = -0.7;
}
else {
helicopter.ddy = 0;
}
// update position based on acceleration and velocity
helicopter.dy += helicopter.ddy + gravity;
// clamp velocity
helicopter.dy = clamp(helicopter.dy, -8, 8);
helicopter.y += helicopter.dy;
// draw the helicopter
context.fillStyle = 'white';
context.fillRect(helicopter.x, helicopter.y, helicopter.width, helicopter.height);
// draw the tunnel walls over the helicopter
context.fillStyle = 'green';
tunnels.forEach((tunnel, index) => {
tunnel.x -= moveSpeed;
// if the last tunnel is fully on screen, we need to spawn a new
// tunnel segment off screen
if (
index === tunnels.length - 1 &&
tunnel.x + tunnel.width <= canvas.width
) {
tunnels.push({
x: tunnel.x + tunnel.width,
width: randInt(minTunnelWidth, maxTunnelWidth),
start: tunnel.end,
end: randInt(minHeight, maxHeight)
});
}
// top tunnel wall
context.beginPath();
context.moveTo(tunnel.x, 0);
context.lineTo(tunnel.x, tunnel.start);
context.lineTo(tunnel.x + tunnel.width, tunnel.end);
context.lineTo(tunnel.x + tunnel.width, 0);
context.closePath();
context.fill();
// bottom tunnel wall
context.beginPath();
context.moveTo(tunnel.x, canvas.height);
context.lineTo(tunnel.x, tunnel.start + 450);
context.lineTo(tunnel.x + tunnel.width, tunnel.end + 450);
context.lineTo(tunnel.x + tunnel.width, canvas.height);
context.closePath();
context.fill();
});
// draw obstacles
obstacles.forEach((obstacle, index) => {
obstacle.x -= moveSpeed;
context.fillRect(obstacle.x, obstacle.y, obstacleWidth, obstacleHeight);
// if the last obstacle is fully on screen, we need to spawn a new
// one off screen
if (
index === obstacles.length - 1 &&
obstacle.x + obstacleWidth <= canvas.width
) {
obstacles.push({
x: canvas.width * 2,
y: randInt(maxHeight + 50, canvas.height - obstacleHeight - maxHeight - 50)
});
}
});
// remove any tunnel segments or obstacles that are off screen
tunnels = tunnels.filter(tunnel => tunnel.x + tunnel.width > 0);
obstacles = obstacles.filter(obstacle => obstacle.x + obstacleWidth > 0);
// pixel perfect collision detection
// get the pixels of the canvas at the helicopter rect and look for
// any pixels that match the wall color. the wall has to be drawn
// above the helicopter in order for this to work
const { data } = context.getImageData(helicopter.x, helicopter.y, helicopter.width, helicopter.height);
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// if we match the tunnel wall color we have a collision
if (r === wallRed && g === wallGreen && b === wallBlue) {
// draw a dotted red circle around the helicopter when it
// crashes
context.strokeStyle = 'red';
context.setLineDash([5, 15])
context.lineWidth = 4;
context.beginPath();
context.arc(helicopter.x + helicopter.width / 2, helicopter.y + helicopter.height / 2, helicopter.width, 0, 2 * Math.PI);
context.stroke();
cancelAnimationFrame(rAF);
}
}
}
// listen to keyboard events to move the helicopter
document.addEventListener('keydown', function(e) {
// spacebar
if (e.code === 'Space') {
spacePressed = true;
}
});
document.addEventListener('keyup', function(e) {
// spacebar
if (e.code === 'Space') {
spacePressed = false;
}
});
// start the game
rAF = requestAnimationFrame(loop);
</script>
</body>
</html>
@Halo-76
Copy link

Halo-76 commented Sep 10, 2023

How to i edit it to slow it down

Nevermind

@Halo-76
Copy link

Halo-76 commented Sep 10, 2023

@akasula09 Thanks! I'm glad you liked my work. Do you have an example of an exploration game? Just want to make sure I understand the ask.

How do you change the upward acceleration?

@Halo-76
Copy link

Halo-76 commented Sep 10, 2023

@Halo-76 https://gist.github.com/straker/0d25ae9d235f6a62f8287fd36a097043#file-helicopter-html-L112

Also, where can i add code so when the game ends it runs the code

@AlinaMuething
Copy link

(i do not know how to code, but tried using this to game) when the spacebar is pressed it restarts the game, but it's also used to control the helicopter. how would I try to 'fix' this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment