Web-based lunar voxel game.

Overview

Mooncraft 2000

A voxelish game — set on the Moon — in Javascript.

The game is being hosted here: https://mooncraft2000.com

I experimented with a few tricks to try to keep this classic pseudo-voxel algorithm fast (but it still falls down on older or slower hardware). Chrome on my Mac is getting a much worse frame-rate, Safari is better — I have no idea why. I'm also learning Javascript.

Mooncraft 2000 screenshot.

How to Play

If your browser is running on a desktop machine there are just five keyboard controls:

  • 'Space Bar' or 'Return' — Thrust (lift)
  • 'A' or 'Left Arrow' — Turn left (left yaw)
  • 'D' or 'Right Arrow' — Turn right (right yaw)
  • 'W' or 'Up Arrow' — Forward acceleration (forward thrust)
  • 'S' or 'Down Arrow' — Reverse acceleration (reverse thrust)

You begin as an Apprentice, Level 0 mooncraft operator (trucker) delivering 1/2 ton cargo from lunar base to lunar base. You advance in rank by successfully moving the requisite amount of cargo. Advancing levels does however get progressively harder — that is at higher rank more cargo is required to be moved to advance further.

After Apprentice, Level 4, you advance to Journeyman, Level 0. At this point you are able to select larger cargo sizes if you wish.

Master is the final rank (if you persist).

Mooncraft 2000 map.

Try to land as gently as you can in the center of a base (use your landing camera to assist alignment, vertical-velocity nixie to watch your rate of descent). Horizontal movement when you touchdown will almost certainly result in a crash. If successful landing on the base, you will be lowered down for debriefing, refueling, cargo loading/unloading.

Back on the surface, the base platform allows you to change heading before lift-off (to conserve fuel). Fly to whichever base you wish with your cargo.

Long cargo runs (distances of 2000 or more) count for double-cargo and will advance you in rank more quickly. Distances of 3000 or greater count as a triple-cargo bonus.

If you become stranded, unable to make it to a base, you are penalized but will be rescued.

Crash your mooncraft, you are heavily penalized.

Mooncraft 2000 console overview.

Compass

At the top is an inertial compass. Markings N, S, E & W as well as intermediate NE, SE, SW, and NW. Pilot is expected to be able to estimate unmarked headings such as N/NW (north by northwest — halfway between N and NW).

Map

Lower left CRT map displays major lunar features, heading overlay. Top of the display is always north, mooncraft is at center. Base locations displayed as brighter crosses with base identifier.

Instruments

Lower-center instrument cluster with downward radar and camera. Top scope displays downward-facing radar of terrain (mooncraft graphic not to scale). Lower CRT is downward-facing (landing) camera. Nixies on either side of CRT give digital values for vertical velocity, altitude, fuel and forward velocity. "Idiot lights" above nixies indicate dangerous vertical velocity, low altitude warning, low fuel, and when on surface (touchdown).

Monitor

Lower right computer monitor (8-bit). Displays messages from various sources. Includes status messages, local lunar features, digital messages sent from bases.

FPS

The "1" key toggles a little debug display in the lower right corner of the Map. The time to execute one entire frame in milliseconds is displayed in the top-right. Frames-per-second (FPS) is displayed below. I have never seen FPS exceed 60 — perhaps the HTML5 Canvas only renders during "screen refreshes" and is gaited to 60 FPS.

The letters to the left of FPS indicate the state of various game settings.

Mooncraft 2000 debug display.

  • An uppercase "P" indicates pixel-doubling is on, lower-case pixel-doubling is off (toggle with the "2" key).
  • An uppercase "F" indicates "fog" is on, lower-case fog is off (toggle with the "3" key).
  • An uppercase "I" indicates interpolation is on, lower-case interpolation is off (toggle with the "4" key).
  • An uppercase "R" indicates roll is enabled, lower-case roll is disabled (there is no key to toggle).
  • An uppercase "L" indicates "long-distance" is on, lower-case near-distance (toggle with the "5" key).

How it was Written

Brick Moon

