{"id":20823,"date":"2013-09-02T06:12:07","date_gmt":"2013-09-02T11:12:07","guid":{"rendered":"http:\/\/www.shamusyoung.com\/twentysidedtale\/?p=20823"},"modified":"2015-07-01T03:44:52","modified_gmt":"2015-07-01T08:44:52","slug":"project-good-robot-part-8-suggestion-box","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=20823","title":{"rendered":"Project Good Robot 8: Suggestion Box"},"content":{"rendered":"<p>As many of you requested, I&#8217;m not going to document every feature that goes into this thing. This means we&#8217;re going to jump ahead and start talking about stuff I&#8217;m dealing with now-ish. A side effect of this is that you&#8217;re going to see unexplained features appear and be tempted to ask, &#8220;But, what is this feature and how does it work?&#8221; Understand that the answer to that question would take a post. A post which would have precluded the writing of this one.  <\/p>\n<p>You can&#8217;t trick me into writing twice as much by asking me to write about current stuff and then asking me to fill in the blanks in the comments. Well, you CAN, but it&#8217;s going to make for a less interesting series for everyone. So just get yourself into some sort of zen state where you&#8217;re okay with seeing unexplained mystery content.<\/p>\n<p>So on Friday I talked about the line-of-sight system I came up with. A few people suggested a completely different way of doing it that would be many, many times faster. And now we come to the part of the project where we have to make the hard choices.<\/p>\n<p>See, there&#8217;s one thing I didn&#8217;t mention on Friday. That&#8217;s this:<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_fov.jpg' class='insetimage'   alt='gr8_fov.jpg' title='gr8_fov.jpg'\/><\/td><\/tr><\/table><\/p>\n<p><!--more-->The visibility system I devised had this side-effect where it was super-easy to implement this &#8220;flashlight mode&#8221;. Actually, &#8220;implement&#8221; is sort of overstating it.  It implicitly WAS flashlight mode, and I&#8217;ve just been setting the beam to be 360 degrees wide. <\/p>\n<p>It&#8217;s really cool, and you kind of need to see it in motion to appreciate just how suddenly panicky and claustrophobic the game feels like this. This was an accidental discovery while working on the LOS system, and it was so interesting I began toying with the idea of making it part of the game.<\/p>\n<p>(The foes depicted are these spinning sawblades that try to swarm you and chop you to bits. They look strange to me in freeze-frame like this, since in-game they&#8217;re always spinning.)<\/p>\n<p>On the other hand, it&#8217;s friggin&#8217; HARD. Hard enough that it changes the entire feel of the game. Foes are really good at approaching from all angles and in flashlight mode  you can&#8217;t dodge their attacks because you can&#8217;t see them coming. If I went this route I&#8217;d be leaving behind the fast-paced dogfighting of my original concept and sliding into something with the pacing of survival horror: <em>Edge forward, fire some shots, back off and check behind you. Move slow, flush enemies out, and try to keep your back to a wall.<\/em> <\/p>\n<p>It&#8217;s a totally different game. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_fov2.jpg' class='insetimage'   alt='gr8_fov2.jpg' title='gr8_fov2.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>The typical gutless design approach is, &#8220;Make it an option!&#8221; That was my first instinct. But really, these are two very different games we&#8217;re talking about here. They each demand different and mutually exclusive approaches to AI, art, weapon balance, and enemy design.  Heck, even non-gameplay stuff like <em>music<\/em> needs to change. You don&#8217;t want the jaws theme creeping along in the background of the <a href=\"http:\/\/www.youtube.com\/watch?v=z2gfDO_8ggQ\">burly brawl<\/a> and it doesn&#8217;t make sense to have <a href=\"http:\/\/www.youtube.com\/watch?v=n3N_Lkgftc4#t=0m45s\">Keep Hope Alive<\/a> thumping away while the characters search the haunted house with the dodgy flashlight, whispering to each other and jumping at shadows. <\/p>\n<p>When you&#8217;ve got two divergent gameplay modes like this you can:<\/p>\n<ol>\n<li>Neglect one mode in favor of the other. (Try our game! It comes with a tacked-on second game that&#8217;s not worth playing!)\n<li>Build two different and mutually exclusive games. (Because time and money are no object!)\n<li>Split the difference and make two crappy games instead of one good one. (Because we don&#8217;t have a clear vision!)\n<\/ol>\n<p>This problem should sound familiar to any developer forced to add mutiplayer deathmatch to their single-player game. <\/p>\n<p>As much as I hate to kill this flashlight mode idea, it&#8217;s too far from my original concept to be added on and it&#8217;s too different to coexist. <\/p>\n<p>I spent all day Friday thinking about the wall-extrusion idea that was <a href=\"?p=20777&#038;cpage=1#comment-349029\">proposed<\/a>. It was eating at me, so I decided to try it out. Here is how it works:<\/p>\n<p>You look at the area directly around the player and find all the line segments that form the walls. <\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_los1.jpg' class='insetimage' width='600' alt='Easy.' title='Easy.'\/><\/td><\/tr><\/table><\/p>\n<p>For every wall, you take the endpoints and draw a line from the player to those points.<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_los2.jpg' class='insetimage' width='600' alt='Still easy.' title='Still easy.'\/><\/td><\/tr><\/table><\/p>\n<p>Now give those points a big ol&#8217; shove along the direction of that line.<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_los3.jpg' class='insetimage' width='600' alt='SO easy. Yawn.' title='SO easy. Yawn.'\/><\/td><\/tr><\/table><\/p>\n<p>Now use the new and old points to form a polygon. Draw it.<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_los4.jpg' class='insetimage' width='600' alt='Amount of rocket science involved: %0.' title='Amount of rocket science involved: %0.'\/><\/td><\/tr><\/table><\/p>\n<p>Do this for all the walls in the area and you get shadows. No line of sight checking. No fussing with collision.<\/p>\n<p>Now, the game runs plenty fast and I didn&#8217;t need to do this for performance reasons. But this wasn&#8217;t really about performance. This is just a better way to do this. <\/p>\n<ol>\n<li>This new system creates pixel-perfect shadows.  The old system still had a little bit of wibble-wobble when looking around corners. This way is flawless and smooth.\n<li>This new system is about 50 lines of code smaller. Somewhat counter-inuitively, programmers love it when they get to delete code. (Well, deleting recent code stings a little.) Less code means less text to wade through, less to document, less to worry about and less to examine when things go wrong. Making something with less lines of code is like making an airplane with less metal. It&#8217;s just good engineering. (Assuming, of course, you&#8217;re genuinely getting rid of lines of code and not just packing the lines together using tight spacing and &#8220;clever&#8221; formatting tricks. Don&#8217;t get me started.)\n<li>And if you DO happen to care about performance: Old system did 1,000 to 2,000 checks, depending on local topography, and always drew 180 polygons. This new system does 25 checks (looking for walls) and draws between zero and thirty polygons. That is, my worst-case rendering situation with the new system is still way, way better than the best-case situation with the old system.\n<\/ol>\n<p>But!<\/p>\n<p>With this new system I would no longer have this cool flashlight mode. Oh sure, I could ADD it with some tricky stencil operations. But that would be adding a feature, instead of having the feature be sort of a byproduct of how you&#8217;re already doing things. <\/p>\n<p>Is it worth fussing with the stencil buffer to restore flashlight mode? I guess I sort of spoiled that conclusion earlier in the post. It&#8217;s not. It&#8217;s a fun gimmick but it doesn&#8217;t work in this particular game. <\/p>\n<p>Now, there are a bunch more optimizations I could do here to trim those polygons down even further. For example, some of these walls face AWAY from me and therefore aren&#8217;t needed for the purposes of blocking sight.<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_los5.jpg' class='insetimage' width='600' alt='These walls are facing away from the viewer, and are therefore behind OTHER walls.' title='These walls are facing away from the viewer, and are therefore behind OTHER walls.'\/><\/td><\/tr><\/table><\/p>\n<p>If I ignored the walls that faced away from the player I could come close to cutting the poly count in half. But right now that would feel like cutting coupons after you won the lottery. Look, I&#8217;m as frugal as they come, but we&#8217;re talking about an optimization that would &#8211; under maximal circumstances &#8211; save us ~16 polygons a frame. That&#8217;s not even worth thinking about right now.<\/p>\n<p>Well, that worked out as well as could be hoped. Let&#8217;s try another community suggestion.  Way back in <a href=\"?p=20649\" title=\"Project Good Robot Part 2: Welcome to 2D\">part 2<\/a> I talked about having the camera chase you around and some people suggested having the camera fly <em>ahead<\/em> of you.<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_camera1.jpg' class='insetimage' width='600' alt='Ha ha! Missed me!' title='Ha ha! Missed me!'\/><\/td><\/tr><\/table><\/p>\n<p>It&#8217;s pretty easy to look at which way the player is going and just have the camera try to stay out in front of them. <a href=\"http:\/\/www.youtube.com\/watch?v=OOw2Bi2lfGo\">The old 2D Grand Theft Auto games<\/a> did this.  As you accelerated, the camera would pull back and move ahead of you, letting you see more of the world. It was facilitating fast movement and rewarding high-risk travel. Let&#8217;s give that a try in this game.<\/p>\n<p>(Minutes later.)<\/p>\n<p>OH MY GOSH WHO RUINED MY VIDEOGAME!?!?!<\/p>\n<p>We have many layers of things going wrong here. While I can see right away that this is a disastrous idea, it takes me a few minutes of play to figure out why. <\/p>\n<p>In Grand Theft Auto, you were traveling in a slowly accelerating vehicle with a turning radius. Even the best cars in the game took a few seconds to reach top speed and required half the screen (or more) to perform a U-turn at speed. But in this game we&#8217;re controlling a flying robot that accelerates in about two seconds (and most of the acceleration happens in the first half second) and can reverse direction almost instantly.  This means that in GTA you would get these wide, sweeping turns, and in this game you get abrupt cutbacks. In GTA you reverse direction rarely. In Good Robot, you spend most of your time zig-zagging through enemy fire.<\/p>\n<p>The result is that the camera has to make huge movements to stay ahead of you.  When you reverse direction it suddenly needs to be on the opposite side of you.  These swinging camera movements are a roadmap to getting puke on your keyboard.  It&#8217;s that bad.<\/p>\n<p>The other problem is that you spend a lot of time fighting while moving backwards. Having the camera fly ahead of you will turn this view:<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_camera2.jpg' class='insetimage' width='600' alt='Why is nobody EVER just glad to see me?' title='Why is nobody EVER just glad to see me?'\/><\/td><\/tr><\/table> <\/p>\n<p>Into this one:<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr8_camera3.jpg' class='insetimage' width='600' alt='Where did everybody go?' title='Where did everybody go?'\/><\/td><\/tr><\/table> <\/p>\n<p>So the camera is often shoving the important projectiles and robots off the screen so it can show you empty space. This is a textbook example of &#8220;fighting the camera&#8221;, where the challenge of the game comes from trying to get the camera into a useful position without dying in the process.<\/p>\n<p>Well, one idea worked out and the other didn&#8217;t. Both were interesting experiments. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>As many of you requested, I&#8217;m not going to document every feature that goes into this thing. This means we&#8217;re going to jump ahead and start talking about stuff I&#8217;m dealing with now-ish. A side effect of this is that you&#8217;re going to see unexplained features appear and be tempted to ask, &#8220;But, what is [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[498],"tags":[311],"class_list":["post-20823","post","type-post","status-publish","format-standard","hentry","category-good-robot","tag-good-robot"],"_links":{"self":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/20823","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=20823"}],"version-history":[{"count":0,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/20823\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=20823"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=20823"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=20823"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}