Adding 3D Terrain to my 2D Game [DEVLOG] — Transcript
Full transcript
- 0:00For over 2 years now, I've been working
- 0:01on my indie project, Isoccor. And after
- 0:03all this time, I finally decided to
- 0:05tackle the biggest challenge I've ever
- 0:07faced in gamedev. For those of you who
- 0:08are new here, first of all, hi and
- 0:10thanks for stopping by. Isoore is a 2D
- 0:13isometric open world sandbox game
- 0:14focused on exploration, farming,
- 0:16crafting, and building. And if you've
- 0:18seen any of my earlier videos, you've
- 0:20probably noticed that the world has
- 0:21always been completely flat. Well, not
- 0:23anymore. Ever since I started this
- 0:25project, there's been one request that
- 0:27I've gotten over and over again. So, it
- 0:29seems like people really want some
- 0:31elevation in the game. For most of you,
- 0:32it also might seem like a logical step
- 0:34in the development of this project, but
- 0:36it's not like I didn't have that idea
- 0:37already. The truth is, until now, I've
- 0:39actually tried avoiding it for a long
- 0:41time. But why did it take me so long to
- 0:43start putting some elevation into the
- 0:44game, you might ask yourself, and there
- 0:46are multiple reasons, which you're going
- 0:47to see later on in this video. But the
- 0:49request for this feature has been so
- 0:50incredibly large and consistent that I
- 0:52finally decided to give it a try. When
- 0:54you just look at the game without
- 0:55knowing what's happening under the hood,
- 0:56it might seem pretty straightforward to
- 0:58add some elevation. Just add some more
- 1:00blocks in a few places and you're done,
- 1:02right? Well, not quite. Isoore isn't a
- 1:053D game. It's a 2D pixel art game, which
- 1:08means that all sprites are completely
- 1:10two-dimensional and the entire game
- 1:11takes place on a flat 2D canvas. Every
- 1:14sprite has a 2D position on that canvas
- 1:16and a value called a sorting order. That
- 1:18sorting order acts as the third
- 1:20dimension in a 2D game, but it's not a
- 1:22real dimension. It simply tells the
- 1:24program in what order to draw the
- 1:25sprites so that the correct ones appear
- 1:27on top and the other ones below. So that
- 1:29value isn't useful for any actual 3D
- 1:32logic. However, to have a dynamic 3D
- 1:34world, you need to know exactly where
- 1:35each object is on all three axes so you
- 1:37can calculate physics, collisions, and
- 1:39other interactions correctly. That means
- 1:41that every object now needs two
- 1:42positions. An internal 3D position
- 1:45inside the world and the 2D canvas
- 1:47position you end up seeing on the
- 1:48screen. So to implement elevation into a
- 1:502D game, I had to completely separate
- 1:52the game's internal functionality and
- 1:54calculations from its visual front end.
- 1:56Up until now, I could just use the 2D
- 1:58canvas position for everything.
- 2:00Movement, collision, rendering, all of
- 2:01[music] it. But with how large this
- 2:03project has grown over time, doing the
- 2:05separation was a massive task. Still,
- 2:08there was no way around it. So I sat
- 2:10down and got to work. I also used the
- 2:11opportunity, since I had to touch most
- 2:13of the game's core [music] systems
- 2:14anyway, to refactor some old code that
- 2:16was, let's just say, pretty bad and
- 2:19definitely overdue for a rewrite. After
- 2:21a good amount of time, I finally managed
- 2:22to separate all the internal mechanics
- 2:24from the front end. Now, every object is
- 2:26positioned internally using a 3D vector
- 2:28inside the terrain. When drawn on
- 2:30screen, that position gets passed
- 2:31through a few functions that calculate
- 2:33the correct canvas position and sorting
- 2:34order. With my game now using actual 3D
- 2:37positions to calculate everything, I
- 2:38could finally start working on the
- 2:40actual elevation feature. As I mentioned
- 2:42in the last video, I've been putting a
- 2:43lot more time and effort into this
- 2:44project the last couple of months. But
- 2:46while going to university, I end up
- 2:48working a lot from campus, libraries,
- 2:49and cafes. So, there is no real way
- 2:52around public Wi-Fi for me a lot of
- 2:53times, which can become a real security
- 2:55issue since the last thing you want is
- 2:56someone snooping on your connection or
- 2:58stealing your data by inserting
- 2:59themselves between your device and the
- 3:01router. But thankfully, I didn't have to
- 3:03worry about that for the last couple of
- 3:04months, thanks to this video's partner,
- 3:06NordVPN. NordVPN is a VPN provider that
- 3:08encrypts your internet traffic so that
- 3:10your data stays safe no matter where you
- 3:12connected. At the same time, NordVPN is
- 3:14also able to recognize malicious links
- 3:15and warns you about dangerous websites,
- 3:17helping to keep you safe from fishing
- 3:19attacks. Also, nowadays, there's almost
- 3:21nothing you can do online without having
- 3:22to create an account first, even for the
- 3:24most rudimentary things. And thinking of
- 3:26a new and complex password each time
- 3:28this happens is super tedious. And let's
- 3:30be honest, I'm pretty sure that most
- 3:32people don't do that. But using the same
- 3:33password over and over again is a huge
- 3:35security risk. All it takes is some
- 3:37company you gave your data to years ago
- 3:39and don't even remember to have a data
- 3:41leak, and your email, phone number, and
- 3:42connected password will be swimming
- 3:44around the internet for others to try
- 3:45them on your other accounts. But with
- 3:47NordVPN's password manager, NordPass,
- 3:49which comes included in the Plus plan,
- 3:50this is no longer an issue. It keeps
- 3:52your password safe and makes it super
- 3:53easy to have highly complex and secure
- 3:55passwords for each and every account.
- 3:57Plus, it makes the login process much
- 3:59faster since you no longer need to
- 4:00remember them all or look them up. If
- 4:02you too want to keep yourself safer when
- 4:04browsing the web, downloading files, and
- 4:05using public internet, you can use the
- 4:07link in my description or just go to
- 4:08nordvpn.com/jbicks
- 4:11to get your plan of NordVPN completely
- 4:13risk-free thanks to Nord's 30-day money
- 4:15back guarantee. And if you get a 2-year
- 4:17plan through my link, Nord even throws
- 4:19an extra 4 months on top.
- 4:21Okay, so for now, our terrain looks like
- 4:23this. broken down to one dimension, the
- 4:25terrain is represented by this simple
- 4:27linear function. If we replace our flat
- 4:29line with a function that returns a
- 4:30random value for each position on the
- 4:32x-axis, we would get something like
- 4:34this. And while this already has some
- 4:36elevation, I think everybody can tell
- 4:38that this is some horrible terrain for
- 4:39most games. The change in height are
- 4:41just way too drastic and unpredictable.
- 4:43So, we need a way to change the terrain
- 4:44height gradually. A sign curve, for
- 4:46example, would do exactly that. Problem
- 4:48is that it just repeats itself every
- 4:50time. So, we are missing the organic
- 4:53randomness. This is where pearl and
- 4:54noise comes into play. Pearl and noise
- 4:56is a mathematical function that returns
- 4:58absoid random value between a set
- 4:59minimum and a set maximum which in my
- 5:02case is zero and one with the catch that
- 5:04instead of jumping harshly like the
- 5:06function before it smoothly transitions
- 5:08between them like the sign curve.
- 5:09Applying this to our terrain already
- 5:11makes it look much better. But this is
- 5:12still far away from ideal and feels very
- 5:15artificial still. To solve that, we
- 5:17apply a little trick called octaves. I
- 5:19have a video on my channel where I
- 5:20explain pearl and noise octaves and
- 5:21other things in depth and how I use them
- 5:23to generate biomes, vegetation, etc. So,
- 5:26if you want to know more about that,
- 5:27check it out. But for the sake of
- 5:28simplicity and time, I'm going to skip
- 5:30that. With the octaves applied to the
- 5:32function, you can see that the overall
- 5:33function is still contained between 0
- 5:35and 1, but has much more detail and
- 5:37looks more organic. This already makes
- 5:39the terrain 10 times more natural
- 5:40looking. However, this still becomes
- 5:42very repetitive and does not feel very
- 5:44spectacular. For now, we've just been
- 5:46applying the value of our noise function
- 5:47to the base height level, which was zero
- 5:49or sea level. To create some interesting
- 5:51steep terrain changes, we can change
- 5:53this default terrain height that we
- 5:54apply the noise function to. For that, I
- 5:56create a second noise function and a
- 5:58height profile like this one, which
- 6:00tells the program what the terrain's
- 6:01default height is based on the second
- 6:03noise function. So, for example, if a
- 6:05noise value of the second noise function
- 6:07is between 0 and 0.25, the default
- 6:09terrain height is minus 10. And if it is
- 6:11between 0.25 and 0.85, 85 it is five.
- 6:15However, when the second noise function
- 6:16is between 0.58 and 0.6, the height
- 6:20increases again to 25. If we use this on
- 6:22our terrain alone, you can see where the
- 6:25default elevation changes, which results
- 6:26in some very nice looking cliffs. Of
- 6:29course, this height profile can be
- 6:30adjusted as liked to generate all sorts
- 6:32of terrain and mountains. Now if we
- 6:34apply our first noise function that we
- 6:35used for smaller height variations onto
- 6:37the default terrain height given by the
- 6:39height profile we get a beautiful and
- 6:41naturallooking terrain with small
- 6:43details large cliffs and mountains. On
- 6:45top of that we can now say that each air
- 6:47block that is below the level of zero
- 6:49becomes water which gives us some nice
- 6:51and natural looking lakes and oceans. So
- 6:53now that we know how to generate the
- 6:55terrain, it's time to implement it into
- 6:56the game. Well, this is actually where
- 6:58things get tricky. The first thing you
- 6:59might already have noticed is that it's
- 7:01very hard to tell where a certain layer
- 7:03ends and where a new one begins. This is
- 7:05due to the fact that the game, being a
- 7:062D game, uses an autographic camera.
- 7:09Normal cameras, like in 3D games or in
- 7:11real life, have a field of view that is
- 7:12a cone, meaning that it expands the
- 7:14further it gets. This has the effect
- 7:16that objects that are further away
- 7:17appear smaller. An autographic camera,
- 7:19however, has a straight view field that
- 7:21does not change based on distance. This
- 7:23means that a sprite that is further away
- 7:25appears the same size as any other
- 7:26sprites that are closer. that makes it
- 7:28super hard to tell what is far away and
- 7:30what is close without some additional
- 7:31details and tricks. To combat that
- 7:33problem a bit, I first added some
- 7:34outlines to the edges when the terrain
- 7:36drops in elevation. This works to a
- 7:38degree, but still not well. You can see
- 7:39that there's an edge, but it's not
- 7:41possible to tell how far down it goes.
- 7:43And this is a problem I'm still trying
- 7:45to solve. If you have any ideas on how
- 7:46to solve this, feel free to leave them
- 7:48in the comments. By the way, if you like
- 7:50the project and want to support it,
- 7:51please consider wishlisting Isocore on
- 7:53Steam. And if you want to become a play
- 7:55tester and have the possibility to
- 7:56already play the current version and
- 7:58give feedback on it, consider joining my
- 7:59Patreon. Both links are in the
- 8:01description. The most difficult problem
- 8:03that I faced was layering. You might
- 8:05remember the sorting order that I was
- 8:06talking about earlier. Of course, the
- 8:08game doesn't know automatically where
- 8:10each object in the world is, which means
- 8:12that I have to tell the program this
- 8:13information. And at first sight, this
- 8:15might seem very simple, but sorting a 3D
- 8:17world based on a singular value is
- 8:19actually much harder than I thought. In
- 8:20a world that only consists of blocks,
- 8:22this is fairly easy. I already figured
- 8:24out a simple way to implement it for the
- 8:25flat to the world before. To calculate
- 8:27the sorting order, you basically only
- 8:28had to calculate the horizontal row the
- 8:30object is in, which is simply just
- 8:32calculated by adding the cells x and y
- 8:34values. If you do that for each cell on
- 8:36a grid, you can see that it gives you
- 8:38horizontal rows that grow in value the
- 8:40further you go up on the y-axis. But we
- 8:42have to draw objects that are here in
- 8:44front of objects that are here. So we
- 8:46just multiply the sum of both values
- 8:48with minus1, which just mirrors all
- 8:50values on the x-axis. Now the objects
- 8:53are correctly sorted on a 2D plane. Now
- 8:55when considering the third dimension, we
- 8:57could just add the height value to the
- 8:59calculation, giving us a working
- 9:01function that correctly sorts blocks in
- 9:03a 3D grid. Problem is just that not all
- 9:05objects are blocks. Buildings and
- 9:07vegetation are often multiple blocks
- 9:08high or wide. My first approach for this
- 9:11was just creating the sum of all the
- 9:12objects occupied cells sorting orders
- 9:14and then just taking the average. This
- 9:16actually works for smaller objects, but
- 9:18it breaks as soon as an object has a
- 9:20diameter larger than two into any
- 9:22direction. I tried around to find a
- 9:24mathematical solution for this, but
- 9:25failed miserably since it seems to be
- 9:27pretty much impossible. But after some
- 9:29time, I ended up with a solution. By
- 9:31adding a weight in the form of a factor
- 9:33to the y-axis, I could make that axis
- 9:35the dominant component for sorting.
- 9:37Inside of a layer, the object gets
- 9:38sorted by the median sorting order of
- 9:40all cells in that layer. But now I still
- 9:42had to take the y-axis into
- 9:44consideration, which is done by slicing
- 9:46each object that is taller than one
- 9:47block into horizontal slices during
- 9:49runtime and then giving each slice the
- 9:52corresponding base sorting order with
- 9:54the weighted Y layer sorting order
- 9:55added. That way I'm able to sort 3D
- 9:58objects that are larger than two blocks
- 10:00based on only one singular value. Great.
- 10:03So everything runs, right?
- 10:04Unfortunately, sorting hell is not over
- 10:06yet. When coding this, I first just used
- 10:09multiple tile maps and layered them to
- 10:10create 3D terrain. This creates the very
- 10:13simple issue that all blocks in a tile
- 10:15map can only have the same sorting order
- 10:17since they are all part of the same mesh
- 10:19which gets rendered together. This is no
- 10:21problem when only sorting blocks since
- 10:23Unity does the sorting inside of a tile
- 10:26map for you. But as soon as you have
- 10:27objects outside the tile map, this
- 10:29becomes a problem. For example, these
- 10:31two blocks are on the same layer in the
- 10:33world and by that part of the same tile
- 10:35map. This means that they share the same
- 10:37sorting order. But that means that the
- 10:38player, for example, can only get
- 10:40rendered in front or behind, but never
- 10:42between the two blocks since they get
- 10:44drawn at the same time. So, I had to
- 10:46write my own custom time-lap system that
- 10:47allows me to give each block an
- 10:49individual sorting order. Now, the
- 10:50layering was finally working and I could
- 10:52move on to something else. The rest of
- 10:54this update was actually just rewriting
- 10:56and redesigning all of the existing
- 10:57mechanics that relied on the old 2D
- 10:59logic. Of course, that means that the
- 11:01new version now includes a lot of
- 11:02untested code and will probably come
- 11:05with a large selection of new bugs for
- 11:06the play testers to discover. I tried to
- 11:08get everything up and running before
- 11:10this video, but unfortunately, I wasn't
- 11:12able to implement all the old mechanics
- 11:13into the new version just yet. So, a few
- 11:15smaller things like grid line strength,
- 11:17customization, slimes, and ruins are
- 11:19still currently missing. But we'll be
- 11:21following in the next update a couple of
- 11:22days after this video. So, after all
- 11:24this time and countless requests, the
- 11:26game finally has elevation. It still has
- 11:29some smaller issues like the depth
- 11:30perception issue that I will be trying
- 11:32to solve in a later update. At first, I
- 11:34was a bit afraid that the elevation
- 11:36would feel weird due to the game's
- 11:37perspective and 2D nature. However, I
- 11:40think that people were right and that
- 11:41the elevation gives the game a lot of
- 11:43gameplay value and new potential. Of
- 11:45course, it's going to still take a
- 11:46couple of updates until everything feels
- 11:48smoothly and polished. I know that I
- 11:50don't upload very often, but between the
- 11:52videos on my channel, there is actually
- 11:53a lot happening behind the scenes and
- 11:55also new versions being released to the
- 11:57play testers. If you want to stay up to
- 11:58date, vote on new features, or just hang
- 12:00out with the community, join the ISO
- 12:02Discord server. The link is in the
- 12:03description. Thanks so much for
- 12:05watching, and I see you in the next one.
About this transcript
This page contains the full transcript of Adding 3D Terrain to my 2D Game [DEVLOG] by JohnBrx, generated from the public captions YouTube serves with the video. The transcript has 2,760 words across 398 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.
What you can do with it
Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.
Free YouTube transcript tool
YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.