The voxel algorithm has been around since the 1990's so here I will give only a brief description. It's a kind of "ray casting" (like a poor man's ray-tracer). The view of the landscape (what the player sees) is, to the algorithm, a series of vertical slits, or pixel columns. The algorithm works on one column at a time. It computes the slope of an imaginary line leaving the player's eye and passing through the bottom-most pixel of the particular pixel-column in question. The ray is advanced, step-wise (slope-wise), until it is determined that it has intercepted a Moon voxel.

My Javascript implementation took inspiration from this code from eight years ago.

The Moon is a vast mosaic of tiles, each tile 512 x 512 "voxels". Because it is a simple terrain map, you can think of each tile as a 512 x 512 grid where, for each square in the grid, there is a color and an elevation. This being the Moon, the color is of course some shade of gray. The elevation is what gives rise to the craters, mountains, mare of the moon.

The Moon data came from NASA's CGI Moon Kit.

The color data for the Moon is without shadows, so I had to render shadows into the color data using the application Blender. Since the shadows were rendered "relief style" (also, orthographically) the Sun is at the same ascension everywhere on the Moon.

When a ray is "cast", walked step-wise, the algorithm determines which grid-square of the moon the ray is over and tests the ray's height against the elevation for that square (voxel) of the moon. When the ray intersects, the color for that voxel is assigned to the pixel on the screen. The ray then continues, representing the next pixel above, until it has either painted the entire vertical column of pixels on the display or until some fixed distance where the algorithm just "gives up". In the latter case we assume the ray has headed off into space (literally) and so instead we color the pixel in question with a color from our starry background.

There is nothing terribly complex about this algorithm — an understanding of basic trig is all that is required. It suffers however from being inefficient. To make it render faster your best bet is to either reduce the number of columns you are rendering (making the world narrower, lower in resolution) or pull in the distance at which the algorithm gives up (making the world shallower).

Some tricks that were successful: pixel doubling, width-wise, about doubled the performance with very little image degradation. Move the ray in larger increments as it heads out toward the horizon — I had to do a kind of (reverse?) mip-mapping so that the stride of the cast ray would not skip and then hit arbitrary voxels. (Unfortunately, I slowed the algorithm a bit by also implementing the reverse — moving the ray in smaller increments when close to the player so that finer detail is captured.)

Not to Scale

The tile data for the moon in Mooncraft 2000 came from 64ppd data sets — that's 64 pixels-per-degree. Therefore, each "voxel" in the game is 1/64th of a degree (of longitude around the equator let's say). But with an equatorial circumference of 10,921 kilometers, that means each voxel is just under 1/2 kilometer on a side (0.474 km). That's a voxel length of a little over five U.S. football fields.

To put it even more in perspective, those little lunar bases in the game that you land on that are maybe 7 x 7 or 9 x 9 voxels in area are in fact over two miles on a side, maybe 40 city blocks.

Data sets exist at 128ppd, 256ppd, even 512ppd. The largest (512ppd) would bring a voxel down to slightly less than a football field in size. That would bring the scale of the moon a little closer to what it should be in the game but at a cost of 64 tiles/voxels for each of the one tiles/voxels in the game. Throw out the data from the "dark" (far) side of the moon and you could cut the number in half. Trim the playable area of the game down further still and maybe you cut it in half again? Still leaves you juggling/storing a lot of tile data.

Additionally, working with very large data sets (moon images) gets to be difficult. It's unlikely you will be able to stitch the tiles together into a large mosaic image and find a paint program that can handle a file of that size.

For the 64ppd data set, I did start with a mosaic (map) of the entire moon. It was difficult to work with (some image editing programs crapped out altogether). Blender (a 3-D modeling application) running on my laptop was only barely able to render the topographical shadows that I required (and took 12 hours or so). If I even went the next step up to the 128ppd image data set, I will likely have to quarter the moon (and leave a row/column overlap between quarters) in order to render shadows.

