{"id":20671,"date":"2013-08-21T05:02:55","date_gmt":"2013-08-21T10:02:55","guid":{"rendered":"http:\/\/www.shamusyoung.com\/twentysidedtale\/?p=20671"},"modified":"2015-07-01T03:43:17","modified_gmt":"2015-07-01T08:43:17","slug":"project-good-robot-part-3-killer-robots","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=20671","title":{"rendered":"Project Good Robot 3: Killer Robots"},"content":{"rendered":"<p>A quick reminder that while I&#8217;m writing this in the present tense (easier for me than switching tenses) we&#8217;re actually looking at this game as it existed three weeks ago. My first code check-in is dated July 29, so we&#8217;re visiting the past right now. If things go the way they usually do, then this series will eventually catch up to the present, since coding tends to slow down as a project grows. <\/p>\n<p>So I&#8217;ve got my world of random walls and I&#8217;ve got a little avatar I can fly through it. I guess next we should work on AI. But to work on AI we need some foes. Well, there&#8217;s no sense in adding foes if they can&#8217;t attack me, and I don&#8217;t want to debug both AI AND shooting mechanics at the same time, so I should add shooting first. Of course, shooting will require collision detection. And to add these things to the scene I need textures for it all so I can tell one thing from another. <\/p>\n<p>I&#8217;ll be using one texture for all the foes and projectiles in the game. In old 8-bit games this was called a sprite sheet and it looked like this:<\/p>\n<p><!--more--><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr3_sprites.jpg' class='insetimage'   alt='gr3_sprites.jpg' title='gr3_sprites.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Basically, you&#8217;ve got a big ol&#8217; tablecloth of little images, and the program cuts it up and uses it according to whatever scheme the programmer devises.  In a strict sense, I don&#8217;t think you can call what I&#8217;m doing a &#8220;sprite sheet&#8221;, although to my non-existent art team the two things would be identical. Sprite sheets are for sprites &#8211; which (as I understand the word) are usually raw pixel transfers of the source image. I&#8217;m not using sprites, I&#8217;m using polygons that kinda look sprite-ish. But I&#8217;m calling it my sprite sheet anyway. You can&#8217;t stop me. I&#8217;ve got the source code.<\/p>\n<p>(For the rest of these screenshots, the player avatar is just a random image from my sprite sheet. None of this is how it&#8217;s supposed to look. While I was doing this coding I was also talking little breaks and making sprites to see what sorts of things look good and what doesn&#8217;t. I messed with colors, shapes, and I messed with background colors of varying levels of saturation and intensity. The screenshots in the next few entries will therefore seem kind of schizophrenic. There&#8217;s no sense in my trying to document the art changes, even if I remembered them.)<\/p>\n<p>This sprite-sheet business is a huge timesaver. In a normal 3D game, you can&#8217;t just stick all your high-resolution wall textures onto a single texture like this. Well, you can, but unless you&#8217;re doing <a href=\"http:\/\/www.youtube.com\/watch?v=BiQCz2NjPR8\">something fancy<\/a> it&#8217;s not worth the hassle. You don&#8217;t want all the textures for your entire level stuck on ONE texture, since you probably don&#8217;t need ALL of them at any given moment. You&#8217;ll just be gobbling up GPU memory for no good reason and giving your artists a bunch of hassle. (I&#8217;ll bet arranging 1024&#215;1024 textures on a huge grid would be a nightmare in photoshop.)<\/p>\n<p>But here I don&#8217;t need a lot of pixels.  This saves me the usual hassle of keeping track of what texture is in use, switching textures when needed, and designing the program to switch textures as rarely as possible. <\/p>\n<p>Making sprites out of triangle pairs is effortless. Projectiles are easy in 2D. Collision detection is simple.  <\/p>\n<p>Next up is shooting. <\/p>\n<p>In 3D, translating the mouse pointer into the scene and figuring out what is being clicked on is an exercise in madness. You&#8217;ve got to project the 2d position into the 3D space, which takes some math and can go wrong in a dozen different ways. Are you projecting a line from the center of the screen, looking for collisions for a hitscan weapon? Or are you translating the mouse position into 3D space to figure out what bit of scenery was clicked on? Do you need the actual position in 3D space or just the target object? (Or worse, the clicked position on the target object. Have fun with that.) Have you taken into account field of view? Aspect ratio?<\/p>\n<p>Ha ha! We&#8217;re doing 2D, which means that converting from screen coordinates to world space is basically the same as looking at a map and converting centimeters to kilometers. You don&#8217;t even need to comment the code, because it&#8217;s so embarrassingly simple.<\/p>\n<p>I set it up so that I can point my mouse somewhere on-screen and my little avatar will shoot at (towards) the cursor, letting me aim with the mouse. The only reason this takes more than five minutes is because I mistakenly believe it isn&#8217;t working. As I fly around the map I see my shots aren&#8217;t intersecting with the mouse target thing. It takes me a minute to realize <strong>of course the shots aren&#8217;t hitting the target, you&#8217;re dragging the target with you as you fly.<\/strong><\/p>\n<p>Oh. Duh.<\/p>\n<p>Are we done with this foundational stuff? Can we write some AI now? Yes? Good. <\/p>\n<p>(Two hours later.)<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr3_shooting3.jpg' class='insetimage'   alt='gr3_shooting3.jpg' title='gr3_shooting3.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Sidenote: Wow! JPEG compression does not like these shades of purple. Kinda turned my screenshots into mud. I didn&#8217;t really notice until I was done. I&#8217;m not going to go back and re-encode all these shots as PNG just so you can see these horrible graphics in crystal clarity. You get the idea. Let&#8217;s just move on. <\/p>\n<p>Okay, I have a system where bad guys operate according to a few rules:<\/p>\n<ol>\n<li>If you&#8217;re too close to the player, back away. (While shooting.)\n<li>If you&#8217;re far away from the player, close in. (While shooting.)\n<li>If you&#8217;re at a happy distance, circle around the player. (Oh yes more shooting.)\n<\/ol>\n<p>Different robots have different pre-set &#8220;ideal&#8221; range, and they alternate which way they want to circle you. For each state they have a list of priorities. If they want to back away and they can&#8217;t (because you&#8217;ve got them against the wall) then they will circle. If they&#8217;re trying to circle and they can&#8217;t, they flip their circle-direction and try again. In short, these are some stupid robots. This is stone-age AI here. <\/p>\n<p>And yet, it actually feels really good. Like, they&#8217;re dumb, but they feel devious. They blast you, but they&#8217;re hard to hit because they&#8217;re circling. So you close in to make it easier to hit them, but they back away. And you can&#8217;t head straight at them without running nose-first into their projectiles. And meanwhile the other robots are still pelting you.<\/p>\n<p>These guys are really annoying in a good way.  The way to beat them is to out-maneuver them, which is exactly the game feel I&#8217;m going for.  <\/p>\n<p>I was going to write <a href=\"http:\/\/en.wikipedia.org\/wiki\/A*_search_algorithm\">A*<\/a>, but I honestly don&#8217;t know what I&#8217;d use it for. I suppose I could use it to help a bot out if it gets stuck in a cul-de-sac. I don&#8217;t know if that will be a big problem or not. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr3_shooting.jpg' class='insetimage'   alt='gr3_shooting.jpg' title='gr3_shooting.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>This is a very strange game right now. I can&#8217;t die. It&#8217;s not even tracking damage. If I kill a robot another one instantly appears. There&#8217;s no sound system yet. So it&#8217;s this silent ballet where I&#8217;m endlessly attacked by an army of implacable flux capacitors. <\/p>\n<p>I know it looks like <em>MS Paint: The Movie: The Videogame Adaptation<\/em>, but it&#8217;s actually turning out really well from a gameplay perspective. I&#8217;ve got bots that zap you from different distances and others that rush at you. (I could use these for melee or suicide bomb attacks.) You&#8217;re often engaging multiple foes at multiple distances and the only way to stay alive is to keep moving, keep dodging. <\/p>\n<p>The only problem now is that with so many identical projectiles flying around it&#8217;s hard to tell which way everything is going. I&#8217;ll find myself plowing into slow-moving projectiles not because I ran out of room, but because I misjudged which way they were going.  This is one of the things I dislike about <a href=\"http:\/\/en.wikipedia.org\/wiki\/Shmups\">shmups<\/a>. <\/p>\n<p>I enjoy zig-zagging through projectiles. When I get hit because I didn&#8217;t think fast enough, that&#8217;s fine. But in a shmup you sometimes get that feeling where the whole screen looks like noise. All the bullets look the same and when you get hit it&#8217;s not because you moved wrong, it&#8217;s because you didn&#8217;t even notice the bullet. Sometimes you just take damage and you don&#8217;t even know what you did wrong. I understand that&#8217;s how shmups work, but that&#8217;s not the kind of game I want to make.  <\/p>\n<p>I try elongating bullets so they have trails:<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/gr3_shooting2.jpg' class='insetimage'   alt='gr3_shooting2.jpg' title='gr3_shooting2.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>That makes a huge difference. This visual change just made the game several times easier. I didn&#8217;t change bullet speed or projectile size. (Unless maybe bullets got slightly <em>bigger<\/em>.) But now you can judge the incoming angles and get a sense of who is shooting at you and where all the laser-bullets are going. <\/p>\n<p>(That&#8217;s how lasers work, right? Gluons make the photons all sticky so they gather up on a big ball you can shoot at the enemy at speeds greater than <em>a professional baseball pitch<\/em>! These high-speed photons then unleash their energy on impact, which will either melt through the hull or burn through the hero&#8217;s shirt to expose his manly chest, depending on context. Science!)<\/p>\n<p>I like where this is going so far. It&#8217;s ugly as hell, but it&#8217;s fun.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A quick reminder that while I&#8217;m writing this in the present tense (easier for me than switching tenses) we&#8217;re actually looking at this game as it existed three weeks ago. My first code check-in is dated July 29, so we&#8217;re visiting the past right now. If things go the way they usually do, then this [&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":[],"class_list":["post-20671","post","type-post","status-publish","format-standard","hentry","category-good-robot"],"_links":{"self":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/20671","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=20671"}],"version-history":[{"count":0,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/20671\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=20671"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=20671"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=20671"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}