diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/hill/game.js | 487 |
1 files changed, 274 insertions, 213 deletions
diff --git a/js/hill/game.js b/js/hill/game.js index b9c2e14..0e960a9 100644 --- a/js/hill/game.js +++ b/js/hill/game.js @@ -4,235 +4,282 @@ canvas.height = window.innerHeight; canvas.style.backgroundColor = '#f0f0f0'; const ctx = canvas.getContext('2d'); -/* -const COLORS = { - player: '#c0c0c0', - particles: '#c0c0c0', - objects: '#e0e0e0', - terrain: '#e0e0e0' -}; -*/ - -const COLORS = { - player: '#2d2d2d', - particles: '#2d2d2d', - objects: '#2d2d2d', - terrain: '#2d2d2d' -}; - - -const gravity = 1; -const jumpForce = 14.75; - -const player = { - x: 200, - y: 0, - vx: 16, - vy: 16, - vm: 32, - jumpForce: gravity + jumpForce, - onGround: false, - width: 20, - height: 20 -}; - -const terrain = { - start: { x: 0, y: 0 }, - end: { x: canvas.width, y: canvas.height }, - height: 8 -}; - -const objects = []; -let lastObjectX = 0; - -const getRandomInterval = (min, max) => { - return Math.floor(Math.random() * (max - min + 1) + min); -} - -const createObject = () => { - const object = { - x: lastObjectX + 10 * canvas.width / 10, - width: Math.random() * 100, - height: Math.random() * 22 +(function(){ + + const COLORS = { + player: '#2d2d2d', + particles: '#2d2d2d', + objects: '#2d2d2d', + terrain: '#2d2d2d' }; - let terrainY = ((terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x)) * (object.x - terrain.start.x) + terrain.start.y; + const gravity = 1; + const jumpForce = 14.75; + + const player = { + x: 200, + y: 0, + vx: 16, + vy: 16, + vm: 32, + jumpForce: gravity + jumpForce, + onGround: false, + width: 20, + height: 20, + distanceTraveled: 0 + }; - object.y = terrainY - object.height; + const terrain = { + start: { x: 0, y: 0 }, + end: { x: canvas.width, y: canvas.height }, + height: 8 + }; - objects.push(object); + const objects = []; + let lastObjectX = 0; - lastObjectX = object.x; -}; + const particles = []; + const createObject = () => { + const object = { + x: lastObjectX + 10 * canvas.width / 10, + width: Math.random() * 100, + height: Math.random() * 22 + }; -// Shout out to Josh Grams! -let lastUpdateTime = 0; -const fps = 60; -const frameDelay = 1000 / fps; + let terrainY = ((terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x)) * (object.x - terrain.start.x) + terrain.start.y; -const update = (currentTime = 0) => { - const deltaTime = currentTime - lastUpdateTime; + object.y = terrainY - object.height; - if (deltaTime >= frameDelay) { - createObject(); - lastUpdateTime = currentTime; - } + objects.push(object); - requestAnimationFrame(update); -}; + lastObjectX = object.x; + }; -update(); + const isColliding = (obj1, obj2) => ( + obj1.x < obj2.x + obj2.width && + obj1.x + obj1.width > obj2.x && + obj1.y < obj2.y + obj2.height && + obj1.y + obj1.height > obj2.y + ); + + const checkCollision = () => { + objects.forEach(object => { + if (isColliding(player, object)) { + if (player.vx >= 1) { + player.vx -= 0.5; + player.vy -= 0.5; + } + } + }); + }; -const initialize = () => { - terrain.start = { x: 0, y: 0 }; - terrain.end = { x: canvas.width, y: canvas.height }; -}; + const updatePlayer = () => { + player.vy += gravity; -const drawPlayer = () => { + player.x += player.vx; + player.y += player.vy; - if (player.vx < 4) { - ctx.fillStyle = COLORS.player; - ctx.font = '22px Arial'; - const text = 'Jump to build up speed!'; - ctx.fillText(text, player.x, player.y - 12); - } - - ctx.fillStyle = COLORS.player; - ctx.fillRect(player.x, player.y, player.width, player.height); -}; - -const drawObjects = () => { - ctx.fillStyle = COLORS.objects; - objects.forEach(object => { - ctx.beginPath(); - ctx.arc(object.x, object.y, object.width / 2, 0, Math.PI * 2, true); - ctx.fill(); - }); -}; - -const drawTerrain = () => { - ctx.beginPath(); - ctx.moveTo(terrain.start.x, terrain.start.y); - ctx.lineTo(terrain.end.x, terrain.end.y); - ctx.strokeStyle = COLORS.terrain; - ctx.lineWidth = terrain.height; - ctx.stroke(); -}; - -const drawParticles = () => { - ctx.fillStyle = COLORS.particles; - particles.forEach((particle, index) => { - ctx.fillRect(particle.x, particle.y, 5, 5); - particle.x += particle.vx; - particle.y += particle.vy; - particle.lifespan--; - if (particle.lifespan === 0) { - particles.splice(index, 1); + const terrainY = (terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x) * (player.x - terrain.start.x) + terrain.start.y; + + if (player.y + player.height > terrainY) { + player.y = terrainY - player.height; + player.vy = 0; } - }); -}; - -const draw = () => { - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.save(); - ctx.translate(-player.x + canvas.width / 4, -player.y + canvas.height / 2); - drawPlayer(); - terrain.start.x = player.x - canvas.width; - terrain.end.x = player.x + canvas.width; - drawObjects(); - drawTerrain(); - drawParticles(); - ctx.restore(); -}; - -const isColliding = (obj1, obj2) => ( - obj1.x < obj2.x + obj2.width && - obj1.x + obj1.width > obj2.x && - obj1.y < obj2.y + obj2.height && - obj1.y + obj1.height > obj2.y -); - -const checkCollision = () => { - objects.forEach(object => { - if (isColliding(player, object)) { - if (player.vx >= 1) { - player.vx -= 0.5; - player.vy -= 0.5; + + if (particlesEnabled) { + particles.push({ + x: player.x, + y: player.y, + vx: Math.random() * 2 - 1, + vy: Math.random() * 2 - 1, + lifespan: player.vx * 4 + }); + } + }; + + const updateObjects = () => { + for (let object of objects) { + object.y += gravity; + + let terrainY = ((terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x)) * (object.x - terrain.start.x) + terrain.start.y; + + if (object.y + object.height > terrainY) { + object.y = terrainY - object.height; } } - }); -}; + }; -const particles = []; + const drawPlayer = () => { + + if (player.vx < 4) { + ctx.fillStyle = COLORS.player; + ctx.font = '22px Arial'; + const text = 'Jump to build up speed!'; + ctx.fillText(text, player.x, player.y - 12); + } else { + if (distanceMeterEnabled) { + ctx.fillStyle = '#aaa'; + ctx.font = '22px Arial'; + const text = player.distanceTraveled.toFixed(0) + ' px'; + ctx.fillText(text, player.x, player.y - 12); + } + } + + ctx.fillStyle = COLORS.player; + ctx.fillRect(player.x, player.y, player.width, player.height); + }; -const gameLoop = () => { - player.vy += gravity; + const drawObjects = () => { + ctx.fillStyle = COLORS.objects; + objects.forEach(object => { + ctx.beginPath(); + ctx.arc(object.x, object.y, object.width / 2, 0, Math.PI * 2, true); + ctx.fill(); + }); + }; - player.x += player.vx; - player.y += player.vy; + const drawTerrain = () => { + ctx.beginPath(); + ctx.moveTo(terrain.start.x, terrain.start.y); + ctx.lineTo(terrain.end.x, terrain.end.y); + ctx.strokeStyle = COLORS.terrain; + ctx.lineWidth = terrain.height; + ctx.stroke(); + }; - const terrainY = (terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x) * (player.x - terrain.start.x) + terrain.start.y; + const drawParticles = () => { + if (particlesEnabled) { + ctx.fillStyle = COLORS.particles; + particles.forEach((particle, index) => { + ctx.fillRect(particle.x, particle.y, 5, 5); + particle.x += particle.vx; + particle.y += particle.vy; + particle.lifespan--; + if (particle.lifespan === 0) { + particles.splice(index, 1); + } + }); + } + }; - if (player.y + player.height > terrainY) { - player.y = terrainY - player.height; - player.vy = 0; - } - for (let object of objects) { - object.y += gravity; + const draw = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.save(); + ctx.translate(-player.x + canvas.width / 4, -player.y + canvas.height / 2); + drawPlayer(); + terrain.start.x = player.x - canvas.width; + terrain.end.x = player.x + canvas.width; + drawObjects(); + drawTerrain(); + drawParticles(); + ctx.restore(); + }; - let terrainY = ((terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x)) * (object.x - terrain.start.x) + terrain.start.y; + const update = (currentTime = 0) => { + const deltaTime = currentTime - lastUpdateTime; - if (object.y + object.height > terrainY) { - object.y = terrainY - object.height; + player.distanceTraveled = Math.abs(player.x - 200); + + if (deltaTime >= frameDelay) { + createObject(); + updatePlayer(); + updateObjects(); + checkCollision(); + lastUpdateTime = currentTime; } - } - - particles.push({ - x: player.x, - y: player.y, - vx: Math.random() * 2 - 1, - vy: Math.random() * 2 - 1, - lifespan: player.vx * 4 - }); - checkCollision(); - initialize(); - draw(); - requestAnimationFrame(gameLoop); -}; + requestAnimationFrame(update); + }; + + const initialize = () => { + terrain.start = { x: 0, y: 0 }; + terrain.end = { x: canvas.width, y: canvas.height }; + }; + + const gameLoop = () => { + initialize(); + draw(); + requestAnimationFrame(gameLoop); + }; -gameLoop(); + const playerDoJump = () => { + const terrainY = (terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x) * (player.x - terrain.start.x) + terrain.start.y; + if (player.y + player.height >= terrainY) { + player.vy = player.jumpForce * -1; + if (player.vx < player.vm) { + player.vx += 0.5; + } + } + }; -const playerDoJump = () => { - const terrainY = (terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x) * (player.x - terrain.start.x) + terrain.start.y; - if (player.y + player.height >= terrainY) { - player.vy = player.jumpForce * -1; - if (player.vx < player.vm) { - player.vx += 0.5; + const handleKeyDown = event => { + if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowUp' || event.key === 'w' || event.key === 'W') { + playerDoJump(); } - } -}; - -const handleKeyDown = event => { - if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowUp' || event.key === 'w' || event.key === 'W') { - playerDoJump(); - } -}; - -canvas.addEventListener('click', playerDoJump); -canvas.addEventListener('touchstart', playerDoJump); -window.addEventListener('keydown', handleKeyDown); - -window.addEventListener('resize', () => { - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - draw(); -}); - -document.addEventListener('keydown', function(event) { - if (event.key === 'f') { + }; + + canvas.addEventListener('click', playerDoJump); + canvas.addEventListener('touchstart', playerDoJump); + window.addEventListener('keydown', handleKeyDown); + + window.addEventListener('resize', () => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + draw(); + }); + + const helpText = 'Controls:\n\nJump: space, up arrow, w or tap the screen on mobile\nFullscreen: f or the button in the upper-right\nParticles: p\nDistance: d\nHelp: h' + + document.addEventListener('keydown', function(event) { + if (event.key === 'f') { + if (canvas.requestFullscreen) { + canvas.requestFullscreen(); + } else if (canvas.mozRequestFullScreen) { + canvas.mozRequestFullScreen(); + } else if (canvas.webkitRequestFullscreen) { + canvas.webkitRequestFullscreen(); + } else if (canvas.msRequestFullscreen) { + canvas.msRequestFullscreen(); + } + } else if (event.key === 'p' || event.key === 'P') { + particlesEnabled = !particlesEnabled; + } else if (event.key === 'h' || event.key === 'H') { + alert(helpText); + } else if (event.key === 'd' || event.key === 'D') { + distanceMeterEnabled = !distanceMeterEnabled; + } + }); + + let particlesEnabled = true; + let distanceMeterEnabled = false; + + const fullscreenButton = document.createElement('button'); + fullscreenButton.style.position = 'absolute'; + fullscreenButton.style.top = '10px'; + fullscreenButton.style.right = '10px'; + fullscreenButton.textContent = 'Fullscreen'; + + const distanceMeterButton = document.createElement('button'); + distanceMeterButton.style.position = 'absolute'; + distanceMeterButton.style.top = '40px'; + distanceMeterButton.style.right = '10px'; + distanceMeterButton.textContent = 'Distance'; + + const particlesButton = document.createElement('button'); + particlesButton.style.position = 'absolute'; + particlesButton.style.top = '70px'; + particlesButton.style.right = '10px'; + particlesButton.textContent = 'Particles'; + + const helpButton = document.createElement('button'); + helpButton.style.position = 'absolute'; + helpButton.style.top = '100px'; + helpButton.style.right = '10px'; + helpButton.textContent = 'Help'; + + fullscreenButton.addEventListener('click', function() { if (canvas.requestFullscreen) { canvas.requestFullscreen(); } else if (canvas.mozRequestFullScreen) { @@ -242,17 +289,31 @@ document.addEventListener('keydown', function(event) { } else if (canvas.msRequestFullscreen) { canvas.msRequestFullscreen(); } - } -}); - -canvas.addEventListener('touchstart', function(event) { - if (canvas.requestFullscreen) { - canvas.requestFullscreen(); - } else if (canvas.mozRequestFullScreen) { - canvas.mozRequestFullScreen(); - } else if (canvas.webkitRequestFullscreen) { - canvas.webkitRequestFullscreen(); - } else if (canvas.msRequestFullscreen) { - canvas.msRequestFullscreen(); - } -}); + }); + + distanceMeterButton.addEventListener('click', function() { + distanceMeterEnabled = !distanceMeterEnabled; + }); + + particlesButton.addEventListener('click', function() { + particlesEnabled = !particlesEnabled; + }); + + helpButton.addEventListener('click', function() { + alert(helpText); + }); + + canvas.parentNode.appendChild(fullscreenButton); + canvas.parentNode.appendChild(distanceMeterButton); + canvas.parentNode.appendChild(particlesButton); + canvas.parentNode.appendChild(helpButton); + + + let lastUpdateTime = 0; + const fps = 60; + const frameDelay = 1000 / fps; + + update(); + gameLoop(); + +} )(); \ No newline at end of file |