Create an account


Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
News - Blog: Rules for a future-proof 2D render order

#1
Blog: Rules for a future-proof 2D render order

<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order.png" width="243" height="77" title="" alt="" /></div><div><p> <!-- Google Tag Manager --> <!-- End Google Tag Manager --> &lt;!– –&gt; <title>Gamasutra: Julian Colbus’s Blog – Rules for a Future-Proof 2D Render Order</title> <!--[if lt IE 9]&gt; <a href="http://html5shim.googlecode.com/svn/trunk/html5.js">http://html5shim.googlecode.com/svn/trunk/html5.js</a> &lt;![endif]--> <!-- CSS --> <!-- link href="https://twimgs.com/gamasutra/css/minified.css" rel="stylesheet" type="text/css" / --> <!-- Mobile Specific Metas --> <!-- Start Visual Website Optimizer Asynchronous Code --> <!-- End Visual Website Optimizer Asynchronous Code --> <!-- Start HeatMap.me Code --> <!-- End HeatMap.me Code --> <!-- Start: GPT Sync --> <!-- End: GPT --> <!-- Eloqua tracking code --> <!-- End Eloqua tracking code --> <!-- Google Tag Manager (noscript) --> <!-- End Google Tag Manager (noscript) --> <!--Cookie banner code starts here --> <!--Cookie banner code ends here --> <!-- Informa Branding code goes here--> </p>
<div id="iribbon-container" class="content-body-wrapper"> <button id="iribbon-title" title="show/hide" class="inactive">Informa</button> </p>
<div id="iribbon-detail" class="ribbon-hide">
<div id="iribbon-left">
<p>Gamasutra is part of the Informa Tech Division of Informa PLC</p>
</p></div>
<div id="iribbon-right">
<p>This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC’s registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.</p>
</p></div>
</p></div>
</p></div>
<p> <!-- Informa Branding code goes here--> <!-- Beginning Sync AdSlot 19 for Ad unit Gamasutra/ ### size: [[2,2]] --> <!-- End AdSlot 19 --> <!-- Beginning Sync AdSlot 20 for Ad unit Gamasutra/ ### size: [[7,7]] --> <!-- End AdSlot 20 --> </p>
<div class="container">
<div id="container-main" class="content-body-wrapper"> <!-- Google Prestitial Ads start here--> <!-- Google Prestitial Ads end here--> </p>
<div class="container bottom3">
<div class="span-7"> <a href="https://www.gamasutra.com"><img loading="lazy" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order.png" alt="Gamasutra: The Art &amp; Business of Making Games" width="243" height="77" border="0"></a><img alt="spacer" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order.gif" width="27"> </div>
</p></div>
<div class="span-20"> <!--end show phone--> <!--end showphone--> <!--end nav--> </p>
<div class="span-20 last content_bg">
<div class="hide-phone">
<div class="span-4">
<div class="content_box_left">
<div class="leftcol"> <!--member login--> <!--end memberlogin--> <!--begin social icons--> <!--begin social icons--> <!--end social icons--> <!--end social icons--> <!--begin page numbers--> <!--end page numbers--> <!--begin leftnav--> <!--end leftnav--> <!-- Beginning Sync AdSlot 3 for Ad unit Gamasutra/ ### size: [[164,59]] --> <!-- End AdSlot 3 --> <!-- Beginning Sync AdSlot 4 for Ad unit Gamasutra/ ### size: [[164,59]] --> <!-- End AdSlot 4 --> <!-- Beginning Sync AdSlot 5 for Ad unit Gamasutra/ ### size: [[164,409]] --> <!-- End AdSlot 5 --> <!-- Beginning Sync AdSlot 9 for Ad unit Gamasutra/ ### size: [[164,177]] --> <!-- End AdSlot 9 --> <!-- Beginning Sync AdSlot 11 for Ad unit Gamasutra/ ### size: [[164,177]] --> <!-- End AdSlot 11 --> <!-- begin event tickers --> <!-- end event tickers --> <!--begin jobs--> <!--end jobs--> <!--begin blogs--> <!--end blogs--> <!--begin press releases--> <!--end press releases--> <!--begin calendar--> &lt;!– </p>
<div class="header"><img loading="lazy" alt="arrow" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-1.png" width="22px" height="20px" /><a href="http://www.gamasutra.com/calendar/calendar.php">Calendar</a></div>
<div class="leftnav bottom2"> <a href="http://www.gamasutra.com/calendar/calendar.php"><strong>View All</strong></a> &nbsp;&nbsp;&nbsp; <a href="http://www.gamasutra.com/calendar/calendar_submit.php"><strong>Submit Event</strong></a> </p>
<hr>
<ul> </ul>
</div>
<p> –&gt; <!--end calendar--> <!--begin about--> </p>
<div class="bottom2"> <a href="https://www.gamasutra.com/advertise"> <img loading="lazy" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order.jpg" alt="Sponsor" border="0" class="whiteTop" width="177px" height="60px"> </a>
</div>
<p> <!--end about--> <!--begin network--> </p>
<div class="leftnav_network bottom2"> If you enjoy reading this site, you might also want to check out these UBM Tech sites: </p>
<hr>
<hr>
<p> <br class="clear">
</div>
<p> <!--end network--> <!--begin store--> <!--end store--> </div>
<p><!--end leftcol--> </div>
</div></div>
<div class="span-16 last">
<div class="content_box_middle"> <!-- InstanceBeginEditable name="BodyContent" --> </p>
<div class="page_item">
<div><a name="twitter_share" href="http://twitter.com?status=RT @gamasutra: Rules for a Future-Proof 2D Render Order http://www.gamasutra.com/blogs/JulianCol...16/370201/" target="_blank" rel="noopener noreferrer"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-1.gif" alt="Share on Twitter" border="0" height="20"></a> <span id="edit_post_link"></span>&nbsp;&nbsp; <a href="https://www.gamasutra.com/blogs/rss/"><img loading="lazy" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-2.gif" width="15" height="15" border="0" alt="RSS"></a> </div>
<hr>
<div class="item_body mobile_image_transform"> <strong><i><small> The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.<br />The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company. </small></i></strong> </p>
<hr>
<p>Once you start working on a 2D game, the question of how to set up a proper render order will come up very early on. Render order, at least in the way I will be using the term, determines which parts of your 2D game get rendered (drawn, displayed, shown) in front of (i.e. later than) other parts.</p>
<p>Unfortunately, you won’t be able to figure out the requirements for a good system until after you need it. I just spent almost a week changing ours from what <em>I thought</em>&nbsp;would be good when we started our <a href="https://store.steampowered.com/app/1364100/Lacuna__A_SciFi_Noir_Adventure/">current project</a> to what many months later <em>turned out</em>&nbsp;to make a lot more sense. Here’s hoping that our findings will save you the trouble of going through that painful refactoring process and help you get it right from the start.</p>
<p>Please note:</p>
<ol>
<li>Some of this makes sense for any 2D game, but some will be specific to a perfect <em>side view</em>&nbsp;typical for side-scrolling games.</li>
<li>This system accounts for <em>real-time lighting</em>; if that’s no concern of yours, a simpler system might do.</li>
<li>It also accounts for <em>multiple vertical layers</em>, some of which are only revealed after a foreground sprite fades out. If your game doesn’t have this, parts of our system will be irrelevant for you.</li>
<li>I will be using some Unity-specific terminology like “Sorting Layer” and “Order in Layer”. Nevertheless, most of this article should be helpful on a conceptual level regardless of game engine.</li>
</ol>
<p>In case you’re working in Unity and are completely new to the topic, I thought I’d provide a short explanation of Unity’s Render Order system (as of Unity 2019.3.13) and give you a quick intro into its usage. If you already know it, skip right ahead to <em>Best Practices</em>&nbsp;below.</p>
<p>The following assumes that you actually work in a 2-dimensional space and aren’t using the Z axis to determine Render Order. If you want your sprites to use Unity’s 2D Render Order system instead of rendering based on your sprites’ Z positions, you need to select your camera and set its Projection to <em>Orthographic</em>&nbsp;instead of <em>Perspective</em>.</p>
<h2>1.1 Sorting Layers</h2>
<p>Once that is done, all your sprites will be rendered based on <em>Sorting Layers</em>&nbsp;(“SL” for short). These work essentially like layers in Photoshop or any other image editing software: All sprites on one SL will always be rendered in front of all sprites on another, thus adding depth on an “imagined Z axis”.</p>
<p>Let’s have a look at the available Sorting Layers. Add a SpriteRenderer component to a game object and select the <em>Sorting Layer</em>&nbsp;drop-down.</p>
<p><img loading="lazy" alt height="364" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-2.png" width="572"></p>
<p>Hit <em>Add Sorting Layer…</em>&nbsp;at the bottom to open the Tags and Layers window. It shows all existing SLs and allows you to add any number of new ones. “Default” cannot be deleted, but you don’t have to use it. The SL at the top of the list is rendered at the very back, the SL at the bottom is all the way in the front.</p>
<p>Most of the advice under <em>Best Practices</em>&nbsp;pertains to which layers you should set up, so I won’t go into detail about it here. However, here’s a Unity-specific tip: When adding new SLs, use a slash to nest them. Nested layers will then appear neatly grouped together in the SL drop-down on your Sprite Renderers. In the example below, we distinguish between middleground (MG) and background (BG), each of which encompass several SLs:</p>
<p><img loading="lazy" alt height="385" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-3.gif" width="570"></p>
<p>Once your SLs are set up, you can assign any sprite in your scene to them by selecting its SpriteRenderer component and picking the desired SL from the drop-down menu.</p>
<h2>1.2 Order in Layer</h2>
<p>Within each SL, sprites are again organized into an Order in Layer (“OiL” for short). While SLs have names, the OiL of a sprite is indicated as an integer. The higher it is, the further in front a sprite is rendered. Consider the following example containing three SLs: “Background”, “Middleground”, and “Foreground”.</p>
<table border="0" cellpadding="1" cellspacing="1" width="500">
<thead>
<tr>
<th scope="col">Sprite</th>
<th scope="col">Sorting Layer</th>
<th scope="col">Order in Layer</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sky</td>
<td>Background</td>
<td>-1</td>
</tr>
<tr>
<td>Moon</td>
<td>Background</td>
<td>0</td>
</tr>
<tr>
<td>Skyline</td>
<td>Background</td>
<td>1</td>
</tr>
<tr>
<td>House</td>
<td>Middleground</td>
<td>-1</td>
</tr>
<tr>
<td>Player character</td>
<td>Middleground</td>
<td>0</td>
</tr>
<tr>
<td>Column</td>
<td>Middleground</td>
<td>1</td>
</tr>
<tr>
<td>Laundry</td>
<td>Foreground</td>
<td>0</td>
</tr>
</tbody>
</table>
<p><img loading="lazy" alt height="340" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-4.gif" width="600"></p>
<p>Even though sky, moon, and skyline are all on the background layer, they are rendered in the correct order because of their Order in Layer. As you can see, an OiL can have a negative int value.</p>
<p>At this point, you might think that you know all you need to render your sprites in the right order. If you’re going for a more simple look, that might be the case. However, if you want to go the extra mile (especially if you have &nbsp;dynamic lighting), here are some more specific tips for setting up Sorting Layers (SLs) and Orders in Layer (OiL) in a future-proof way. And even though the terminology comes from Unity, much of the following advice should apply to any engine on a conceptual level.</p>
<h2>2.1 Background and Foreground</h2>
<p>For our project, we have decided to distinguish between background (BG), middleground (MG), and foreground (FG). This is merely a conceptual distinction; on a technical level, each of these is made up of multiple Sorting Layers. We also added a Sorting Layer named “Dev” for sprites that we use for development only that’s invisible to the player. Something we didn’t do (but might in the future) is add another visible layer for all World Space UI to ensure that it renders in front of all non-UI sprites.</p>
<p><img loading="lazy" alt height="339" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-5.gif" width="600"></p>
<blockquote>
<p>Hiding and showing the three main groups of Sorting Layers (BG, MG, and FG) using a custom script</p>
</blockquote>
<p>No matter how you end up doing it, one thing to keep in mind when setting up SLs is lighting. Each 2D Light component in Unity can be set to affect or not affect any number of Sorting Layers:</p>
<p><img loading="lazy" alt height="325" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-3.png" width="414"></p>
<p>This means that the system determining render order and the system determining which lights affect which sprites are intertwined. This may cause problems; for example, let’s go back to the example shown under <em>Sorting Order</em>&nbsp;above: If you add a light to brighten the moon, it will inevitably (within its range) also brighten the sky and skyline because all three of them are on the same Sorting Layer. You might not want one light in the background to affect another background layer that seems very far away from it (on the imagined Z axis). Luckily, this can easily be resolved by making the system more granular, i.e. by adding more Sorting Layers. The moon and sky could remain on the same SL and the skyline could be put on a new one that’s visually closer to the player. This way, the moon would light up the clouds, but not the skyline.</p>
<p>For our game, we created 7 Sorting Layers for the background alone. We ended up with that number specifically after experimenting and deciding that 7 visually distinct layers looked alright. You’ll notice that some background layers contain a big, semi-transparent sprite that adds atmospheric tint:</p>
<p><img loading="lazy" alt height="339" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-6.gif" width="600">Incidentally, we got another usage out of those 7 background layers by giving each one its own parallax speed. (Creating a parallax effect without the need for a Z axis is a topic for another day.)</p>
<p><img loading="lazy" alt height="338" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-7.gif" width="600"></p>
<p>Some of those SLs may be further subdivided into Orders in Layer. For example, this street in the background is made up of three parts in itself: handrail in front, NPCs in the middle, and the rest in the back.</p>
<p><img loading="lazy" alt height="527" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-8.gif" width="600"></p>
<p>We also added a “negative parallax” layer to the very foreground that moves in opposition to the camera and the parallax layers in the background:</p>
<p><img loading="lazy" alt height="338" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-9.gif" width="600"></p>
<p>The middleground is where it gets complicated, so let’s give it a closer look!</p>
<h2>2.2 Middleground</h2>
<p>The middleground (at least in our terminology) is where all the action takes place, where the player and NPCs live, and as a result, where things move around a lot. I’ll first explain how we set it up and then elaborate on the reasoning behind it.</p>
<p><img loading="lazy" alt height="339" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-10.gif" width="600"></p>
<h3>2.2.1 Sorting Layers</h3>
<p>This next distinction might appear arbitrary at first, but it is in fact the ideal result we arrived at after a long series of experimentation: There are generally two types of Sorting Layers in the middleground; “movement layers” where the player and NPCs move around (called <em>MG/middle</em>&nbsp;and <em>MG/back</em>), and “wall layers” separating them (called <em>MG/frontwall</em>, <em>MG/middlewall</em>, and <em>MG/backwall</em>). Think of this latter type as the “walls” sandwiching the middle and back SLs between each other (even though they contain many sprites not specifically depicting walls).</p>
<p>The player and NPCs can only move on <em>MG/middle</em>&nbsp;and <em>MG/back</em>&nbsp;and sometimes switch from one to the other. Differently put, they can be in “middle areas” (where they are rendered on <em>MG/Middle</em>) and “back areas” (where they are rendered on <em>MG/back</em>). The distinction between these two isn’t always immediately apparent and may seem open for interpretation. When does the player transition from middle to back? The quick answer is: Whenever they “pass by” a layer of sprites (specifically <em>MG/middlewall</em>) that rendered behind them before and now renders in front of them, i.e. when they walk past a wall layer.</p>
<p>By doing so, the player may enter the bounds of a sprite on the wall layer inbetween that then fades out. We call these “facades”, and one can be seen in the first example below.</p>
<p><em>Moving up:</em>&nbsp;Buildings and areas entered via frontal stairs from below are usually considered as the player entering a back area because the player walks “further into” the screen or “away from the player”, behind the wall layer they were previously in front of:</p>
<p><img loading="lazy" alt height="340" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-11.gif" width="600"></p>
<p>However, this isn’t necessarily the case when moving up. In the example below, the player stays on <em>MG/middle</em>&nbsp;the whole time. That is because they never pass by a wall layer; the facade sprite that fades out was in front of them the whole time (on <em>MG/frontwall</em>), as you can see from the columns at the bottom:</p>
<p><img loading="lazy" alt height="338" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-12.gif" width="600"></p>
<p><em>Other movements:</em>&nbsp;Entering a building or area from the side or top is different as the player never goes “further into” the screen, thus never bypassing a wall layer, no matter if they walk normally, use stairs, or walk through a door. This case <em>never</em>&nbsp;counts as entering or leaving a back area because no movement on the imagined Z axis occurs.</p>
<p><img loading="lazy" alt height="388" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-13.gif" width="600"><em>Teleporting:</em>&nbsp;Teleporters moving the player from an open area into a room or building aren’t all the same. If the player is transported behind a sprite that was previously behind the player (again: if they move past <em>MG/middlewall</em>), they are indeed moving from <em>MG\middle</em>&nbsp;to <em>MG\back</em>:</p>
<p><img loading="lazy" alt height="450" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-14.gif" width="600">However, if the player does not move past a sprite that was previously behind them, they stay on the same layer. In the example below, the player teleports from <em>MG\middle</em>&nbsp;to <em>MG\middle</em>.</p>
<p><img loading="lazy" alt height="450" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-15.gif" width="600"><strong>2.2.1.1 What to look out for</strong></p>
<p>After telling you what worked, I also want to talk about the mistake we made with our Sorting Layers that caused me to rebuild the system completely to the way it is now. I urge you to read this bit as it might save you a lot of headache!</p>
<p>The render order we used to have included only the Sorting Layers <em>MG/front/middle/back/veryback</em>. However, this fell apart with the introduction of 2D Lights: In certain scenarios, a sprite needed to be rendered on a certain layer but wasn’t supposed to be affected by a light source that applied to other sprites on that same layer. This mostly affected facades; consider the example below:</p>
<p><img loading="lazy" alt height="403" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-4.png" width="600"></p>
<blockquote>
<p>Example setup; sprites have been moved on the Z axis only for demonstration purposes</p>
</blockquote>
<p>The player needs to be rendered in front of the facade while outside the building. Therefore, since the player is on the (formerly) <em>MG\middle</em>&nbsp;layer, the facade needs to be on the same or a lower middleground layer to be rendered behind the player; it cannot be on <em>MG/front</em>.</p>
<p><img loading="lazy" alt height="240" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-5.png" width="600"></p>
<blockquote>
<p>Example: outside (MG/middlewall)</p>
</blockquote>
<p>&nbsp;<img loading="lazy" alt height="225" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-6.png" width="600"></p>
<blockquote>
<p>Example: inside (MG/backwall)</p>
</blockquote>
<p>However, light sources <em>inside</em>&nbsp;the building apply to (formerly) <em>MG/middle</em>&nbsp;as well as (formerly) <em>MG/back</em>&nbsp;to light up the correct sprites on the inside, behind the facade. As a result, lights rendered <em>behind</em>&nbsp;the facade appear to fall on the <em>front</em>&nbsp;of the facade, which shouldn’t be the case.</p>
<p>That’s why the facade has to be on another layer <em>between</em>&nbsp;the player and the regular <em>MG\middle</em>&nbsp;layer, which is only affected by lights in front of it. To allow for this, we needed to add more layers.</p>
<p>Unfortunately, simply adding more layers only half-solved the problem because sprites like the player and NPCs may switch from middle to back and vice versa. As a result, any light on one of those layers will <em>either not affect</em>&nbsp;those moving sprites <em>or always affect</em>&nbsp;them, including in the wrong scenarios (i.e. through walls). That is why we came up with the distinction between <em>middle areas</em>&nbsp;and <em>back areas</em>&nbsp;as well as the wall layers between them. Moving from one to the other changes the moving sprite’s Render Layer to <em>MG/middle</em>&nbsp;and <em>MG/back</em>&nbsp;respectively (simply based on entering trigger colliders). As a result, a light source affects them when they’re right next to it in the same room – but not when they’re right next to it <em>but with a wall-inbetween</em>.</p>
<h3>2.2.2 Order in Layer</h3>
<p>Similarly to the background, each Sorting Layer in the middleground is in itself divided into multiple Orders in Layer (OiL). Here’s what we learned about using those.</p>
<p><strong>2.2.2.1 Keep OiL the same across SLs</strong><br />The “wall layers” are each structured very similarly to one another, meaning that one wall layer’s OiL values are (almost) exactly the same as all the other wall layers’ OiL values. The two movement layers also share the same OiL values. This makes memorizing OiL much easier and considerably speeds up placing sprites in the scene. For example, one type of sprite (e.g. foliage) has the same OiL across all wall layers on which it occurs. Make sure to set up external documentation for these general OiL rules (and your Render Order in general)!</p>
<p><strong>2.2.2.2 Reserve OiL</strong><br />In our project, some of the sprites can be interacted with (we call these “Interactables”Wink and show outlines once the player gets in range. The outlines need to be rendered <em>behind the Interactable</em>, but <em>in front of the sprite behind it</em>. This means that one OiL <em>between</em> the Interactable and the sprite right behind it needs to be reserved for the outline to prevent “fighting” for render order between the outline and the background sprite. For instance, if an Interactable (e.g. a cigarette vending machine) is on OiL 1 and the sprite behind it (e.g. a wall) is on OiL 0, the outline of the Interactable would have nowhere to go in-between the two. That’s why we have now reserved certain OiL for certain renderers: The OiL of any sprite must always be set to a multiple of ten (i.e. end in 0); outlines always render 1 OiL behind the sprite they belong to (i.e. end in 9). In our example, the cigarette vending machine could be on 10, the wall behind it on 0, and the vending machine outline on 9. Particle effects also often overlap with multiple sprites in uncontrolled ways, so we always place them on an OiL ending in 8 to avoid “fights”. Layers ending in 2 to 7 are still free for future special usage if the need arises.</p>
<p><strong>2.2.2.3 Dynamic OiL</strong><br />Most moving sprites in our game, like characters, update their Order in Layer every frame depending on their y position. This has to happen in order for them to render in the correct order relative to one another (on the same SL) where movement on the Y axis occurs, e.g. on stairs. In the example below, the player character renders in front of the NPCs above it, but behind the NPCs below it.</p>
<p><img loading="lazy" alt height="461" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-7.png" width="600"></p>
<p>Simply binding the OiL to an object’s y position – the further up it is on the screen, the lower its OiL – <em>might</em>&nbsp;be enough depending on your setup. However, you’ll likely run into a couple of problems:</p>
<p><em>Problem 1:</em>&nbsp;This might not work for special situations, e.g. complex set piece animations with lots of moving parts. &nbsp;<br /><em>Solution:</em>&nbsp;That is why we recommend adding a boolean so you can turn this functionality off if you ever desire to set the Order in Layer manually instead.</p>
<p><em>Problem 2:</em>&nbsp;What about the rule that sprites can only be rendered on OiL that end in 0? &nbsp;<br /><em>Solution:</em>&nbsp;To ensure that this is the case for moving sprites as well, just multiply the OiL by 10. (Concrete formula below.)</p>
<p><em>Problem 3:</em>&nbsp;Calculating the render order of sprites based on their relative position only works for sprites that rest on the floor. &nbsp;<br /><em>Solution:</em>&nbsp;If your sprites can jump, float, or fly, things get a bit trickier. The short answer is: take the distance between a sprite and the ground platform it relates to into account when calculating the sprite’s OiL. This wasn’t a problem for us because we have no jumping mechanics, so I won’t go into more detail about it. However, [this great blog post](https://breadcrumbsinteractive.com/two-u...ric-games/) does!</p>
<p>Finally, here’s our complete OiL calculation formula (located in the _Update()_ method) followed by a quick explanation:&nbsp;</p>
<pre><code>movingSprite.sortingOrder = -1 * (Mathf.RoundToInt(transform.position.y * 100f) * 10);</code></pre>
<table border="0" cellpadding="1" cellspacing="1" width="500">
<thead>
<tr>
<th scope="col">Element</th>
<th scope="col">Reasoning</th>
</tr>
</thead>
<tbody>
<tr>
<td>movingSprite.sortingOrder</td>
<td>The sprite’s Order in Layer value.</td>
</tr>
<tr>
<td>-1</td>
<td>Sprites higher up are actually further in the back, i.e. the higher the y value, the lower we want the OiL to be.</td>
</tr>
<tr>
<td>Mathf.RoundToInt()</td>
<td>The OiL needs to be an integer. Use rounding rather than casting to int, which only chops off the decimals.</td>
</tr>
<tr>
<td>transform.position.y * 100</td>
<td>Take the sprite’s y position down to the second position after the decimal point before rounding. (bigger order of magnitude = more precise)</td>
</tr>
<tr>
<td>* 10</td>
<td>Make sure the sprite’s OiL always ends in 0 as established above.</td>
</tr>
</tbody>
</table>
<p>This is what it looks like in the game. Note that the OiL at the top changes as the player moves up and down.</p>
<p><img loading="lazy" alt height="435" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-16.gif" width="600"></p>
<p>And that’s how we do it!</p>
<p>I’ll talk more about properly setting up 2D assets, e.g. creating a parallax effect and how to handle pixel art, in future blog posts.&nbsp;Keep an eye our for that on <a href="https://digitales.games/blog">our blog</a>&nbsp;or <a href="https://twitter.com/digitales_games">our Twitter</a>. If you found an error in this post or see room for improvement, I’ll be happy to update it. And if you found it useful, feel free to share it!&nbsp;</p>
<p>Cheers, &nbsp;<br />Julian</p>
</p></div>
<hr>
<div class="hide-phone">
<h3>Related Jobs</h3>
<div class="stories_item">
<div class="thumb"><a href="https://jobs.gamasutra.com/job/senior-engine-programmer-champaign-illinois-34567"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-1.jpg" alt="Deep Silver Volition" width="120"></a></div>
</p></div>
<div class="stories_item">
<div class="thumb"><a href="https://jobs.gamasutra.com/job/senior-technical-designer-champaign-illinois-34566"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-1.jpg" alt="Deep Silver Volition" width="120"></a></div>
</p></div>
<div class="stories_item">
<div class="thumb"><a href="https://jobs.gamasutra.com/job/ue4-technical-artist-london-england-34565"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-8.png" alt="Random42" width="120"></a></div>
</p></div>
<div class="stories_item">
<div class="thumb"><a href="https://jobs.gamasutra.com/job/senior-technical-developer-bordeaux-34539"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-9.png" alt="Evil Empire" width="120"></a></div>
</p></div>
</p></div>
<hr></div>
<p> <!-- InstanceEndEditable --> </div>
<p><!--end contentbox-->
</div>
<p><!--end span-16--> </div>
<p> <br class="clear"> </div>
<p><!--end span-21--> <!--begin right sidebar--> <!--end right sidebar--> &lt;!– </div>
<p> Extra Div –&gt; <!-- Beginning Sync AdSlot 21 for Ad unit Gamasutra/ ### size: [[8,2]] --> <!-- End AdSlot 21 --> <!-- Beginning Sync AdSlot 22 for Ad unit Gamasutra/ ### size: [[8,4]] --> <!-- End AdSlot 22 --> <!-- Beginning Sync AdSlot 23 for Ad unit Gamasutra/ ### size: [[4,4]] --> <!-- End AdSlot 23 --> <br class="clear"></p></div>
<p><!--end content-body-wrapper--> </div>
<p><!--end container--><br />
<!--begin footer--> <!--end footer--> <!-- SiteCatalyst code version: H.21.
Copyright 1996-2010 Adobe, Inc. All Rights Reserved
More info available at http://www.omniture.com --> <a href="http://www.omniture.com" title="Web Analytics"><img loading="lazy" src="https://www.sickgaming.net/blog/wp-content/uploads/2020/09/blog-rules-for-a-future-proof-2d-render-order-17.gif" height="1" width="1" border="0" alt></a><!--/DO NOT REMOVE/--><br />
<!-- End SiteCatalyst code version: H.21. --> <!-- Begin ADSNATIVE Code --> <!-- End ADSNATIVE Code --> <!--Begin Ad script for pixel --> <!--END Ad script for pixel --></p>
</div>


https://www.sickgaming.net/blog/2020/09/...der-order/
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

Forum software by © MyBB Theme © iAndrew 2016