Finally, the Moon is also not to scale vertically. There is a vertical scale factor I apply to the terrain — a bigger value makes the craters deeper, the mountains more peaked. A friend who's something of an avid hobbyist astronomer tried the game and suggested that I take the vertical scale down a bit to make it more true to the actual Moon. I did try dialing it back but I'm afraid I had already fallen in love with the more exaggerated Moon terrain I had begun with and so rolled it back up again.

More about creating the game on my blog.

Shortcomings

Poor performance on less-than-high-end hardware is my sorest regret. I tried all manner of things to eke out more FPS. The surest way I found to improve the speed was to either make the canvas smaller (thus lower resolution) or to set the "yon" distance to be considerably shorter (the horizon would end abruptly). I did not wish to shrink the canvas any more than the already small size of 512 pixels wide — it became much too chunky.

I did find that I could run out the horizon further by increasing the "stride" of the marching ray as it moved away from the viewer. When the stride was greater than the period of the voxels in the terrain however, a kind of "shimmering" would become very apparent (it already is a bit of a problem). One voxel might be "cast upon" by the marching ray but then a slight movement of the player would cause that voxel to be passed over and a neighboring voxel cast upon instead. I mitigated this to a large degree with a sort of "mip mapping" of the voxel data. When the stride is increased I instead cast rays upon voxels that represent instead the average height and color of a group of neighboring voxels.

I pursued but ultimately aborted an attempt at using Web Workers to handle (in parallel) the inner loop of the ray casting code. The "columnar nature" of the ray-casting algorithm lends itself wonderfully to parallelism and so I had hoped Web Workers would be an ideal fit. I found no way that each Web Worker could share the global tile cache and I had no interest in replicating that memory-intensive data structure for each Web Worker. If there is a means to share read-only data among Web Workers then this approach may still be workable. My knowledge of Web Workers though is poor.

I suspect the last, best chance of performance improvement is through Web Assembly. At this point I think Web Assembly is beyond my abilities. I have written the voxel algorithm in C before but how to compile for Web, expose an API, call into this API … I have no idea.

For reasons I don't understand, the frame-rate seems to slow down as you play when on Chrome (I'm testing on Mac OS). This does not happen on Safari. I have found though that if I refresh the page on Chrome, the frame-rate comes back up to what I expect. That feels to me like perhaps the garbage is not being taken out and we are resource starved, but I'm only guessing.

I had noticed in development that having a clip on the Canvas seemed to slow down Chrome much more than Safari. (I had tried to get rid of the clipping, perhaps it crept back into the code.)

Mobile, I confess, was an after-thought. I tried somewhat to make the game playable on mobile by providing on-screen buttons for controlling the craft. It didn't make a lot of sense though when I tried it on my phone — it was so small. I have not tried it on an iPad but I am not sure there is much of an audience on that configuration.

Looking Forward

From things detailed above, it's clear the game would benefit from any performance improvements.

Also it would be nice to move to a more detailed Moon tile set. I created an experimental 128ppd Moon tile data set (twice the longitudinal and twice the latitudinal resolution as the 64ppd tiles in the game) and it really made the Moon feel much, much larger. The data-set itself had a good deal more flaws than the 64ppd data which is unfortunate. Also, doubling the scale of the data also doubled the dynamic range of the elevation. It was looking like limiting elevation to 256 discreet values (one byte) would fall down as you moved to 128ppd and higher resolutions. One solution would be to go to two-bytes for elevation (that will increase the tile size). Another solution would be to stick with one-byte-per-voxel but make it relative to some two-byte value for the tile itself (we can assume that both the Mount Everest and Death Valley of the Moon are not on the same tile).

Apart from the technical improvements, I wonder if there's not a better game hiding within this one. To get the game out — give it a chance to breath — I did not invest in a complicated lunar ecosystem where, perhaps, lunar bases nearer the poles mine water in the shadows while equatorial bases harvest solar energy. Maybe one or two bases near the equator have rail launchers and serve as the cargo destination for other moon resources mined like rare minerals or metals. With such an ecosystem simulated, certain goods would be needed in some areas of the Moon, be in surplus in others. The player therefore could engage in mercantilism much like the early classic 8-bit game, Elite.

