Starmancer’s art style is a blend of 2D sprites and 3D objects, all existing in the same world. At first glance it might seem easier to create 2D sprites for colonists instead of 3D models--after all, games have been using sprites since Mario.
Using 2D sprites is a deliberate art choice. We want to capture the feeling of traditional games, like Starcraft and Final Fantasy. But we also want some modern-feeling 3D models, especially with our ships and machines.
Unfortunately for us, 2D and 3D don't always seamlessly mix together
The most obvious problem with a 2D sprite is that it’s flat. By definition, a two-dimensional object has a width and height, but no depth. As you rotate the camera, you’ll quickly discover that a 2D sprite is masquerading as a 3D object.
To fix this we simply rotate the sprite towards the camera constantly.
A sprite that’s constantly rotated towards the camera is a billboard sprite.
No matter how hard you try, you'll never see the back of the sprite.
We had to write a special shader (not a very complex one) that constantly rotates the sprites towards the camera.
We're also using 4 different sprite sides that automatically change as the camera is rotated.
An interesting thing about sprites, is that when you rotate them, they appear to be taller or shorter. You’ll notice that once billboard sprites were implemented, the sprites seemed taller
You've probably seen billboard sprites before. They're most notable in older games, like Duke Nukem 3D (the enemy, the trash can, and the light are all billboard sprites.)
So that's one problem solved, but what happens as we move these sprites around the 3D world?
Clipping occurs when one object impossibly passes through a different object, and appears to be “cut-off.” If you run into a wall in almost any 3D game you’ll notice that something, usually a weapon/item, will clip into the wall.
It's difficult to see here, but the colonist's head is clipping through the wall
An even easier example is this box of potatoes
One thing that you can do to fix this is to always render sprites on top of 3D objects. This will certainly prevent all clipping, but it will lead to something like this:
In the end we went with a system where the 2D sprites were sometimes drawn on top of 3D models when they organically weren’t supposed to be, and we never move colonists too close to 3D objects.
More specifically, we adjust the clipping planes of the camera as the player zooms in and out, so that sprites never appear to clip through 3D objects.
Here's what happens if you don't adjust the clipping planes (the crates start to clip through the shelf)
But once you adjust the clipping planes everything appears correct
To recap, we prevent colonists from walking too close to 3D objects, and adjust the clipping planes of the camera as needed.
This is the end result:
2D Sprite Clipping
An interesting property of sprites is that you could move them up and down (and rescale them) to give the illusion of moving along the Z-axis. We don’t do this, because it would be insane, but this property of sprites does make it difficult to portray relative positions between two sprites.
To put it simply, sprites that are closer to the camera should render on top of the sprites that are farther away
When the render order is messed up it creates a very weird height and position illusion. The blue colonist looks like it's standing on top of the red colonist in an M.C Escher way
To solve this problem we dynamically adjust the render order of sprites as they move around the screen. Higher render orders are always drawn on top of lower render orders. The lower in the screen that a sprite is, the higher its render order is.
Of course, it's slightly more complicated because colonists can have many different sprites, and they all need to have a consistent render order.
Once we solved the clipping issues the game started to look much better, but we still had an issue of sprites not having consistent positions relative to 3D objects.
3D Relative Positioning
2D sprites do not look good when you rotate the camera. Their position relative to 3D objects becomes very very skewed.
Here's a colonist lying in a bed to demonstrate:
The put it bluntly, this looks terrible. To solve the problem we force the player to 90 degree rotation increments. Freely rotating the camera is awesome, but the sprites become so out of place. It would constantly remind the player that they're playing a video game and ruin their immersion (and we're immersion snobs)
When you step rotate it barely looks like the colonist is moving
There's actually one more trick that we're doing here. We're slightly moving the colonist based on which sprite side is being used. This provides a much more consistent position as the player rotates the camera.
Here's what it looks like when we don't adjust the position (look at the position of the colonist's head relative to the pillow, and where the colonist is positioned left/right on the bed.)
And another minor issue, is when you pan the camera. If the 2D sprite is positioned too high there will be discrepancies as you pan the camera. This is because the colonist is actually above the bed.
To solve this we place the colonist at a y position of 0 and force it to render on top of the bed.
So that solves the issues with 3D objects, but 2D objects have position problems too
2D Relative Positioning
Sprites will appear to be in vastly different positions as you pan the camera. The cause of this is similar to the issue with 3D objects.
Look at how this crate appears to be in vastly different positions as the camera moves.
To solve this we're forcing the pivot of both the colonist and the crate the be the same. We then reposition the crate in a shader (and we do the same thing with colonist heads, by the way)
Once we apply this trick, everything keeps the same relative position
So now the sprites maintain their relative position as they move around the screen, but there’s still an issue when colonists animate and their sprites change. If you don’t adjust the positions of all the sprites, you’ll end up with some weird looking colonists:
This was a somewhat simple fix. Internally, every sprite has a unique head and carry position. As the sprite changes, we adjust the position of the head and crate as necessary. This allows Victor to make the colonists bob up and down or do whatever else it is that Victor wants to do.
When you combine these systems (pivot offsets and position offsets) you give the illusion that the colonist is a single unit
Combining 2D sprites and 3D models is not as simple as it might seem at first glance. It creates a bit of extra work (and is sometimes a headache,) but in the end we think it's worth it.
The end product will hopefully be a nice blend of 2D and 3D that gives Starmancer a unique feel.
If you have any questions, feel free to leave a comment
Don't forget to follow us on Patreon or any other social media site (if you're not already)
Once again, Starmancer is currently live on Kickstarter. You can support it here.