Intro
Let's talk about ships!!!! In this devlog, I'm going to explain the general process for how I create the ships, both creatively and (to a lesser extent) in code. I'm going to link this devlog to anybody who asks me "how did you make these?" in the future, as I get that question A LOT. So if you've been linked here, that's why
SO HERE'S THE TL;DR for the question "how do you make procedural space ships??":
1. I spent 2 months building a library of 3D shapes and warps to use as building blocks.
2. I research games & movies with a similar aesthetic to what I want, and pick a style of ship to build.
3. I hard-code a generic ship for that style.
4. I add randomness and detail to the generic algorithm.
5. I stretch the limits of the randomness for each part, getting to a point where it doesn't look like a ship anymore, then backing up.
6. I'm either satisfied with the algorithm and keep it, or decide that style isn't working and trash it. Either way, I might also branch it to create a new algorithm and repeat steps 2-5 for the next style!
This process has given me 3 styles that I'm happy with so far: the classic fighter style, the surreal style, and the totally-not-a-tie-fighter style.
If you're interested in knowing more than the TL;DR of how that all works, then keep reading!
Don't forget you can follow me on Twitter, where I post ship screenshots daily(-ish). In addition, I'll also link every LT tweet on these devlog threads, since I know lots of LT fans don't use Twitter XD I could also link them on the Reddit threads for each devlog... hmm...
Here are all of the ship galleries since the last devlog!
LT Ships 12.18.17
LT Ships 12.20.17
LT Ships 12.21.17
LT Ships 01.04.18
Now, onto the part that only the most hardcore fans/ nerds actually read :p
Summary
12.15.17 - 01.04.18
- Nailed 3 ship types: classic, surreal, and tie.
- Made & then discarded a few other ship types- vertical, radial, for example.
- Pulled apart all ship generated algorithms into resuable parts - wings, hulls, engine, and gun all come in different algorithms than can be reusable by new ship types.
- Started adding customization properties for all of the ship generation types.
- Added a new UI value type for enums to allow UI selection of which ship type to generate.
- Put a few of the randomness properties (for ex, wing length) into the UI for customization
- Leaving alone for now; waiting for new UI scheme to be done so that I can build the ship customization tool for PAX demo.
Lots of Words About Ships
Let's break down each step from the TL;DR into more detail about how it's executed.
1. I spent 2 months building a library of 3D shapes and warps to use as building blocks
For the most part, I detailed this process in all of my previous devlogs. But let's summarize that work here just for kicks. Each item on each list links to an example of that shape, or a shape/ship with that warp applied.
Here's all of the shapes I have:
- Ellipsoid
- Torus
- Block (rectangular prism) (image is made of many blocks lol)
- Pyramid (currently only square pyramid)
- Icosahedron
- Prism
- Irregular prism (prism w/ random angles between each vertex on the base face)
The warps & detail functions:
- Greeble
- Stellate (per-poly and all over)
- Extrude (per-poly and all over)
- Bevel
- Bell-curve warps (applied as an all-over warp to make the ship have a subtle curve)
The code structures:
- Shapes, for storing polys & tris
- Joints & JointFields, for connecting Shapes
There are a couple of items exluded from each list, mainly because I haven't been using them much for the ships I've made so far. Also, the Joint system has had almost no use since starting ships. I believe it'll be useful for stations in the future, when there are many big parts attaching at joints to a main structure, but for now, using AABB-based placement for ship parts is working pretty well. An AABB basically means that I know the dimensions of the original shape that I need to attach a new shape to. This information has proved more useful than creating a Joint or a JointField for shape placement.
2. I research games & movies with a similar aesthetic to what I want, and pick a style of ship to build.
I have around 10 art books, ranging from movie & game concept art from titles like Mass Effect and Star Wars to personal art books from sci-fi artists, that Josh is lending me. I also collect images on a Pinterest board of fighter ships from places like Eve's wiki (and other games) and Behance accounts from concept artists for games like Halo.
This isn't to copy their work, but rather, to collect information on what tropes are common. Right now, since I'm building fighters specifically, I want to find what visual elements are common that tell players that this is a 'fighter space ship'. Those visual tropes are extremely important for creating procedural ships. I can't just use ANY combination of random shapes under the sun. As much as I'd personally like to create extremely surreal alien architecture, from a player's perspective, you need to be able to look at a ship and (hopefully) be able to visually classify it as armed or not- and if armed, how dangerous. So, weapons need to be recognizable, and armed things like battleships and fighters need to be recognizabley separate from cargo ships and satellites. Sampling tropes from other art is how I recreate them- and getting tropes from a LOT of different sources is how I get the inspiration to make the trope usage unique.
Within each large ship class, like 'battleship', 'cargo transport', 'corvette', and 'fighter', I then come up with sub-classes to focus on for each algorithm. I didn't do a lot of planning a head with this, but rather, I'm figuring them out as I go. These sub-classes are the 3 types I've presented so far- classic, surreal, and Tie fighter, all within the 'fighter' class. The sub-classes probably won't be a named thing from a gameplay standpoint; they're mostly helpful for coding purposes. I don't think that one algorithm can cover a whole class- it would have to be random while maintaining coherence for lots of different types of ship parts. The split between the surreal sub-class and other fighter sub-classes is the best example of this. I've tried adding surreal wings to the other sub-classes, for example, and it just makes them TOO random. It's good to have a separate classic algorithm for figthers that fill the Freelancer-like trope, without stretching the bounds of recognizability too far.
3. I hard-code a generic ship for that style.
The last thing I do before writing any code is sketch out a very generic ship for the sub-class I'm going to code. I then use that to break down what parts the ship will need and how they'll be arranged. The only exception to this so far is the surreal sub-class, which is really just an algamation of features from other classes, but with the bounds of the randomness stretched extremely far.
On the doodle, I break down each ship part into its MOST basic shapes (cubes, prisms, etc) to figure out how I'll use my library to create it. I then hard-code the shape, scale, and position for each basic component of the ship. So far, all of the ships started with just a hull and two wings. I've found the extrusion function to be extremely helpful for this step. The hull of the fighter ship, for example, is a prism with the base face extruded & contracted to make a long, pointy hull-shape. Some wings are created with extrusion also.
4. I add randomness and detail to the generic algorithm
Now that I have a static ship, it's ready to add variation. Variation is added with randomness. For example, with just the hull and wings created, I can randomize the:
- base shape
- position
- rotation
of the hull and wings.
I then add detail with my warp library. For example, I extrude random polygons for more shape variation; I add surface detail with stellation, extrusion, and greebling; I randomly add an overall warp to the whole ship so that it curves gently on an axis.
My goal going forward is to expand on this step quite a bit. I feel that the ships are lacking in detail, and now that I've nailed down the basic shapes for a few sub-classes, I want to add lots more detail. This means texturing, actually placing engines where you see engine trails, placing guns where the turrets are being added (hopefully Josh will talk about turrets in his devlog!), and other random surface details. I probably won't get all of that done for the PAX demo (probably going to have to skip texturing), but after we get back from PAX, I'm going to keep working on it!
5. I stretch the limits of the randomness for each part, getting to a point where it doesn't look like a ship anymore, then backing up.
Words.
I continue to add randomness to the ships until things 'break'. It's hard to describe- basically, I keep stretching the upper and lower limits of every random value, and adding more opportunities for random values, until the generated ships stop looking like 'ships'. At that point, I identify where the randomness went to far, and dial it back a little. This allows me to experiment with the balance between variation and recognizability, which I've been emphasizing pretty much every devlog. We want a whole universe of ships, yet the ships need to be recognizably ships. I think this method of starting with a hard-coded, boring ship, then adding randomness to stretch the limits of recognizablility, is paramount to how this process works.
I try not to rely on version control for this, but it has saved my butt a couple of times, as I made changes that I thought looked good until a few days of contemplation. For the most part, now, if I want to make DRASTIC changes to a ship algorithm, I'll go ahead and branch it, which brings us to step 6.
6. I'm either satisfied with the algorithm and keep it, or decide that style isn't working and trash it. Either way, I might also branch it to create a new algorithm and repeat steps 2-5 for the next style!
Words.
This pretty much explains itself. When I feel I've gotten to a stopping point with an algorithm, I move on to the next one! This whole process is basically just a bunch of experimenting. I gather reference, hard-code, randomize, rinse, repeat.
On the Topic of Limit Theory and Realism
I get a LOT of comments and questions about how 'realistic' the ships look, and I want to address that here. I'm probably also going to link people to this in the future when I get questions about realism. If you've been linked here, I intend not to insult you- PLEASE understand that I get comments & questions about realism daily and I just don't have the finger stamina to answer all of them.
Limit Theory prioritizes fun, gameplay, and general player experience over 'realism'. Why? Because every element of a game, including how 'realistic' a feature is, is useless at best and obstructive at worst if it doesn't contribute to how fun the game is! When that's applied to ship generation, we're often talking about what realistically could or could not fly in space or on planets VS the variety and 'coolness' of the ship aesthetics. Unfortunately, many qualifications of 'realism' seriously limit what we'd be allowed to generate, thus limiting variety and coolness.
For example, the LT stance on artificial gravity is the hand-wavy, Star Trek/ Star Wars style: even non-rotating ships could have artificial gravity. Why? Because you won't see the inside of ships, so the more FUN option is to not worry about it at all, and not limit ourselves to ships that could 'realistically' produce artificial gravity!
I need to address another big stick in these arguments: LT ship generation does not prioritize realistic flight in planet atmosphere. Why? Again, simply because that would severely limit the variety of ships we could produce. Who knows what alien or far-future technology could come up with with respect to flight, both on and off planets, so why not let our imagination go wild?
Now, I also know that for many fans, 'realism' IS what's fun. However, this definition changes allllll the time, both on the 'actual science' scale, and from player to player! Not only do we get daily comments about how 'realistic' something does or doesn't look, the comments are often people debating with each other what it even means This is not a bad thing. This is a sign of y'all giving a crap about the game and I love that y'all do. I just want y'all to understand how it's hard to satisfy a general consensus on what's realistic or not. We'll always upset somebody :p
Remember also that there is a kind of realism that the ship generation algorithms to satisfy, and that's player recognizability. If you've been reading the devlogs, you know that my #1 priority with ship generation is balancing regoznizability and variety. Players need to be able to look at a ship and determine whether or not it's armed, for example. That kind of readability isn't quite tied to what physics allows, but it is tied to SOME kind of player sensitibility of what a 'satellite', 'cargo ship', and 'battleship' all look like.
The sci-fi genre, and especially sci-fi games, exist to STRETCH and question what 'realistic' means. I think that debating what is or isn't possible is one of the most fun parts of the entire sci-fi genre. I just want ya'll to know that at the end of the day, what is or isn't the most 'realistic' option is often lower in priority than what is or isn't FUN. This has always been Josh's policy, so it's my policy too.
FIN
Well there you go!!! That is, as of posting, how I make the ships in LT. I hope this has been enlighting to LT fans and at least explanitory for any poor fool who got linked here because they dared to ask me how I make procedural ships XD
Gallery links once again:
LT Ships 12.18.17
LT Ships 12.20.17
LT Ships 12.21.17
LT Ships 01.04.18
Twitter link once again.
Thank you,
Lindsey Reid
P.S. I have a bunch of tutorials on 3d geometry & shader writing up on my personal blog. I'm thinking about writing a beginner 'how to make procedural geometry' tutorial, let me know if any of y'all who are programmers would find that interesting.