Occasional medical emergencies might emerge and take high priority — moving medical supplies taking priority over profit.

What if cargo could be jettisoned anywhere over the surface of the moon, picked up later?

Love to figure out how to make dust billow up when you touch-down/lift-off.

A huge move forward would be to add the ability to render 3D into the scene (or psuedo-3D like the classic Wing Commander series of games). Other mooncraft could be seen taking off, landing at bases, flying between bases (the bases themselves could be made more interesting looking). Suddenly mercantilism combined with multi-player could then transform the game. In addition to the rendering technical challenges however, a server back-end and something like Web Sockets would be required…

"Good enough for 1.0…"

You might also like...

🤖 A programming-themed based-in-terminal clicker game!

🤖 A programming-themed based-in-terminal clicker game!

Termi Clicker 🤖 A programming-themed clicker game for your terminal! 🤖 Usage npx termi-clicker@latest - install & play the game For now, global inst

Oct 3, 2022

an electron based custom client for the game kirka.io

Better-Kirka-Client A simple, yet performance-optimized and feature-rich kirka client This client features: Highly stable and outstanding performance

Dec 17, 2022

FCIV.NET is a open source strategy game loosely based on Freeciv

FCIV.NET is a open source strategy game loosely based on Freeciv

FCIV.NET FCIV.NET is an open-source turn-based strategy game. It can be played in any HTML5 capable web-browser and features in-depth game-play and a

Nov 22, 2022

Team Empire offers an innovative and intuitive game based on puzzles solved by two players working in a team.

Team Empire offers an innovative and intuitive game based on puzzles solved by two players working in a team.

Team Empire offers an innovative and intuitive game based on puzzles solved by two players working in a team. The user can create an account to monitor their results and achievements. The whole idea of the product is to make the player think and develop his logical thinking.

Aug 8, 2022

This is a project based on a game Leaderboard. You can interact with an API inserting your user name and score. Built with HTML, CSS, JavaScript and WEBPACK

This is a project based on a game Leaderboard. You can interact with an API inserting your user name and score. Built with HTML, CSS, JavaScript and WEBPACK

Leaderboard: Leaderboard project - Microverse Acces link Check the live version here Built With HTML CSS JavaScript VScode Webpack GitFlow Quick view

Oct 8, 2022

🏃‍♂️Open source, puzzle based adventure game in your browser

🏃‍♂️Open source, puzzle based adventure game in your browser

🏃‍♂️ Rungeon Rungeon is an open source, puzzle based adventure game. 📙 About Rungeon features a series of puzzles in the form of stages, in which th

Aug 25, 2022

a discord-based monster catching game engine, used for Lozpekamon on our Discord server

discord-monster-catching-game a discord-based monster catching game engine, used for Lozpekamon on our Discord server How to Use Requirements install

Oct 30, 2022

375 DSA Tracker helps you build your confidence in solving any coding related question and helps you prepare for your placements. It is your personal web-based progress tracker based on 375 DSA Sheet by Aman Dhattarwal & Shradha Didi

375 DSA Tracker helps you build your confidence in solving any coding related question and helps you prepare for your placements. It is your personal web-based progress tracker based on 375 DSA Sheet by Aman Dhattarwal & Shradha Didi

375-DSA Tracker 👨‍💻 Me and my friend Abhilash Jena made a 375 DSA Tracker website based on 375 DSA Sheet by Aman Dhattarwal & Shradha Didi which hel

Nov 11, 2022

The leaderboard web app displays the top scoring player on the fictional game of life.

The leaderboard web app displays the top scoring player on the fictional game of life.

Leaderboard is website that displays the scores submitted by all players. It also allows the current user to submit his/her score. All data is preserved thanks to the external Leaderboard API service.

