YouTube2Text

Adding 3D Terrain to my 2D Game [DEVLOG] — Transcript

by JohnBrx · 2,760 words · 398 segments · language en · Watch on YouTube

Full transcript

  1. 0:00For over 2 years now, I've been working
  2. 0:01on my indie project, Isoccor. And after
  3. 0:03all this time, I finally decided to
  4. 0:05tackle the biggest challenge I've ever
  5. 0:07faced in gamedev. For those of you who
  6. 0:08are new here, first of all, hi and
  7. 0:10thanks for stopping by. Isoore is a 2D
  8. 0:13isometric open world sandbox game
  9. 0:14focused on exploration, farming,
  10. 0:16crafting, and building. And if you've
  11. 0:18seen any of my earlier videos, you've
  12. 0:20probably noticed that the world has
  13. 0:21always been completely flat. Well, not
  14. 0:23anymore. Ever since I started this
  15. 0:25project, there's been one request that
  16. 0:27I've gotten over and over again. So, it
  17. 0:29seems like people really want some
  18. 0:31elevation in the game. For most of you,
  19. 0:32it also might seem like a logical step
  20. 0:34in the development of this project, but
  21. 0:36it's not like I didn't have that idea
  22. 0:37already. The truth is, until now, I've
  23. 0:39actually tried avoiding it for a long
  24. 0:41time. But why did it take me so long to
  25. 0:43start putting some elevation into the
  26. 0:44game, you might ask yourself, and there
  27. 0:46are multiple reasons, which you're going
  28. 0:47to see later on in this video. But the
  29. 0:49request for this feature has been so
  30. 0:50incredibly large and consistent that I
  31. 0:52finally decided to give it a try. When
  32. 0:54you just look at the game without
  33. 0:55knowing what's happening under the hood,
  34. 0:56it might seem pretty straightforward to
  35. 0:58add some elevation. Just add some more
  36. 1:00blocks in a few places and you're done,
  37. 1:02right? Well, not quite. Isoore isn't a
  38. 1:053D game. It's a 2D pixel art game, which
  39. 1:08means that all sprites are completely
  40. 1:10two-dimensional and the entire game
  41. 1:11takes place on a flat 2D canvas. Every
  42. 1:14sprite has a 2D position on that canvas
  43. 1:16and a value called a sorting order. That
  44. 1:18sorting order acts as the third
  45. 1:20dimension in a 2D game, but it's not a
  46. 1:22real dimension. It simply tells the
  47. 1:24program in what order to draw the
  48. 1:25sprites so that the correct ones appear
  49. 1:27on top and the other ones below. So that
  50. 1:29value isn't useful for any actual 3D
  51. 1:32logic. However, to have a dynamic 3D
  52. 1:34world, you need to know exactly where
  53. 1:35each object is on all three axes so you
  54. 1:37can calculate physics, collisions, and
  55. 1:39other interactions correctly. That means
  56. 1:41that every object now needs two
  57. 1:42positions. An internal 3D position
  58. 1:45inside the world and the 2D canvas
  59. 1:47position you end up seeing on the
  60. 1:48screen. So to implement elevation into a
  61. 1:502D game, I had to completely separate
  62. 1:52the game's internal functionality and
  63. 1:54calculations from its visual front end.
  64. 1:56Up until now, I could just use the 2D
  65. 1:58canvas position for everything.
  66. 2:00Movement, collision, rendering, all of
  67. 2:01[music] it. But with how large this
  68. 2:03project has grown over time, doing the
  69. 2:05separation was a massive task. Still,
  70. 2:08there was no way around it. So I sat
  71. 2:10down and got to work. I also used the
  72. 2:11opportunity, since I had to touch most
  73. 2:13of the game's core [music] systems
  74. 2:14anyway, to refactor some old code that
  75. 2:16was, let's just say, pretty bad and
  76. 2:19definitely overdue for a rewrite. After
  77. 2:21a good amount of time, I finally managed
  78. 2:22to separate all the internal mechanics
  79. 2:24from the front end. Now, every object is
  80. 2:26positioned internally using a 3D vector
  81. 2:28inside the terrain. When drawn on
  82. 2:30screen, that position gets passed
  83. 2:31through a few functions that calculate
  84. 2:33the correct canvas position and sorting
  85. 2:34order. With my game now using actual 3D
  86. 2:37positions to calculate everything, I
  87. 2:38could finally start working on the
  88. 2:40actual elevation feature. As I mentioned
  89. 2:42in the last video, I've been putting a
  90. 2:43lot more time and effort into this
  91. 2:44project the last couple of months. But
  92. 2:46while going to university, I end up
  93. 2:48working a lot from campus, libraries,
  94. 2:49and cafes. So, there is no real way
  95. 2:52around public Wi-Fi for me a lot of
  96. 2:53times, which can become a real security
  97. 2:55issue since the last thing you want is
  98. 2:56someone snooping on your connection or
  99. 2:58stealing your data by inserting
  100. 2:59themselves between your device and the
  101. 3:01router. But thankfully, I didn't have to
  102. 3:03worry about that for the last couple of
  103. 3:04months, thanks to this video's partner,
  104. 3:06NordVPN. NordVPN is a VPN provider that
  105. 3:08encrypts your internet traffic so that
  106. 3:10your data stays safe no matter where you
  107. 3:12connected. At the same time, NordVPN is
  108. 3:14also able to recognize malicious links
  109. 3:15and warns you about dangerous websites,
  110. 3:17helping to keep you safe from fishing
  111. 3:19attacks. Also, nowadays, there's almost
  112. 3:21nothing you can do online without having
  113. 3:22to create an account first, even for the
  114. 3:24most rudimentary things. And thinking of
  115. 3:26a new and complex password each time
  116. 3:28this happens is super tedious. And let's
  117. 3:30be honest, I'm pretty sure that most
  118. 3:32people don't do that. But using the same
  119. 3:33password over and over again is a huge
  120. 3:35security risk. All it takes is some
  121. 3:37company you gave your data to years ago
  122. 3:39and don't even remember to have a data
  123. 3:41leak, and your email, phone number, and
  124. 3:42connected password will be swimming
  125. 3:44around the internet for others to try
  126. 3:45them on your other accounts. But with
  127. 3:47NordVPN's password manager, NordPass,
  128. 3:49which comes included in the Plus plan,
  129. 3:50this is no longer an issue. It keeps
  130. 3:52your password safe and makes it super
  131. 3:53easy to have highly complex and secure
  132. 3:55passwords for each and every account.
  133. 3:57Plus, it makes the login process much
  134. 3:59faster since you no longer need to
  135. 4:00remember them all or look them up. If
  136. 4:02you too want to keep yourself safer when
  137. 4:04browsing the web, downloading files, and
  138. 4:05using public internet, you can use the
  139. 4:07link in my description or just go to
  140. 4:08nordvpn.com/jbicks
  141. 4:11to get your plan of NordVPN completely
  142. 4:13risk-free thanks to Nord's 30-day money
  143. 4:15back guarantee. And if you get a 2-year
  144. 4:17plan through my link, Nord even throws
  145. 4:19an extra 4 months on top.
  146. 4:21Okay, so for now, our terrain looks like
  147. 4:23this. broken down to one dimension, the
  148. 4:25terrain is represented by this simple
  149. 4:27linear function. If we replace our flat
  150. 4:29line with a function that returns a
  151. 4:30random value for each position on the
  152. 4:32x-axis, we would get something like
  153. 4:34this. And while this already has some
  154. 4:36elevation, I think everybody can tell
  155. 4:38that this is some horrible terrain for
  156. 4:39most games. The change in height are
  157. 4:41just way too drastic and unpredictable.
  158. 4:43So, we need a way to change the terrain
  159. 4:44height gradually. A sign curve, for
  160. 4:46example, would do exactly that. Problem
  161. 4:48is that it just repeats itself every
  162. 4:50time. So, we are missing the organic
  163. 4:53randomness. This is where pearl and
  164. 4:54noise comes into play. Pearl and noise
  165. 4:56is a mathematical function that returns
  166. 4:58absoid random value between a set
  167. 4:59minimum and a set maximum which in my
  168. 5:02case is zero and one with the catch that
  169. 5:04instead of jumping harshly like the
  170. 5:06function before it smoothly transitions
  171. 5:08between them like the sign curve.
  172. 5:09Applying this to our terrain already
  173. 5:11makes it look much better. But this is
  174. 5:12still far away from ideal and feels very
  175. 5:15artificial still. To solve that, we
  176. 5:17apply a little trick called octaves. I
  177. 5:19have a video on my channel where I
  178. 5:20explain pearl and noise octaves and
  179. 5:21other things in depth and how I use them
  180. 5:23to generate biomes, vegetation, etc. So,
  181. 5:26if you want to know more about that,
  182. 5:27check it out. But for the sake of
  183. 5:28simplicity and time, I'm going to skip
  184. 5:30that. With the octaves applied to the
  185. 5:32function, you can see that the overall
  186. 5:33function is still contained between 0
  187. 5:35and 1, but has much more detail and
  188. 5:37looks more organic. This already makes
  189. 5:39the terrain 10 times more natural
  190. 5:40looking. However, this still becomes
  191. 5:42very repetitive and does not feel very
  192. 5:44spectacular. For now, we've just been
  193. 5:46applying the value of our noise function
  194. 5:47to the base height level, which was zero
  195. 5:49or sea level. To create some interesting
  196. 5:51steep terrain changes, we can change
  197. 5:53this default terrain height that we
  198. 5:54apply the noise function to. For that, I
  199. 5:56create a second noise function and a
  200. 5:58height profile like this one, which
  201. 6:00tells the program what the terrain's
  202. 6:01default height is based on the second
  203. 6:03noise function. So, for example, if a
  204. 6:05noise value of the second noise function
  205. 6:07is between 0 and 0.25, the default
  206. 6:09terrain height is minus 10. And if it is
  207. 6:11between 0.25 and 0.85, 85 it is five.
  208. 6:15However, when the second noise function
  209. 6:16is between 0.58 and 0.6, the height
  210. 6:20increases again to 25. If we use this on
  211. 6:22our terrain alone, you can see where the
  212. 6:25default elevation changes, which results
  213. 6:26in some very nice looking cliffs. Of
  214. 6:29course, this height profile can be
  215. 6:30adjusted as liked to generate all sorts
  216. 6:32of terrain and mountains. Now if we
  217. 6:34apply our first noise function that we
  218. 6:35used for smaller height variations onto
  219. 6:37the default terrain height given by the
  220. 6:39height profile we get a beautiful and
  221. 6:41naturallooking terrain with small
  222. 6:43details large cliffs and mountains. On
  223. 6:45top of that we can now say that each air
  224. 6:47block that is below the level of zero
  225. 6:49becomes water which gives us some nice
  226. 6:51and natural looking lakes and oceans. So
  227. 6:53now that we know how to generate the
  228. 6:55terrain, it's time to implement it into
  229. 6:56the game. Well, this is actually where
  230. 6:58things get tricky. The first thing you
  231. 6:59might already have noticed is that it's
  232. 7:01very hard to tell where a certain layer
  233. 7:03ends and where a new one begins. This is
  234. 7:05due to the fact that the game, being a
  235. 7:062D game, uses an autographic camera.
  236. 7:09Normal cameras, like in 3D games or in
  237. 7:11real life, have a field of view that is
  238. 7:12a cone, meaning that it expands the
  239. 7:14further it gets. This has the effect
  240. 7:16that objects that are further away
  241. 7:17appear smaller. An autographic camera,
  242. 7:19however, has a straight view field that
  243. 7:21does not change based on distance. This
  244. 7:23means that a sprite that is further away
  245. 7:25appears the same size as any other
  246. 7:26sprites that are closer. that makes it
  247. 7:28super hard to tell what is far away and
  248. 7:30what is close without some additional
  249. 7:31details and tricks. To combat that
  250. 7:33problem a bit, I first added some
  251. 7:34outlines to the edges when the terrain
  252. 7:36drops in elevation. This works to a
  253. 7:38degree, but still not well. You can see
  254. 7:39that there's an edge, but it's not
  255. 7:41possible to tell how far down it goes.
  256. 7:43And this is a problem I'm still trying
  257. 7:45to solve. If you have any ideas on how
  258. 7:46to solve this, feel free to leave them
  259. 7:48in the comments. By the way, if you like
  260. 7:50the project and want to support it,
  261. 7:51please consider wishlisting Isocore on
  262. 7:53Steam. And if you want to become a play
  263. 7:55tester and have the possibility to
  264. 7:56already play the current version and
  265. 7:58give feedback on it, consider joining my
  266. 7:59Patreon. Both links are in the
  267. 8:01description. The most difficult problem
  268. 8:03that I faced was layering. You might
  269. 8:05remember the sorting order that I was
  270. 8:06talking about earlier. Of course, the
  271. 8:08game doesn't know automatically where
  272. 8:10each object in the world is, which means
  273. 8:12that I have to tell the program this
  274. 8:13information. And at first sight, this
  275. 8:15might seem very simple, but sorting a 3D
  276. 8:17world based on a singular value is
  277. 8:19actually much harder than I thought. In
  278. 8:20a world that only consists of blocks,
  279. 8:22this is fairly easy. I already figured
  280. 8:24out a simple way to implement it for the
  281. 8:25flat to the world before. To calculate
  282. 8:27the sorting order, you basically only
  283. 8:28had to calculate the horizontal row the
  284. 8:30object is in, which is simply just
  285. 8:32calculated by adding the cells x and y
  286. 8:34values. If you do that for each cell on
  287. 8:36a grid, you can see that it gives you
  288. 8:38horizontal rows that grow in value the
  289. 8:40further you go up on the y-axis. But we
  290. 8:42have to draw objects that are here in
  291. 8:44front of objects that are here. So we
  292. 8:46just multiply the sum of both values
  293. 8:48with minus1, which just mirrors all
  294. 8:50values on the x-axis. Now the objects
  295. 8:53are correctly sorted on a 2D plane. Now
  296. 8:55when considering the third dimension, we
  297. 8:57could just add the height value to the
  298. 8:59calculation, giving us a working
  299. 9:01function that correctly sorts blocks in
  300. 9:03a 3D grid. Problem is just that not all
  301. 9:05objects are blocks. Buildings and
  302. 9:07vegetation are often multiple blocks
  303. 9:08high or wide. My first approach for this
  304. 9:11was just creating the sum of all the
  305. 9:12objects occupied cells sorting orders
  306. 9:14and then just taking the average. This
  307. 9:16actually works for smaller objects, but
  308. 9:18it breaks as soon as an object has a
  309. 9:20diameter larger than two into any
  310. 9:22direction. I tried around to find a
  311. 9:24mathematical solution for this, but
  312. 9:25failed miserably since it seems to be
  313. 9:27pretty much impossible. But after some
  314. 9:29time, I ended up with a solution. By
  315. 9:31adding a weight in the form of a factor
  316. 9:33to the y-axis, I could make that axis
  317. 9:35the dominant component for sorting.
  318. 9:37Inside of a layer, the object gets
  319. 9:38sorted by the median sorting order of
  320. 9:40all cells in that layer. But now I still
  321. 9:42had to take the y-axis into
  322. 9:44consideration, which is done by slicing
  323. 9:46each object that is taller than one
  324. 9:47block into horizontal slices during
  325. 9:49runtime and then giving each slice the
  326. 9:52corresponding base sorting order with
  327. 9:54the weighted Y layer sorting order
  328. 9:55added. That way I'm able to sort 3D
  329. 9:58objects that are larger than two blocks
  330. 10:00based on only one singular value. Great.
  331. 10:03So everything runs, right?
  332. 10:04Unfortunately, sorting hell is not over
  333. 10:06yet. When coding this, I first just used
  334. 10:09multiple tile maps and layered them to
  335. 10:10create 3D terrain. This creates the very
  336. 10:13simple issue that all blocks in a tile
  337. 10:15map can only have the same sorting order
  338. 10:17since they are all part of the same mesh
  339. 10:19which gets rendered together. This is no
  340. 10:21problem when only sorting blocks since
  341. 10:23Unity does the sorting inside of a tile
  342. 10:26map for you. But as soon as you have
  343. 10:27objects outside the tile map, this
  344. 10:29becomes a problem. For example, these
  345. 10:31two blocks are on the same layer in the
  346. 10:33world and by that part of the same tile
  347. 10:35map. This means that they share the same
  348. 10:37sorting order. But that means that the
  349. 10:38player, for example, can only get
  350. 10:40rendered in front or behind, but never
  351. 10:42between the two blocks since they get
  352. 10:44drawn at the same time. So, I had to
  353. 10:46write my own custom time-lap system that
  354. 10:47allows me to give each block an
  355. 10:49individual sorting order. Now, the
  356. 10:50layering was finally working and I could
  357. 10:52move on to something else. The rest of
  358. 10:54this update was actually just rewriting
  359. 10:56and redesigning all of the existing
  360. 10:57mechanics that relied on the old 2D
  361. 10:59logic. Of course, that means that the
  362. 11:01new version now includes a lot of
  363. 11:02untested code and will probably come
  364. 11:05with a large selection of new bugs for
  365. 11:06the play testers to discover. I tried to
  366. 11:08get everything up and running before
  367. 11:10this video, but unfortunately, I wasn't
  368. 11:12able to implement all the old mechanics
  369. 11:13into the new version just yet. So, a few
  370. 11:15smaller things like grid line strength,
  371. 11:17customization, slimes, and ruins are
  372. 11:19still currently missing. But we'll be
  373. 11:21following in the next update a couple of
  374. 11:22days after this video. So, after all
  375. 11:24this time and countless requests, the
  376. 11:26game finally has elevation. It still has
  377. 11:29some smaller issues like the depth
  378. 11:30perception issue that I will be trying
  379. 11:32to solve in a later update. At first, I
  380. 11:34was a bit afraid that the elevation
  381. 11:36would feel weird due to the game's
  382. 11:37perspective and 2D nature. However, I
  383. 11:40think that people were right and that
  384. 11:41the elevation gives the game a lot of
  385. 11:43gameplay value and new potential. Of
  386. 11:45course, it's going to still take a
  387. 11:46couple of updates until everything feels
  388. 11:48smoothly and polished. I know that I
  389. 11:50don't upload very often, but between the
  390. 11:52videos on my channel, there is actually
  391. 11:53a lot happening behind the scenes and
  392. 11:55also new versions being released to the
  393. 11:57play testers. If you want to stay up to
  394. 11:58date, vote on new features, or just hang
  395. 12:00out with the community, join the ISO
  396. 12:02Discord server. The link is in the
  397. 12:03description. Thanks so much for
  398. 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.