Draw Order for Sprite Rending is Nuts!

First big rendering refactor

Series:
Posted on

Last Time (and a little bit after that)…

The last time I worked on my Jamuary game, I planned on drawing all props (1-tile-high stools, 2-tile-high characters, and 3×3-tile walls) all on the same layer. Due to how my code was originally written to draw different “heights” of props at different layers, my initial refactor looked a bit out of order 🙂

Some time after sharing that screenshot in the LDG discord, Vox (the main driving force behind Jamuary) gave me the following tip:

I don’t know what you’re using, but everything in my game has a rendering group. Before my entities go to my rendering system they are sorted into groups, then each group is sorted by their y + height value. This allows me to ensure everything is ordered correctly on rendering. Let me know if you need any help and I’ll do my best 🙂 Other people in here probably have way more advanced stuff you can do, but this is the simplest solution I could figure out on my own.

Before I read that, I was thinking over how the following scenario would work:

  • A small 1×2 character is battling against a large 6×4 boss
  • The character moves to the bosses face to slash the boss, then runs back to his original position

With my current rendering system, which groups all Props together, the character would be hidden beneath the boss for most of the movement and attack. So render groups would allow me to stick the boss in a group that always renders before the group that the character is in. Perfect!

Day 2 (of working on this)

So here was my plan for render groups:

  1. subfloor – Why not? maybe something in a hole that needs to be below the map could go here
  2. floor – Always the base level of rendering, only used for the map
  3. on-floor – will only be above the floor, always renders below the in-air render group (think a giant boss that PCs need to be seen above)
  4. in-air – Main render group for props, characters, and map walls (glorified props)
  5. roof – Always rendered on top, typically ceiling tiles, also only used for the map
  6. super-roof – Why not again?

This is the order that the groups will render, ensuring that props will always be on the floor and ceiling tiles will always be on top of props. Now I don’t really like the name ‘on-floor’, but it’s what I got for now (search and replace will take care of that later).

Before I bother with render groups though, I need to fix that monster shown above.

The position of this wall is a bit off

Ok, just need to move that wall to the correct spot. Luckily the wall prop is grid aligned, so this should be easy…

Just gotta get that wall in the right place...

At this point, I realized that my y-sort ordering isn’t perfect for my use case.

The y-sorting isn't perfect

So the bottom of the guy (boxed in green) is below the drawn portion of the wall, but it’s above the bottom of the entire wall prop (boxed in red). So I shared this shot and my realization with the LDG chat, and before I could start to think of a solution, Vox came in again with an awesome bit of knowledge:

you need to bound your sprites to the ground. Think of your space like a 3d space even though it’s in 2d. Give your sprites a 16 x 16 base and don’t allow them to be in areas that aren’t technically inhabited by something else.
In other words that wall should have some bounding areas that can be compared against for collision as well as drawing purposes.

This took me some time to digest. So rather than explain my thinking process, allow me to show you some screenshots that hopefully express what my brain went through.

So we have some tiles on a grid with tiles that are 32x32

I could just make that bottom left tail think its own prop

Oh, ground the props with some collision boxes that are then compared

Alright, I was starting to grasp this. Instead of using the prop’s full y and height, just use a “collision block” for a prop to determine the y-sorting. For small props (1×1, 1×2) there will only be one “collision block”, but for larger props (like that 3×3 wall), there will be a collection of “collision blocks”.

Before doing that, I need to get things rendering in their proper render groups, which took a bit of refactoring but made my code look nice.

rendering with render groups

So two render groups are special groups for map data (floor and roof) and all the props/characters are another layer so I can do a rough y-sort for props that don’t have an AABB collision (look it up). The next step is to dive into “collision” data for sorting out any props that have an AABB collision. Here’s an image showing how it works

How the AABB collision check works

So first, the engine detects a collision between the guy’s bounding box (the blue box) and the wall’s bounding box (the hot pink one). It then grabs the collision object for the guy (the light blue box) and the wall (the purple box). If those collision boxes are colliding (which they wouldn’t in the game, but it’s good to have fallbacks) I use the center of both collision boxes to find out which should be behind the other. In this case, the wall’s collision center has a lower y than the guy’s collision center, so the wall is rendered first. If there isn’t a collision, then I y-sort by the collision boxes.

All in all, it ended up working!

Render order working!

Progress Summary

Successes:

  • Got amazing tips from Vox (Thanks!)
  • Implemented render groups to make sure some things always render on top of other things
  • Got render order working properly with a combination of rough AABB checks, then more detailed collision AABB checks

Potential plan for the next day of work:

  • Get one of those guys moving
  • Make the guy controlled by the keyboard

Lessons learned:

  • Always discuss things out loud with other people working on the same stuff. Odds are they will be happy to share you some knowledge that will fast track your progress

Leave a Reply

Your email address will not be published. Required fields are marked *