Nov 23, 2022
Comments
  • Detect iPad, display buttons.

    Detect iPad, display buttons.

    Perhaps more general "is touch device". StackOverflow suggests:

    function isTouchDevice() { return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)); }

    opened by EngineersNeedArt 1
  • Lovely!

    Lovely!

    Just wanted to say this is a fun, lovely little game and I enjoyed the discussion of implementation and various tradeoffs (engineering-wise and design-wise) in the README.

    opened by wiseman 1
  • Performance gradually decreases over time

    Performance gradually decreases over time

    Win 10 / Firefox 99.0.1

    Curiously, resizing the window, even to a larger or similar sizes restores performance. Looking at a profile shows requestAnimationFrame at 240ms, with 221ms spent in CanvasRenderingContext2D.putImageData. After a resize, that same call is down to 1.9ms, with a total 22ms for the frame.

    Given the code is simply setting the same image buffer it does every frame, I suspect this is a browser bug, but worth reporting in case a workaround can be discovered as it does severely impact playability.

    opened by noct 2
  • null dereference when landing at first base

    null dereference when landing at first base

    I carried landed at a base (green) and the game froze with this this error,

    Uncaught TypeError: Cannot read properties of null (reading '0')
        at Object.handleGameState (MoonDebriefState.js:170:36)
        at _gameLoop (Mooncraft.js:287:35)
    

    Looks like departedLocation[0] fails because departedLocation is null.

    opened by tantalor 6
Owner
John Calhoun
After a 30-year hiatus, back to writing shareware, ha ha.
John Calhoun
NFT Game Starter Project: https://github.com/buildspace/buildspace-nft-game-starter

Running React on Repl.it React is a popular JavaScript library for building user interfaces. Vite is a blazing fast frontend build tool that includes

Zahuis 2 Feb 11, 2022
Conways-game-of-life - A Conway's Game Of Life project using Python

conways-game-of-life A Conway's Game Of Life project using Python JavaScript Devlog January 1st 2022: also need to remember Python's syntax a bit will

Felipe Melgaço Magesty Silveira 0 Sep 23, 2022
Slime jumper game is a simple game that requires you to escape from the enemies that come your way.

Slime Jumper What is this game? The slime jumper game is a game with a simple logic (but it was not so easy to do) where you have to escape from the e

Fatih 2 Mar 1, 2022
Clinton Mbonu 20 Jun 30, 2022
INeedHelp is a Game Boy, Game Boy Advance, and Nintendo DS emulator written in JavaScript and TypeScript.

INeedHelp INeedHelp is a Game Boy Advance and Nintendo DS emulator written in JavaScript/TypeScript. FAQ Why is it called INeedHelp? You would need he

Powerlated 6 Jun 29, 2022
This is a Tic Tac Toe game built with HTML, CSS, and JavaScript. It is a simple and fun game where two players take turns marking X and O on a 3x3 grid.

Tic Tac Toe Game This is a Tic Tac Toe game built with HTML, CSS, and JavaScript. It is a simple and fun game where two players take turns marking X a

Andrew Tsegaye 4 Mar 4, 2023
A web-based implementation of the Hanabi board game.

Senko Project "Senko" is a web-based implementation of the Hanabi board game. Online Demo The frontend uses TypeScript, Stylus, Mithril, Rxjs, and Mat

Yuchen Shi 4 Feb 17, 2022
Idle Game Based on A Dark Room with bits of NGU Idle and others mixed in.

An-Empty-World Main Story: You wake up in a strange place, surrounded by debris. Unaware of how you got there, it becomes immediately aware that you'r

Andre Schoolman 2 Mar 26, 2022
Landing Page for Villagers.finance Play To Earn WEB3 Game, NFT Based.

create-svelte Everything you need to build a Svelte project, powered by create-svelte. Creating a project If you're seeing this, you've probably alrea

Ahmed DEMIAI 9 Sep 15, 2022
PokemonNFT started as Buildspace Project - "Create your own mini turn-based NFT browser game" - ROSE Emerald Paratime Testnet

Welcome to PokemonNFTGame ?? Buildspace Project - Create your own mini turn-based NFT browser game ✨ Demo Install npm install Usage npm run dev Blockc

Alberto Cruz Luis 7 Oct 3, 2022