GDC Logo

Deep Dive: Designing Candy Transit and the distinctive challenges of programming in 2D

Posted on

Truscinski Deep Dives are an ongoing collection with a objective of shedding gentle on particular design, artwork, or technical options inside a online game with a view to present how seemingly easy, elementary design choices aren’t actually that straightforward in any respect.

This text was written by Ernestas Norvaišas, the solo developer behind Candy Transit, a strategic metropolis constructing sport set in a world the place the railway is king and trains are the only real technique of transportation and enlargement.

When designing a sport, working with 2D relatively than 3D graphics can typically appear the better alternative. With extra elements, variables, and issues that may go fallacious, 3D can seem the extra complicated area and difficult possibility by which to construct a world. Candy Transit is an upcoming 2D isometric transport sim and metropolis builder, and I assumed I would share the truth of working inside this area, in addition to among the challenges distinctive to 2D programming that I’ve encountered for the reason that venture’s inception.

3D Limitations & Graphics Sorting

To start, let’s take a look at 3D. In 3D video games, most course of sorting is completed inside the GPU and measured by the depth of every pixel and its proximity to the sport’s digital digicam. The CPU assists in optimisation and sorting clear objects, however the majority of the heavy lifting is completed by the graphics card. In 2D video games, it is fairly the alternative — most sprites are clear, giving the GPU little or no to render and that means the restrictions imposed on 2D programming are these from the CPU relatively than the GPU. Because of this working inside 2D area can come alongside a novel set of challenges you’d by no means encounter in 3D.

Not like 3D, 2D graphics sorting is not completed per pixel however holistically, by particular person in-game photographs or sprites. For the designer, this leaves quite a bit much less area to work inside. A typical instance of those limitations might be seen in one thing like a 2D side-scrolling sport, the place the foreground will at all times seem to sit down in entrance of the background. Isometric 2D video games, like Candy Transit, use the place of the display to find out the order of photographs rendered. Funnily sufficient, these on high are literally behind these on the underside and the rendering course of is executed in that order, one graphic at a time.

One other limitation of 3D is that graphical constancy is usually dictated by the ability of the tip consumer’s graphics card. There’s a restrict to what number of polygons can concurrently go by a GPU and produce a constant picture of 60 frames-per-second. In 2D, graphical energy is never a problem. There are instances the place over-rendering and pairing a comparatively sluggish GPU with a 4K monitor may end up in drop in framerate, however these are fairly uncommon.

2D Graphics Sorting

In 2D, the CPU is the limiting issue. Earlier than sending the whole lot you wish to render to the GPU, you first want to assemble your sprites and type them into your required show order. That is no straightforward job, even with fashionable CPUs. At present, in Candy Transit, there are a median of 40,000-60,000 sprites being rendered inside every body of the sport. To mitigate a 2D, CPU-heavy workload, one efficient device is ‘layering’. In 2D isometric video games, particular person picture layers home particular person objects. For instance, tiles and water exist on their very own layers, as do bushes, trains, and constructions.

All through the layering course of, one display of sprites is split into a number of horizontal strips, that are rendered, then sorted utilizing threads. When merging the strips, the layering course of means the sport solely must kind the tip of every strip relatively than the entire assortment, which might be very economical in relation to processing energy.

After the layering course of, all sorted 2D sprites are mixed into a number of massive 3D objects — a course of which, when rendering, permits the programmer to concern smaller instructions to the GPU by solely sending the ID of every sprite as an alternative of the complete batch of particular person photographs. Indices within the GPU are by no means touched however as an alternative, assigned originally of every rendering course of to assemble every new batch of sprites and permit the processing value of every batch to be saved to a minimal.

Imitating Shadows and Gentle

In 2D, In-game shadows are additionally rendered on their very own layer. Shadows on the bottom are extra easy and the sport will render a sprite for every whereas taking into consideration the particular angle of particular person objects. Nonetheless, for Candy Transit, I wished shadows for all in-game objects, so I rendered every shadow as its personal picture, then layered this onto every shadow’s mother or father sprite. This course of produced extra real looking shadows at solely a tiny processing value, so it was a no brainer. After being sorted, the shadow layer renders on high of the sport layer to mimic day and night time cycles. This works effectively, however the downside is that shadows aren’t solid on different objects.

Sprite Slicing

The most typical drawback in making an isometric sport is defining, to the system, methods to kind two photographs positioned at an similar peak on-screen. For an individual, with our frames of reference and powers of spatial reasoning, it is clear what needs to be on high, however a pc has no thought. A number of options exist for this drawback however they’re all fairly processor heavy and for Candy TransitI wished efficiency and economic system of processing to be a precedence.

For my very own answer, I made a decision to slice every sprite into a number of elements. I manually outlined what number of slices ought to happen after which had an automated system do the remaining. Nonetheless, this proved inadequate because of the sheer variety of layers on high of every sprite. For instance, a prepare wagon often has a number of picture layers sitting on high, akin to a tint masks, lights, and cargo. Once I ran the automated system, it calculated the sorting primarily based on how far the sliced ​​sprite was from the middle of the picture and produced layers of various width. Not best and positively not what I used to be after.

Subsequent, I attempted calculating offsets primarily based on the mother or father sprite, in order that the wagon layer would at all times be rendered beneath its cargo, however this meant I may not share cargo layers between wagons! Ultimately, I discovered the best choice to be defining my actual slice positions and offsets. Now, when the sport sliced ​​every sprite, it calculated the place of every slice primarily based on the precise place of every sprite (on this case the wagon), relatively than the sprite itself. Success!

