Table of Contents
Welcome to the twelvth article of the Sonic Battle (GBA) Renderer series.
Drop shadows are sprites that are drawn under the feet of Sonic Battle characters. They follow the X and Y center positions of their characters and snap to the terrain height on the Z axis. They serve as a visual clue of the characters’ positions.
The shadows are scaled uniformly in width and height according to the character’s distance to the ground under their feet. It’s done through frame animation so no affine transformations are used at all (besides the usual translation). The base image is already vertically squished to approximate the effect of the camera’s pitch.
mGBA emulator’s sprite inspection tool confirms the absence of affine transformations:
It also shows that there are 3 frames for the scaling animation:
I mistakenly implemented the scaling using an affine matrix, and let me tell you - the aliasing is terrible due to the GBA’s small screen resolution. Frame animation was possibly used here to avoid that specific issue, if not for performance reasons.
The physics engine of Sonic Battle is out of the scope of this series. But something minimal needed to be implemented for the drop shadow vertical positioning. I would later use the same algorithm for preventing character-wall intersection.
I was interested in detecting circle overlap against an axis-aligned rectangle (AABB). The circle would represent a character’s contour on the XY plane, and the AABB would be a section of the high ground (or top floor). I found StackOverflow user Jack Ding’s solution (https://gamedev.stackexchange.com/a/120897) particularly intuitive and elegant so I chose to implement it.
It’s not representitive of the true game’s physics engine, most likely.
It’s possible for characters to be right above rising/falling edges of the terrain. A realistic shadow would need to be split into parts and projected on a number of surfaces. That’s a detail I can live without, and so could the game’s developers: in the edge case the top terrain wins the tie and the shadow appears to levitate.
My implementation assumes that because shadows are projected on terrain they should be the very first to be drawn after the said terrain appears on screen. Nothing in the game world is situated between the terrain and shadows so they don’t need to be sorted either (but they do need to be grouped by floor).
Callback to article 5, The big picture.
Draw order - reality
The draw order is a tad more complicated than I had thought initially.
What! How? Our established draw order here is
1st floor tilemap -> walls -> 2nd floor tilemap -> 2nd floor shadow -> 2nd floor character. But in reality there’s a wall sprite that is drawn after the 2nd floor shadow? Unacceptable I say.
I did some digging. Turns out that my draw order theory was largely correct - it corresponds to the original developers’ intentions. However they ran afoul of a tricky hardware constraint. The consequences are whacky draw priorities like the one I captured above.
I wrote another post that goes into a lot of detail about the issue: The Escher Sandwich.
The next article will discuss the culling of all the walls and objects contained in a scene.
You can subscribe to the mailing list to be notified when a new article appears.