BuckleFrog is an HTML5 Canvas based implementation of the 1981 Konami classic Frogger game. It is written in OCaml and transpiled to JavaScript.
Click here to play!
- A cute frog with a pleasant jumping animation
- A highway and river, both perfect for killing said frog
- High score tracking so you can get needlessly competitive with your friends
The code is written in OCaml, transpiled to JavaScript via bucklescript, and then bundled with parcel. All in all, incremental compilations are usually complete within 100ms.
The game features a pretty standard gameloop in index.ml
. The most remarkable part of the code is how state is updated on each frame. In order to use a functional approach (as opposed to a more standard object oriented one), state is updated via a reducer-like function with the signature: val stepWorld: worldT -> int -> worldT
. stepWorld
takes in the current world and the time elapsed since the last frame, then returns the next state of the world.
Within stepWorld
there is a multi-pass architecture similar to some compilers. Each "pass" has responsibility for updating a single piece of state or generating some intermediate values. For example updateHighScore
is an example of a pass that checks if the current score is greater than the high score and updates the highscore if necessary. Some passes don't update any final state and are only responsible for generating some intermediate data. An example of that would be findCollisions
. Collision data is necessary for other passes to operate, but isn't actually necessary for rendering and so doesn't end up in worldT
.
modules
- index.ml: Contains the gameloop and initialization code
- render.ml: All of the logic for rendering to the canvas.
- types.ml: All of the types used in the game
- state.ml:
stepWorld
and all of the logic for updating state. - utils.ml: Things that I had no idea where else to put. e.g. a new binary operator for generating a range between two numbers.
npm run build
npm run parcel
Open up two terminals. In the first run
npm run watch
In the second run
npm run parcel