{"id":498,"date":"2011-02-06T09:06:39","date_gmt":"2011-02-06T15:06:39","guid":{"rendered":"http:\/\/nootropicdesign.com\/projectlab\/?p=498"},"modified":"2019-05-26T11:24:43","modified_gmt":"2019-05-26T16:24:43","slug":"asteroids-on-hackvision","status":"publish","type":"post","link":"https:\/\/nootropicdesign.com\/projectlab\/2011\/02\/06\/asteroids-on-hackvision\/","title":{"rendered":"Asteroids Available for Hackvision"},"content":{"rendered":"<p>I&#8217;m very happy to announce Asteroids for <a href=\"https:\/\/nootropicdesign.com\/hackvision\" target=\"_blank\" rel=\"noopener noreferrer\">Hackvision<\/a>.  This arcade classic gives you real arcade action written completely using Arduino.  You can download it from the <a href=\"https:\/\/nootropicdesign.com\/hackvision\/games.html#asteroids\" target=\"_blank\" rel=\"noopener noreferrer\">Hackvision games page<\/a>, or <a href=\"https:\/\/nootropicdesign.com\/store\/index.php?main_page=product_info&#038;cPath=2&#038;products_id=11\" target=\"_blank\" rel=\"noopener noreferrer\">order it pre-loaded on a Hackvision<\/a>.<\/p>\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/w03dO0Hd660\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><\/p>\n<p><br clear=\"all\"\/><br \/>\nGame play is just like you remember:<\/p>\n<ul>\n<li>Shoot the asteroids and they break into smaller asteroids.  Large asteroids are worth 20 points, medium 50 points, and small 100 points.<\/li>\n<li>Press the thrust button to move your ship forward.  Unlike in real space, there is drag which slows your ship to a stop over time.<\/li>\n<li>Watch out for the flying saucer &#8212; it shoots in random directions and can crash into you.  Shoot it for 500 points.<\/li>\n<li>If you are in real trouble, press the hyperspace button.  This transports you to a different location, but that location may be very dangerous, too!<\/li>\n<li>When all the asteroids on a level are destroyed, a new level starts.  Higher levels have more asteroids.<\/li>\n<li>You start with 3 ships, and get an extra ship every 10,000 points.<\/li>\n<li>At the end of the game you can enter your initials if you got one of the top 10 high scores.  High scores are stored in EEPROM so they are not erased when power is off.  Also, different games use different high score &#8220;files&#8221; in EEPROM, so your Asteroids scores won&#8217;t interfere with your Space Invaders scores, etc.<\/li>\n<\/ul>\n<p>Here&#8217;s how the controls work:<\/p>\n<div id=\"attachment_497\" style=\"width: 650px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidsControls.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-497\" class=\"size-full wp-image-497\" title=\"asteroidsControls\" src=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidsControls.jpg\" alt=\"\" width=\"640\" height=\"374\" srcset=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidsControls.jpg 640w, https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidsControls-300x175.jpg 300w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><p id=\"caption-attachment-497\" class=\"wp-caption-text\">Controls for Asteroids<\/p><\/div>\n<p\/>\n<p\/>\n<h3>Programming Details<\/h3>\n<table>\n<tr>\n<td><a href=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-494\" style=\"border:none;\" title=\"asteroidImage\" src=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage.png\" alt=\"\" width=\"240\" height=\"240\" srcset=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage.png 240w, https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage-150x150.png 150w\" sizes=\"auto, (max-width: 240px) 100vw, 240px\" \/><\/a><\/td>\n<td>\n<div class=\"padded\">Most of the objects drawn on the screen are defined as bitmaps.  An asteroid is simply a bitmap defined in <span class=\"code\">asteroid_bitmaps.h<\/span>.  Other files define bitmaps for the ship, flying saucer, explosions, etc.  A customized version of TVout&#8217;s <span class=\"code\">bitmap<\/span> function allows bitmaps to be overlayed on each other without erasing the pixels underneath.  This allows asteroids to overlap without overwriting each other.<\/div>\n<\/td>\n<\/tr>\n<\/table>\n<p><br clear=\"all\"\/><\/p>\n<p\/>\n<p\/>\nThe main loop of the game looks basically like this.  Some details are omitted, but this is the basic idea:<\/p>\n<pre class=\"codeblock\">\r\nvoid loop() {\r\n  moveAsteroids();\r\n  moveShots();\r\n  getInput();\r\n  moveShip();\r\n  drawShip();\r\n  drawSaucer();\r\n  killed = detectCollisions();\r\n  if (killed) {\r\n    \/\/ pause and reset the ship...\r\n  }\r\n  drawExplosions();\r\n  if (nAsteroids == 0) {\r\n    \/\/ reset variables...\r\n    newLevel();\r\n  }\r\n  if (remainingShips == 0) {\r\n    gameOver();\r\n    if (score > 0) {\r\n      enterHighScore(1);\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p\/>\nThis game is all about collision detection.  While the game is running, the code needs to detect the following collisions:<\/p>\n<ul>\n<li>collisions between your ship and asteroids<\/li>\n<li>collisions between the shots you fire and asteroids<\/li>\n<li>collisions between the saucer and your ship<\/li>\n<li>collisions between the saucer&#8217;s fired shot and your ship<\/li>\n<li>collisions between the shots you fire and the saucer<\/li>\n<\/ul>\n<p>This sounds like a lot of work, but the ATmega328 has more than enough speed to perform all these computations many times per second.  The key to these collision detections is using a &#8220;point in polygon&#8221; algorithm which determines whether a point is within the bounds of a polygon defined as a set of vertices.<\/p>\n<table>\n<tr>\n<td><a href=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-495\" style=\"border:none;\" title=\"asteroidImage2\" src=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage2.png\" alt=\"\" width=\"240\" height=\"240\" srcset=\"https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage2.png 240w, https:\/\/nootropicdesign.com\/projectlab\/wp-content\/uploads\/2011\/02\/asteroidImage2-150x150.png 150w\" sizes=\"auto, (max-width: 240px) 100vw, 240px\" \/><\/a><\/td>\n<td>\n<div class=\"padded\">Each object displayed on the screen is defined by a set of vertices for the purpose of detecting collisions.  The vertices define a polygon which let us detect if a point is in the polygon.  For example, the code needs to detect if any of the shots fired by our ship are within any of the polygons that define the asteroids.  This point-in-polygon algorithm is used for all collision detection in the game.  Polygons are defined in the vertices source files, e.g. <span class=\"code\">asteroid_vertices.cpp<\/span> and <span class=\"code\">saucer_vertices.cpp<\/span>.<\/div>\n<\/td>\n<\/tr>\n<\/table>\n<p><br clear=\"all\"\/><br \/>\nThe point-in-polygon algorithm is surprisingly short, but not very easy to understand.  Here&#8217;s the function <span class=\"code\">inPolygon<\/span> which takes as arguments the number of vertices, an array of the X coordinates of the vertices, an array of the Y coordinates of the vertices, and the (x,y) coordinates of the point we are testing.  <\/p>\n<pre class=\"codeblock\">\r\n\/*                                                                                                 \r\n * This is the point-in-polygon algorithm adapted from                                             \r\n * http:\/\/www.ecse.rpi.edu\/Homepages\/wrf\/Research\/Short_Notes\/pnpoly.html                          \r\n *\/\r\nboolean inPolygon(byte nvert, byte *xvert, byte *yvert, int x, int y) {\r\n  char i, j;\r\n  byte xvi, xvj, yvi, yvj;\r\n  boolean inside = false;\r\n  for (i=0, j=nvert-1; i&lt;nvert; j=i++) {\r\n    xvi = pgm_read_byte(xvert + i);\r\n    xvj = pgm_read_byte(xvert + j);\r\n    yvi = pgm_read_byte(yvert + i);\r\n    yvj = pgm_read_byte(yvert + j);\r\n    if ( ((yvi &gt; y) != (yvj &gt; y)) &&\r\n         (x &lt; (xvj - xvi) * (y - yvi) \/ (yvj - yvi) + xvi) )\r\n       inside = !inside;\r\n  }\r\n  return inside;\r\n}\r\n<\/pre>\n<p\/>\nNotice that the vertices are read from the AVR flash memory using the function <span class=\"code\">pgm_read_byte(prog_uchar *pointer)<\/span>.  I stored all the bitmaps and vertices in flash memory to conserve SRAM.  This is critical when developing games on the ATmega328 since it only has 2K of SRAM.  For more info on using flash memory, see <a href=\"http:\/\/www.arduino.cc\/en\/Reference\/PROGMEM\">the Arduino page on this subject<\/a>.<\/p>\n<h3>Sound Design<\/h3>\n<p>With this game I took the sound design to a new level.  The TVout library provides a simple <span class=\"code\">tone(frequency, duration)<\/span> function for generating tones using PWM on Arduino pin 11.  This mechanism is very limiting when trying to create sounds for games.  It&#8217;s easy to start a tone and have it play for a specified duration, but it&#8217;s difficult to create sound effects that require the rapid changing of frequency while still allowing the game to continue forward.<\/p>\n<p>I needed a more dynamic mechanism to create the laser shot sound effect which requires the frequency to drop rapidly at a fixed rate of change so it didn&#8217;t sound too choppy.  The solution was to take advantage of the VBI hook feature of TVout.  VBI means Vertical Blanking Interval, and it&#8217;s the time period between the end of one screen frame and the beginning of the next frame.  TVout draws at 60 frames per second (with NTSC), so there are 60 VBIs per second.  The TVout function <span class=\"code\">set_vbi_hook(void (*func)())<\/span> allows you to specify a function to be called at the end of every frame.  This means you can specify some code to run 60 times per second at a perfectly uniform rate.  <\/p>\n<p>I created a function <span class=\"code\">soundISR()<\/span> to run at each VBI.  This function checks to see if there is a sound playing, and a flag tells it whether the sound is the FIRE sound (laser shot) or EXPLOSION sound.  Then the frequency is changed accordingly (e.g. for laser shot, decrement the frequency by 100 Hz) and the pin 11 PWM registers adjusted accordingly by the function <span class=\"code\">setPWMFreq(unsigned int f)<\/span>.  The sound effect is still initiated by calling <span class=\"code\">tv.tone(freq, duration)<\/span> and terminated by the TVout library, but my function modifies the frequency 60 times a second as needed.  This led to a much better way of generating sound effects without interrupting game play.<\/p>\n<h3>Hacking Ideas<\/h3>\n<ul>\n<li>Give yourself more lives.  Find the line of code that sets <span class=\"code\">remainingShips = 3<\/span> and change it.<\/li>\n<li>Enable &#8220;autofire&#8221; so you can hold the fire button down.  Find the line in the code that says &#8220;uncomment for autofire&#8221;.<\/li>\n<li>Define your own bitmaps for the asteroids.  What would you like to shoot?  Also define vertices that define the bounding poloygon.<\/li>\n<li>Change the sound effects.<\/li>\n<li>Wire up an alternate controller using some <a href=\"http:\/\/www.sparkfun.com\/products\/9336\">awesome arcade buttons<\/a>.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m very happy to announce Asteroids for Hackvision. This arcade classic gives you real arcade action written completely using Arduino. You can download it from the Hackvision games page, or order it pre-loaded on a Hackvision. Game play is just like you remember: Shoot the asteroids and they break into smaller asteroids. Large asteroids are [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-498","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/posts\/498","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/comments?post=498"}],"version-history":[{"count":47,"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/posts\/498\/revisions"}],"predecessor-version":[{"id":2281,"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/posts\/498\/revisions\/2281"}],"wp:attachment":[{"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/media?parent=498"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/categories?post=498"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nootropicdesign.com\/projectlab\/wp-json\/wp\/v2\/tags?post=498"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}