{"id":50786,"date":"2020-09-22T06:00:49","date_gmt":"2020-09-22T10:00:49","guid":{"rendered":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=50786"},"modified":"2020-09-22T07:06:27","modified_gmt":"2020-09-22T11:06:27","slug":"project-bug-hunt-3-the-doors","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=50786","title":{"rendered":"Project Bug Hunt #3: The Doors"},"content":{"rendered":"<p>A point of order: In the last entry I talked about deforming the walls to give them some interest. In my final example screenshot, I showed an image of hallways that bulged out in the middle. I didn&#8217;t make it totally clear how I planned to use this, and as a result some people were left with the impression that my plan was to have bare bulbous walls everywhere.<\/p>\n<p>The bulbous walls were just a minimum-effort demonstration of the idea. My plan is to have the shape of the wall vary by room type. Maybe the corridors will be wide but short, like the ship in Alien. Maybe some walls will be very narrow and tall like in the first level of System Shock 2. Whatever.<\/p>\n<p>There was also the concern that bare walls would get boring, and I should add greebles. I&#8217;m currently planning on using the &#8220;furniture&#8221; idea for this. Something like this image from System Shock 2:<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_greebles1.jpg' width=100% alt='I&apos;m worried these greebles aren&apos;t greebly enough, but we&apos;ll see.' title='I&apos;m worried these greebles aren&apos;t greebly enough, but we&apos;ll see.'\/><\/div><div class='mouseover-alt'>I&apos;m worried these greebles aren&apos;t greebly enough, but we&apos;ll see.<\/div><\/p>\n<p>If that doesn&#8217;t work out, then maybe I&#8217;ll back up and try something else. But let&#8217;s try this easy thing first.<\/p>\n<p><!--more--><\/p>\n<p>With that cleared up, let&#8217;s talk about&#8230;<\/p>\n<h3>The Doors<\/h3>\n<blockquote><p>There are things known and things unknown and in between are the doors.<\/p>\n<p>&#8211; Jim Morrison<\/p><\/blockquote>\n<p>No, not <a href=\"https:\/\/en.wikipedia.org\/wiki\/The_Doors\">that<\/a> kind of Doors. I mean doors between rooms.<\/p>\n<p>If you remember from last time, a room is made up of line segments. Each segment is based on the grid. You can grab an arbitrary line and follow it to the next one, and the next one, and it will take you around the room counter-clockwise<span class='snote' title='1'>Last entry I said clockwise. Wrong. I&#8217;ve lost track of the number of times I&#8217;ve gotten that mixed up and created a bug. My brain just really WANTS it to be clockwise I guess.<\/span> until you go all the way around and land back where you started.<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_door1.png' width=100% alt='' title=''\/><\/div><div class='mouseover-alt'><\/div><\/p>\n<p>Note: If we&#8217;re dealing with an interior support pillar, then the lines go around clockwise. That&#8217;s not something I programmed, that&#8217;s just a natural result of how the system works. In both cases, you can take the direction the line is pointing, turn left, and be pointing directly into the room.<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_door2.png' width=100% alt='The red arrows point directly away from the walls. They&apos;re called surface normals. They&apos;re useful in helping me shape the walls, but they&apos;re also required for properly rendering and lighting the walls.' title='The red arrows point directly away from the walls. They&apos;re called surface normals. They&apos;re useful in helping me shape the walls, but they&apos;re also required for properly rendering and lighting the walls.'\/><\/div><div class='mouseover-alt'>The red arrows point directly away from the walls. They&apos;re called surface normals. They&apos;re useful in helping me shape the walls, but they&apos;re also required for properly rendering and lighting the walls.<\/div><\/p>\n<p>Internally, I call these lines of wall segments &#8220;chains&#8221;. So if you&#8217;ve got a room with a lone support pillar in the middle, then the room will have two chains. One chain will be the outer wall, and the other chain will be the outside of the pillar.<\/p>\n<p>So now we need to create doors. That&#8217;s not as simple as blowing random holes in the walls. To create a door we need to do a lot of checks:<\/p>\n<p>1) Obviously, we&#8217;re only looking at the chains that make up the outer walls. There&#8217;s nothing to be gained by attempting to put a doorway to access the void space inside a pillar. You&#8217;d just fall out of the level.<\/p>\n<p>2) We need to find a straight section of lines that&#8217;s at least 3 segments long. The marching squares system we&#8217;re using will create walls that can face any of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cardinal_direction\">four cardinal directions, or the four ordinal directions<\/a>. You can picture a simple &#8220;round&#8221; room as being shaped like a STOP sign. Any of those eight sides are fine, but we want to avoid putting a door directly touching one of the corners because we&#8217;re going to need to blend a door frame object into the level geometry and if we do that at a corner then we&#8217;ll get messy intersections and it won&#8217;t look right.<\/p>\n<p>In practical terms, to be a viable candidate for a wall, we&#8217;re looking for a line segment that&#8217;s the same shape as the one directly before it and the one directly after it. The ones in green are valid here:<\/p>\n<p><img decoding=\"async\" src=\"images\/bughunt_door4.png\" \/><\/p>\n<p>3) We&#8217;re going to assume that the main corridor is how we&#8217;ll move from one section of the level to the next. So any side-rooms MUST have access to the main corridor. If rooms A and B only connect to each other, then the player won&#8217;t be able to enter them. So we&#8217;re going to place doorways from the perspective of the side-rooms. The corridor will not attempt to make any doorways for itself. Instead the rooms will search around their outer walls, looking for a connection to the main corridor, or access to rooms that have corridor access.<\/p>\n<p>4) Once a room finds a straight section of wall, it needs to look on the opposite side of the wall to make sure the wall is also straight on the flip side.<\/p>\n<p>5) Once we&#8217;re happy with a spot, we create a door. This door punches holes in the two rooms, enabling one of them to gain corridor access from the other.<\/p>\n<p>We pass over the list of rooms again and again, until everyone gains corridor access or the process halts. If we go all the way through the list of rooms and nothing changes, then we know we&#8217;ve created all the doors we can. If we get to the end of this process and discover that we <i>still<\/i> somehow have rooms with no access, we can just throw them out and leave their assigned space empty.<\/p>\n<p>One final thing we do is to go over all the chains and do a bit of rounding.<\/p>\n<h3>Rounding<\/h3>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_door3.png' width=100% alt='' title=''\/><\/div><div class='mouseover-alt'><\/div><\/p>\n<p>You&#8217;ll notice that point A (Fig.1) is on the edge of two walls that face different directions. For one wall A is the beginning, and for the previous wall A is the end. To allow them to share this point without leaving a crack \/ distortion in the wall, we average their normals (Fig.2) so that A is midway between both.<\/p>\n<p>That&#8217;s a good start, but we can take this a step further if we want to round the walls even more. Let&#8217;s take a few lines between the neighbors of A (the green lines in Fig.3) and turn those lines left to get even more normals. Average everything together. If you have many walls facing the same way (such as all of the line segments on the far left of A in Fig.1) then they&#8217;ll all face the same direction, which would be south in this case. But as you approach the corner, the points will begin to gradually curve. This allows us to create a wall with gently sloping curves, as in Fig.4. (The amount of curve is exaggerated here for demonstration purposes.)<\/p>\n<p>And here is what the level looks like:<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_door8.jpg' width=100% alt='It wasn&apos;t until I took this screenshot that I realized the rounding is perhaps TOO subtle. I should turn up the effect and see how it looks.' title='It wasn&apos;t until I took this screenshot that I realized the rounding is perhaps TOO subtle. I should turn up the effect and see how it looks.'\/><\/div><div class='mouseover-alt'>It wasn&apos;t until I took this screenshot that I realized the rounding is perhaps TOO subtle. I should turn up the effect and see how it looks.<\/div><\/p>\n<p>Next I guess we&#8217;d better add some floors and ceilings so this can start feeling like a proper level.<\/p>\n<h3>I Forgot What I was Doing<\/h3>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/three_weeks_later.jpg' width=100% alt='' title=''\/><\/div><div class='mouseover-alt'><\/div><\/p>\n<p>At this point in the project, I had to set it aside for a couple of weeks to finish up <a href=\"?p=50501\">my SWJFO series<\/a> and do some general blog maintenance<span class='snote' title='2'>Did anyone notice that I added a few dozen new entries to the &#8220;From the Archives&#8221; thing at the end of every post? No? To be honest, me neither. It&#8217;s fine. Those things are mostly there for newer readers to get them to hang around and see a little more of the site before they wander off.<\/span>. When I came back, I&#8217;d forgotten a lot of fundamental things.<\/p>\n<p>I&#8217;m constantly amazed at how quickly you can forget this kind of stuff. I can still remember names, faces, quotable lines, and bits of music from movies I haven&#8217;t seen since the 90&#8217;s. But the structure of the code I wrote last week? That might as well have been written by someone else.<\/p>\n<p>Like I demonstrated above, the marching squares system will create divisions <strong>within<\/strong> a grid square. But when I got back, I was picturing it incorrectly in my head.<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_door5.png' width=100% alt='Marching squares should divide grid squares through the center.' title='Marching squares should divide grid squares through the center.'\/><\/div><div class='mouseover-alt'>Marching squares should divide grid squares through the center.<\/div><\/p>\n<p>Note how the blue arrows line up with the grid in the right-hand image. I expected straight sections of wall to be aligned like this, and I assumed that I&#8217;d just need to do something special to handle the spots where the wall travels diagonally.<\/p>\n<p>To make the floor. I just made a simple square for all parts of the grid that were inside a room. What alarmed me was that all along the walls, I&#8217;d see floor tiles from the neighboring room sticking into this one. The two rooms would have different floor textures, and I could see the ghastly <a href=\"https:\/\/en.wikipedia.org\/wiki\/Z-fighting\">z-fighting<\/a> between them.<\/p>\n<p>Because I&#8217;d forgotten what I was doing, I assumed this was a bug.<\/p>\n<p><em>Oops. Looks like my walls are off the grid by 0.5 and I didn&#8217;t notice. No problem. Easy to fix.<\/em><\/p>\n<p><strong>Spoiler: This did not fix it.<\/strong><\/p>\n<p><em>Um. Oh! I see. One room is reaching into its neighbor. I&#8217;ve probably got an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Off-by-one_error\">off-by-one<\/a> error somewhere.<\/em><\/p>\n<p><strong>No Shamus, there isn&#8217;t an off-by-one error.<\/strong><\/p>\n<p><em>Uh? What am I doing wrong? Am I making floor tiles too big? Are my walls the wrong size? Do I have a bug in the code I use to look up what rooms occupy which points on the grid?<\/em><\/p>\n<p><strong>No, no, and no.<\/strong><\/p>\n<p><em>Oh! The floor object itself has probably been nudged off- center within the Unity scene. Happens sometimes when you mis-click. I&#8217;ll make sure the objects are all positioned at the world origin.<\/em><\/p>\n<p><strong>C&#8217;mon dude. I thought you were smarter than this. Stop wasting our time.<\/strong><\/p>\n<p><em>I don&#8217;t get it. It&#8217;s like each room has half a tile that sticks into the next room, as if I needed to chop the tile in half.<\/em><\/p>\n<p><strong>You&#8217;re getting closer, dummy.<\/strong><\/p>\n<p><em>But marching squares&#8230; Oh wait.<\/em><\/p>\n<p><strong>YEAH. HOW &#8216;BOUT THOSE MARCHING SQUARES?<\/strong><\/p>\n<p>This isn&#8217;t a bug in the code. This is literally how marching squares work. They cut cells by dividing them in half or taking off a corner. By definition, EVERY tile around the edge of a room is going to need to be a partial tile.<\/p>\n<p>Once I get my head on straight, I stop and write some code specifically to handle those literal edge-cases. When I&#8217;m done&#8230;<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_door6.jpg' width=100% alt='We don&apos;t have door FRAMES yet, so around doors we have this gap in the level geometry. It&apos;ll be fixed soon.' title='We don&apos;t have door FRAMES yet, so around doors we have this gap in the level geometry. It&apos;ll be fixed soon.'\/><\/div><div class='mouseover-alt'>We don&apos;t have door FRAMES yet, so around doors we have this gap in the level geometry. It&apos;ll be fixed soon.<\/div><\/p>\n<p>You know, that looks kinda cool. It makes the floor a little more interesting to have a border like this. I think I&#8217;ll add a feature so that the artist can assign different textures to the edge and the main part of the floor.<\/p>\n<p>Once I have that working, it&#8217;s trivial to fill in the rest of the floor and then make the exact same shape for the ceiling. Then while I&#8217;m at it, I might as well slap some polygons inside the door frame. We&#8217;ll fill in the door properly once we have furniture, but I just want to avoid having a gap where you can see out of the level.<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_door7.jpg' width=100% alt='This is taken from the same vantage point as the screenshots in the previous entry. These pillars come from the black dots on the east side of the map. (See the next image if you don&apos;t remember.)' title='This is taken from the same vantage point as the screenshots in the previous entry. These pillars come from the black dots on the east side of the map. (See the next image if you don&apos;t remember.)'\/><\/div><div class='mouseover-alt'>This is taken from the same vantage point as the screenshots in the previous entry. These pillars come from the black dots on the east side of the map. (See the next image if you don&apos;t remember.)<\/div><\/p>\n<p>Okay. We now have a fully enclosed level. My system is working, and the spaces are interesting yet easy to generate from simple input data. The space can be navigated and I&#8217;m happy with the shape of things for now.<\/p>\n<p><div class='imagefull'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt_map7.png?' width=100% alt='Left: The hand-drawn 64x64 image that guides the program in building the map. Right: The resulting level layout.' title='Left: The hand-drawn 64x64 image that guides the program in building the map. Right: The resulting level layout.'\/><\/div><div class='mouseover-alt'>Left: The hand-drawn 64x64 image that guides the program in building the map. Right: The resulting level layout.<\/div><\/p>\n<p>We&#8217;re miles from being done, but I think this is a really important milestone.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A point of order: In the last entry I talked about deforming the walls to give them some interest. In my final example screenshot, I showed an image of hallways that bulged out in the middle. I didn&#8217;t make it totally clear how I planned to use this, and as a result some people were [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[66],"tags":[],"class_list":["post-50786","post","type-post","status-publish","format-standard","hentry","category-programming"],"_links":{"self":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/50786","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=50786"}],"version-history":[{"count":32,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/50786\/revisions"}],"predecessor-version":[{"id":50823,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/50786\/revisions\/50823"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=50786"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=50786"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=50786"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}