Render Layers


September 18th, 2024

Hello! Today, I'll cover how I handle render layers. This refers to the order in which things are drawn. For example, I want to make sure the player is always drawn on top of the background, and not the other way around. 

NOTE: I use the terms "render layer" and "render priority" interchangeably. 

Defines

Here are some constants and typedefs to reduce verbosity:

The MAX_RENDER_LAYERS is the size for an array I'll discuss later. By default, entities will be rendered last, so DEFAULT_RENDER_LAYER is the last index of that array. SharedEntity is just a shared Entity pointer. The EntitySortFunction is a bit interesting. This function denotes the compare condition for sorting a specific layer. I'll go into detail later.

Here is an enum I'll use to specify render layers:

Entity Additions

In the Entity, I'll add a variable for it's render layer. This is set to DEFAULT_RENDER_LAYER by default. I've created a getter and setter for this variable:

The mRenderPriority is private because if it changes, we need to callmEntityManager->ChangeRenderPriority. So whenever we change the mRenderPriority, SetRenderPriority needs to be called.

Entity Manager

I'll introduce EntityManager's the new members: 

The mLayerCount keeps track of how many entities belong to each render layer. The mDynamicSortFunctions stores a compare function for dynamic, inner-layer sorting. These compare functions will be called every frame.

We do this to give every render layer it's own sort function. For example, I have two different rendering layers, Characters and Backgrounds. I want my characters to be sorted by their y-value, so higher characters appear behind lower characters:

This is fine, but I don't care to separate my Backgrounds by their y-value because it doesn't matter as long as the Backgrounds are behind the Characters. By setting lambda functions in mDynamicSortFunctions, I can specify that I want to sort JUST the Characters by their y-value. The code for that will be in the Demonstration section. 

We'll have to modify MakeEntity to increment the correct value in mLayerCount:

This updates the default layer, but what about when we change layers? This is where the ChangeRenderPriorty function comes in:

This updates the counts for the correct indices. The "compare" lambda specifies we want the lowest render priorities (which is 0) at the front of the array (which is 7). 

The EntityInsertionSort function is just what it sounds like, an insertion sort for an array of SharedEntities array:

I opt to do an insertion sort because the average time complexity is linear ( O(n) ) on an array that's already sorted. It's unlikely that more than one entity will change its render layer at a time, so this is more efficient than a quick sort on average. The reason I created a function for this is because we'll use different "start" and "end" iterators in the next function. 

We still need to ensure the render layers are updated per frame, according to their dynamic sort function. We can do this in my Refresh function, which is called before Update: 

Using the mLayerCount, we make sure to only look at entities that exist on the render layer we're looking at per loop. The lowerBounds and upperBounds serve as the offset for to the mEntities array. We can add this offset to the mEntities.begin() iterator to only sort relevant entities. 

Here is a demonstration of it in action!

Demonstration

I want to sort the Characters by their y-axis. I'll do this at the top of my DribbleKing::Init()

This compare function sorts entities by their y-coordinate plus their height. This ensures if the bottom of a character's sprite is lower than another, it will be rendered behind it. 

After each object specifies its RenderPriority, that's all it takes on the user's end! Here's a video of it working:

 

Conclusion

I'm happy with this implementation, I like that it doesn't take much on the user's end to make a custom sorting function. I reapplied what I learned about InsertionSort here (from when I was implementing Sweep and Prune), which felt nice. 

This concludes the most basic systems for the game. I plan on fleshing out the Game Design Doc a bit more and soon I'll be working on some game-specific mechanics. Stay tuned for updates!

Thanks for reading,

Jamari

Leave a comment

Log in with itch.io to leave a comment.