Face Generator Updates
Since putting together the first playable alpha I've pivoted back to work on the procedural face generator. Original details over here.
The face generator is written in Python but in a C-like style that could potentially port to full C. The idea was to eventually move the code to run on-device and generate an infinite number of faces at runtime.
As the game design solidified I realized this runtime generation wasn't going to happen. For one, it's not really necessary: I can generate thousands of faces offline and just include them in the game -- each face is just a few kb.
Second, the face generator is not quick. Part of the problem is Python's leisurely pace but even with conversion to C and heavy optimizations it'd never be as quick as loading a small PNG on-device.
Third, although I'm well focused on the success rate of the generation, there's still a clear range from bad-to-good with the resulting faces. Being able to curate these and choose exactly what appears in the game will save us all some grief.
Pre-generating the faces does require a quick system to check/approve faces, and a reliable way to re-generate a face when aspects of the generation code change, but none of that's been a problem so far.
Besides drastically simplifying the runtime this choice also let me refactor the generator code to be more Pythonic and easier to manage/extend.
Of extending, here's most of the new stuff that's been added recently:
More Part Variations
The simplest way to expand the face generator output is to add new part variations. Just draw new features on their respective grids and the generator can drop them in.
Some added eye, nose, and mouth variations
More Part Types
Besides the basic eye/nose/mouth, I also added a few rarer parts types like whiskers, antennas, and wrinkles. Each of these needs special logic to integrate with the face layout.
Whiskers, antennas, wrinkles. The whole rodeo.
Some game mechanics require certain accessories on the face. Currently that means sunglasses and respirators but I expect more eventually.
Sunglasses and respirator overlays
The core structure of the face is determined from the initial eyes/nose/mouth layout. These hand-drawn parts are then surrounded with a bezier path that defines the shape of the head.
The path is stroked using a hand-drawn texture ball. Adding more texture balls is one way to add variety, and the precise way the border surrounds the core features is also variable. Since the last face update I've added more textures and a few new shapes. I'd planned to add a lot more shapes but found a better way to get variety was to apply a lumpiness deformation to the path instead.
Border textures, head shapes, and lump deformations
Everything about this game is 2D but I am faking a lot of 3D with the style and the in-game layer parallax. Mucking around with things recently I made a few subtle tweaks to the face generator to add some dimensionality.
// Shifted Parts, Muted Lumps
Each face has a left or right "look direction" to establish a slightly off-center view so the simplest improvement is just shifting the parts horizontally to match. Another technique is to reduce the lump deformation effect on one side to simulate a more edge-on view.
Original, shifted parts, & reduced lump deformation on right side
// Banana Warp
Applying a gentle bend of the entire face away from the look direction also adds a hint of volume.
Warping the face along a slightly curved spline
I feel pretty done with the martian generator right now. Even if I still have to dip in for fixes and customizations, the general output is basically shippable.
Martians generated with all the latest changes
Up until recently I've put all my effort on make funny looking martians and it was only in the back of my mind that I'd need to change things up somehow. Bring that to the front and now I've added humans.
The main focus for this game is on the martians so anything else is really intended for flavoring. On first getting the humans in, I noticed they made the martians seem more interesting by comparison. That seemed like a good thing so I leaned in to making the humans slightly more grounded to emphasize that gap.
A Mars-faring human
These use most of the same face generation logic as the martians, with a bunch of parameters locked down (eyes = 2, nose = 1, mouth = 1, etc), new head-shape logic, and all new hand-drawn parts.
I wanted to keep the work manageable here so they're all in spacesuits as an excuse to stay covered up. I've only bothered to hand draw a few eyes/noses/mouths so far and need to add more eventually.
Human face variety
As with the martians, the heads are generated from a stroked path. I tried to do the same with the shoulders but I don't have a good pipeline for the path editing -- it's all just numbers in code. I gave up on the paths and just drew the suits by hand instead.
Stroked-path bodies replaced with hand-drawn ones
The upside is that this inspired me to define six factions, each with their own badge and suit design. May come in handy for the narrative.
It's also possible to put a martian in one of these suits. I haven't figured out if that's useful or not yet.
Never seen a more useful dude
Now Do Robots
Refactoring the Python code for humans put me in the right place for adding more fundamental face types so here's robots. There are now three supported species -- martians, humans, and robots -- which is surely enough.
Along with new hand-drawn parts, the big change for robots is the head shape generation. Without getting too clever, I wanted something simple and distinct from martians and humans. Same idea: to create contrast with the martians that make up the majority of the population.
After placing the core hand-drawn parts, the generator divides the face into stacked rows and assigns them discrete randomized widths. These are joined together and combined with shifts and other 2D fakery to add depth. I happily punted on the bodies and just use a shaded box for all of them.
Randomizing robot head profiles and row depths
Shading on the front is the same as with martians/humans. For the sides I built a simple hatching system that strokes a bunch of parallel lines with selectable density.
With added shadows, variable necks, and strategic skews they all basically look like Futurama rejects. Good enough.
The rise of AI in the last few years has got me reflecting on the futility of this kind of hand-built random face generation. I'm still happy with the results and I don't think generative art would get quite the same effect but I bet it'd be close.
Anyways, all this offline work has been fun but at this point I'm looking forward to getting back to narrative design and game code.
Log in with itch.io to leave a comment.
That looks really good, it's very impressive.
I was wondering if, on the hardware specification, would it be possible to generate a bunch of faces needed for the game when you create a new game save? Or is it too long or maybe too complicated to manage?
I really like the randomness of the faces, but I get that pre-generation is way more easier.
If it was fast enough to generate them all at once then it'd be fast enough to generate as-needed, so there's no real advantage to doing everything up front. My plan is to include a few thousand faces, so hopefully it still feels random enough without having to port everything to the runtime.
Why not keep it in the game as a metagame?
People seem to love making characters, so why not martians?
You could even take this a step further and have in-game progress unlock new traits (e.g. Cyclops Anger Management unlocks Eye_Count=1)
If the generator is deterministic and works on a fixed number of parameters, you could even give every martian its own ID and let people share them. Or download them as avatars off a website...
Edit: oh and the look soooo good. You might be able to pull off talking by just animating the mouth (LucasArts style)
Cool idea. The generator is a means to an end though - to create a diverse martian population for the game's mechanics and settings. I'd do things differently if the focus was on build-a-face. Not that it couldn't work but it'd be a different game.
For the mouths, you're on point. I haven't shown it yet but each mouth has 3 versions: em/oh/eh, which is just barely enough for animating speech.
This looks so good!
i continue to be extremely excited for this game!
Funny how I had initially the same instincts of trying to generate as much stuff as possible at run time, to realize that the storage saved might just not be worth the computing time and coding challenges.
But man these characters look spectacular! So much personality and they all fit the same world. Like you said the humans make a great contrast with the aliens. Super clever to develop the game around the tiny screen, with big assets like the faces and the dishes . They make Mars After Midnight stand out from the other Playdate games, including mine, which look like they’ve been ported for the Playdate rather than developed for it specifically.
I had another game idea specifically targeting the Playdate's 1-bit screen and the crank but not really the physical screen size. Mars was a followup idea trying to hit all 3.
Despite having very fancy ears, mouths, etc. I've always wandered how this procedural images were done to make all fit in a way that it doesn't look weeeird. Thanks for the notes on head shapes.
Amazing work as always, looking forward for more about martians (and others).
Thanks! There's a fairly simple "collision" system that I use to snug the parts together and I think that helps with making the fit feel natural. Also because everything is low resolution 1-bit, it's easier to forgive some mismatching.