-
-
Save jonurry/adefda2a4e8dc1959e92d9b21cf24310 to your computer and use it in GitHub Desktop.
<link rel="stylesheet" href="css/game.css"> | |
<body> | |
<script> | |
function trackKeys(keys) { | |
let down = Object.create(null); | |
function track(event) { | |
if (keys.includes(event.key)) { | |
down[event.key] = event.type == "keydown"; | |
event.preventDefault(); | |
} | |
} | |
window.addEventListener("keydown", track); | |
window.addEventListener("keyup", track); | |
down.unregister = () => { | |
window.removeEventListener("keydown", track); | |
window.removeEventListener("keyup", track); | |
} | |
return down; | |
} | |
function runLevel(level, Display) { | |
let display = new Display(document.body, level); | |
let state = State.start(level); | |
let ending = 1; | |
return new Promise(resolve => { | |
/* all of the game pausing logic has to happen | |
inside the promise function because when that | |
resolves the game level is over */ | |
let gamePaused = false; | |
// function to determine if the game is paused or not | |
const isGamePaused = () => gamePaused; | |
/* function to alternately pause or resume the game | |
when the escape key is pressed */ | |
const pauseOrResumeGame = (e) => { | |
if (e.key == 'Escape') { | |
gamePaused = !gamePaused; | |
} | |
}; | |
/* wait for the game to resume | |
check resume status every 100 ms | |
once resumed, the animation function is called again */ | |
const waitForResume = () => { | |
if (isGamePaused()) { | |
setTimeout(waitForResume, 100); | |
} else { | |
runAnimation(animate); | |
} | |
}; | |
const animate = (time) => { | |
state = state.update(time, arrowKeys); | |
display.setState(state); | |
if (isGamePaused()) { | |
// game is paused, wait for it to resume | |
waitForResume(); | |
// return false to stop the animation | |
return false; | |
} else if (state.status == "playing") { | |
return true; | |
} else if (ending > 0) { | |
ending -= time; | |
return true; | |
} else { | |
display.clear(); | |
resolve(state.status); | |
// stop listening for the escape key | |
document.removeEventListener('keydown', pauseOrResumeGame); | |
// unregister control keys | |
arrowKeys.unregister(); | |
return false; | |
} | |
}; | |
// listen for the escape key to be pressed (to pause/resume game) | |
document.addEventListener('keydown', pauseOrResumeGame); | |
// register keys to control character | |
let arrowKeys = trackKeys(["ArrowLeft", "ArrowRight", "ArrowUp"]); | |
runAnimation(animate); | |
}); | |
} | |
runGame(GAME_LEVELS, DOMDisplay); | |
</script> | |
</body> |
Hints
An animation can be interrupted by returning false
from the function given to runAnimation
. It can be continued by calling runAnimation
again.
So we need to communicate the fact that we are pausing the game to the function given to runAnimation
. For that, you can use a binding that both the event handler and that function have access to.
When finding a way to unregister the handlers registered by trackKeys
, remember that the exact same function value that was passed to addEventListener
must be passed to removeEventListener
to successfully remove a handler. Thus, the handler function value created in trackKeys
must be available to the code that unregisters the handlers.
You can add a property to the object returned by trackKeys
, containing either that function value or a method that handles the unregistering directly.
16.2 Pausing The Game
Make it possible to pause (suspend) and unpause the game by pressing the Esc key.
This can be done by changing the
runLevel
function to use another keyboard event handler and interrupting or resuming the animation whenever the Esc key is hit.The
runAnimation
interface may not look like it is suitable for this at first glance, but it is if you rearrange the wayrunLevel
calls it.When you have that working, there is something else you could try. The way we have been registering keyboard event handlers is somewhat problematic. The arrows object is currently a global binding, and its event handlers are kept around even when no game is running. You could say they leak out of our system. Extend
trackKeys
to provide a way to unregister its handlers, and then changerunLevel
to register its handlers when it starts and unregister them again when it is finished.