The Bridge Downside

Rendering bridges in Candy Transit was one other unexpected problem. In a typical train-under-bridge state of affairs, there are a number of layers being sorted and rendered. First, is the bridge behind the prepare; then the prepare itself; and at last, a bridge in entrance of the prepare. The variety of layers is not the issue however once you add outdoors affect, akin to one other prepare touring throughout the highest of a bridge, this provides in lots of extra picture layers to be sorted, all inside a really tight area.

To resolve this, I first added a high world layer, that means each picture above a sure peak could be minimize off and rendered individually by the sport. This labored and meant that I may now kind the underside and high trains individually. Nonetheless, it was removed from the cleanest answer. Executing this course of would require 30% extra sprites, and modders would then have a right away overhead in ensuring all taller sprites had been sliced ​​accurately. On high of this, sorting trains on an inclined floor additionally proved problematic, because it was the one occasion of a backside layer shifting to the highest. Even when an incline had ten layers, one would at all times clip by one other.

After many makes an attempt, I made a decision so as to add small, figuring out indicators referred to as ‘flags’. First, the sport would kind every sprite primarily based on its depth, then this system would do one other go and search for flags tagged to every sprite. If the rectangles intersected, the sport would inform a sprite with a prepare flag to render above a sprite with a bridge flag. The concept was that I may accurately kind a bridge state of affairs by telling the sport to render a prepare sprite with a prepare flag above a bridge sprite with a bridge flag. The draw back was that efficiency took a success because of the further processes however this was balanced out by the sport having to render fewer sprites total.

Lastly, the bridge drawback was solved however as with the whole lot in sport growth, not shortly, and with some pesky aspect instances. For instance: wheels. A sprite with a prepare flag would seek for sprites with a ‘wheels’ flag to accurately place the wheels underneath the locomotive. No matter whether or not the prepare moved above or beneath the bridge, the wheels ought to additionally kind accurately. The issue was when two particular person trains handed throughout and beneath the bridge on the similar time. This meant a number of prepare sprites intersecting with a number of wheels sprites, and in some instances, the wheels rendering underneath the fallacious prepare!

To resolve this, I spent a day scratching my head, making an attempt totally different approaches, and taking part in with sorting distances and the underlying logic of a second sorting go. Ultimately, I had the sport test the middle intersection of every wheels sprite, then prioritise the prepare sprite closest to every set of wheels.Voila! Wheels all sorted.

Adorning a 2D World

Typically, sorting itself is just too processor heavy. In Candy Transit, I found this myself when including distinctive visible decorations, akin to bushes and clumps of grass, to the sport. To minimise the processing wanted to render these objects, I made positive that some in-game decorations could be dynamic and never cached anyplace. This meant the visuals would have selection however as soon as a construction was eliminated, the bottom did not look empty and barren to the participant.

Nonetheless, having all of those efficiency and RAM-cheap visible decorations launched one other drawback: collision detection. For constructions, this was positive, as these had been rendered above any decorations. Nonetheless, some elements of Candy Transit‘s railway tracks render beneath the ornamental parts, so this did generally trigger some clipping.

To resolve the ornament drawback, I added collision checks to any railway tracks and decorations rendered inside every body. To my full lack of shock, this hit efficiency however, in pairing this with one of many oldest methods within the guide, the traditional ‘gradual distance culling when zooming out’, I took care of a superb chunk of the issue. I additionally improved efficiency by ensuring collision checks had been as economical as potential and weren’t creating any new variables when executed.

The world ofCandy Transit is not static however dwelling, so it additionally made sense that some railway tracks needs to be overgrown by vegetation. For this, I made a decision that these much less ceaselessly used railway tracks ought to enhance and develop into overgrown over time. These ‘decorations’ would even be naturally trimmed any time a prepare ran over them.

Because it turned out, irrespective of what number of trains or tracks I added, there wasn’t a lot of an issue and the answer was fairly economical! All tracks in Candy Transit have a utilization struct of three bytes: one for the overgrowth counter and two devoted to the ultimate second of every calculation. Every time a prepare makes use of a monitor, the struct will replace the counter and mark the second. When the monitor is rendered, the sport checks when the three byte calculation was final executed and updates, if obligatory. From right here, I simply informed the sport to calculate what number of procedural decorations it will must render primarily based on how typically every monitor is used.

Making video games in 2D typically seems like fixing all points with duct tape. One addition I’d like to make to Candy Transit earlier than its launch later this 12 months is so as to add correct locomotive headlights to trains operating at night time, but it surely offers me shivers simply fascinated by how I would do that. On the finish of the day, 2D picture sorting is like making any artwork: if completed correctly, folks will not discover you probably did something in any respect, and that is the largest achievement you’ll be able to hope for. After engaged on the sport for therefore lengthy, there have been instances after I’ve thought that perhaps 3D would have been the better alternative by which to program, however I like the flexibility of 2D sprites and how one can pack in so many tiny particulars whereas nonetheless holding system necessities comparatively low for the participant. And it is the small issues in Candy Transit that I am most pleased with. I am unable to wait for everybody to find them for themselves later this 12 months.

Candy Transit releases into Steam Early Entry in 2022