{"id":3012,"date":"2015-02-09T11:50:35","date_gmt":"2015-02-09T16:50:35","guid":{"rendered":"http:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/?p=3012"},"modified":"2015-10-24T21:30:34","modified_gmt":"2015-10-25T01:30:34","slug":"asteroids-drawing-objects","status":"publish","type":"post","link":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/2015\/02\/asteroids-drawing-objects\/","title":{"rendered":"Asteroids: Drawing Objects"},"content":{"rendered":"<blockquote><p>Recently I&#8217;ve started\u00a0mentoring a local high school student a bit on implementing a video game, and this is a technical note toward\u00a0that.<\/p><\/blockquote>\n<p>Drawing basic shapes out of polygons to represent game objects is straightforward and requires just a bit of trigonometry, outlined here.<\/p>\n<h2>Shapes<\/h2>\n<p>The core idea is that\u00a0the polygon is located around\u00a0the object&#8217;s current position. So, a standard looking Asteroids ship might be\u00a0defined as four points about the origin as in this diagram:<\/p>\n<div id=\"attachment_3019\" style=\"width: 210px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-3019\" class=\"size-full wp-image-3019\" src=\"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship.png\" alt=\"Four coordinates defining a fairly standard Asteroids player ship.\" width=\"200\" height=\"200\" srcset=\"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship.png 200w, https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship-150x150.png 150w, https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship-60x60.png 60w\" sizes=\"auto, (max-width: 200px) 100vw, 200px\" \/><\/a><p id=\"caption-attachment-3019\" class=\"wp-caption-text\">Four coordinates defining a fairly standard Asteroids player ship.<\/p><\/div>\n<p>The polygon is captured by a list (array) of points in order around the shape.\u00a0Caveat other restrictions in the game&#8217;s code, it doesn&#8217;t really matter if they&#8217;re clockwise or counter-clockwise.\u00a0In\u00a0this case the ship is defined as follows, proceeding counter-clockwise:<\/p>\n<ol>\n<li>(0, 24)<\/li>\n<li>(-18, -24)<\/li>\n<li>(0, -18)<\/li>\n<li>(18, -24)<\/li>\n<\/ol>\n<p>One cute trick that&#8217;s often done in Asteroids is to randomly generate the polygons for the asteroids themselves. The points list needs to be in order though or else the lines will overlap and the shape look funny.\u00a0There are several ways to do this, but one is to go\u00a0around in a circle, picking a random angle within that\u00a0arc of the circle, picking a random distance from the origin for that tip of the rock, and computing the x &amp; y value for that polygon point using that angle and distance.<\/p>\n<h2>Drawing<\/h2>\n<p>In most polygon graphics APIs, drawing starts by starting a new shape if necessary (i.e., <code>newpath<\/code>) and moving the cursor to the first\u00a0point in the list (i.e., <code>moveto<\/code>). It then loops over each of the other points and draws a line from the previous position to the current point (i.e., <code>lineto<\/code>). The path is then either closed by drawing a line to the first point in the list from the last, or using a specific function to close the path\u00a0if the API has one\u00a0(i.e., <code>closepath<\/code>).<\/p>\n<h2>Rotation and Placement<\/h2>\n<p>Of course, in the game the object typically has to rotate and move.\u00a0Rotation is simple because we&#8217;re taking the object&#8217;s current position as the origin of the polygon representing it. Each\u00a0point to draw just needs to be calculated through the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Rotation_(mathematics)\">basic rotation formula<\/a>:<\/p>\n<blockquote><p><code>x' = (x * cos(angle)) - (y * sin(angle))<br \/>\ny' = (x * sine(angle)) + (y * sin(angle))<\/code><\/p><\/blockquote>\n<p>In\u00a0these\u00a0formulas, <code>x<\/code> and <code>y<\/code> are the current point in the polygon list while <code>x'<\/code> and <code>y'<\/code> are the actual points to draw. The\u00a0direction the object is currently rotated to, i.e.,which way\u00a0the player is facing, is in <code>angle<\/code>.<\/p>\n<p>This will draw the polygon around the origin, but of course the object is actually somewhere else on screen. This is a simple translation, effectively moving the polygon&#8217;s origin to the object&#8217;s actual position. In other words, just adding the object&#8217;s x and y coordinates to the point to draw:<\/p>\n<blockquote><p><code>x' = x' + objectx<br \/>\ny' = y' + objecty<br \/>\n<\/code><\/p><\/blockquote>\n<h2>Minor Complications<\/h2>\n<p>Although the ship above has somewhat naturally been modeled facing up, angle 0 in trigonometry is actually facing to the right. So, the polygon should instead be modeled with its natural direction facing that way.<\/p>\n<p>Adding just a small detail, essentially all modern computer displays and most software use a slightly different coordinate system from what&#8217;s typically used in mathematics: The origin is at the top left of the screen, and the y axis increases going down the screen, not up. Note that this means\u00a0the 90 degree angle is actually facing\u00a0down and 270 degrees points straight up.<\/p>\n<p>A wide variety of ways to work\u00a0with these facts\u00a0can be applied, but the easiest is just to model the polygon facing to the right and to keep that\u00a0coordinate scheme in mind. So,\u00a0the example polygon above\u00a0would actually be modeled as follows:<\/p>\n<div id=\"attachment_3020\" style=\"width: 210px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship-rotated.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-3020\" class=\"size-full wp-image-3020\" src=\"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship-rotated.png\" alt=\"The ship actually facing angle 0.\" width=\"200\" height=\"200\" srcset=\"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship-rotated.png 200w, https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship-rotated-150x150.png 150w, https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-content\/uploads\/2015\/02\/ship-rotated-60x60.png 60w\" sizes=\"auto, (max-width: 200px) 100vw, 200px\" \/><\/a><p id=\"caption-attachment-3020\" class=\"wp-caption-text\">The ship actually facing angle 0.<\/p><\/div>\n<p>The counter-clockwise ordering of points would then be:<\/p>\n<ol>\n<li>(24, 0)<\/li>\n<li>(-24, -18)<\/li>\n<li>(-18, 0)<\/li>\n<li>(-24, 18)<\/li>\n<\/ol>\n<p>Another small detail to keep in mind\u00a0is that nearly all trigonometry functions in software libraries are based on <a href=\"http:\/\/en.wikipedia.org\/wiki\/Radian\">radians<\/a> rather than degrees, though most\u00a0people work more\u00a0easily in the latter. Converting between the two just requires a simple formula based on the identity relationship between them:<\/p>\n<blockquote><p><code>radians = pi *\u00a0degrees \/ 180<\/code><\/p><\/blockquote>\n<h2>Code<\/h2>\n<p>A simple demonstration of this is in the box below. Pressing the left and right arrow keys make the ship rotate, and it&#8217;s being drawn\u00a0in the center of the screen rather than the origin (you may have to click on the box first to focus the keyboard on it):<\/p>\n\n<!-- iframe plugin v.6.0 wordpress.org\/plugins\/iframe\/ -->\n<iframe loading=\"lazy\" src=\"http:\/\/rocketshipgames.com\/tmp\/20150208-asteroids-ship.html\" width=\"100%\" height=\"324\" scrolling=\"yes\" class=\"iframe-class\" frameborder=\"0\"><\/iframe>\n\n<p>The code snippets for this below are\u00a0in Javascript, but should be easily applicable to most platforms.<\/p>\n<p>The polygon for the ship has been defined as follows:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar playershape = &#x5B;\r\n  { x: 24,  y: 0},\r\n  { x: -24, y: -18},\r\n  { x: -18, y: 0},\r\n  { x: -24, y: 18},\r\n]\r\n<\/pre>\n<p>It&#8217;s just an array of points, each a Javascript object with an x and y value. There is also a player object that has its own x, y, and angle, representing the player&#8217;s position and orientation on screen.<\/p>\n<p>A couple trigonometry helper functions are defined, to convert degrees to radians, and to rotate x and y values:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction degtorad(angle) {\r\n  return 3.1415926 * angle \/ 180;\r\n}\r\n\r\nfunction rotx(x,y,angle) {\r\n  return (x*Math.cos(angle)) - (y*Math.sin(angle));\r\n}\r\n\r\nfunction roty(x,y,angle) {\r\n  return (x*Math.sin(angle)) + (y*Math.cos(angle));\r\n}\r\n<\/pre>\n<p>Note that each of the x and y rotations take as input x, y, and angle, because the rotation formula requires each of those values.<\/p>\n<p>As discussed above, the ship is drawn by starting a path at the first point of the polygon, looping through each other point, and then closing it off. At each step the point to draw is rotated about the ship&#8217;s position as the origin, and then translated to the ship&#8217;s actual position on screen. This function captures that, and is called by the main drawing routine each time the ship needs to be displayed:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction drawplayer() {\r\n  var x, y;  \/\/ These will be the point to draw.\r\n  var i = 0;  \/\/ i is the current polygon point we're using\r\n\r\n  ctx.beginPath(); \/\/ ctx is the graphics drawing context in this Javascript program.\r\n\r\n  \/\/ Calculate the actual draw point by rotating and then translating.\r\n  x = rotx(playershape&#x5B;i].x, playershape&#x5B;i].y, player.angle) + player.x;\r\n  y = roty(playershape&#x5B;i].x, playershape&#x5B;i].y, player.angle) + player.y;\r\n  ctx.moveTo(x, y); \/\/ Start the polygon at this point.\r\n\r\n  \/\/ Loop through the other points---note that this therefore begins at point 1, not 0!\r\n  for (i = 1; i &lt; playershape.length; i++) {\r\n    x = rotx(playershape&#x5B;i].x, playershape&#x5B;i].y, player.angle) + player.x;\r\n    y = roty(playershape&#x5B;i].x, playershape&#x5B;i].y, player.angle) + player.y;\r\n    ctx.lineTo(x, y); \/\/ Extend the path from the previous point to this new one.\r\n  }\r\n  ctx.closePath(); \/\/ Close the path by adding a line back to the start.\r\n  ctx.stroke(); \/\/ Draw the path.\r\n}\r\n<\/pre>\n<p>To initialize the player, its position is set to the middle of the screen and its orientation set as facing straight up:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n  player.x = canvas.width \/ 2;\r\n  player.y = canvas.height \/ 2;\r\n  player.angle = degtorad(270);\r\n<\/pre>\n<p>Each time the left or right key is pressed, the ship&#8217;s angle is updated like this.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    player.angle -= degtorad(10); \/\/ Decrease the angle by 10 degrees, making the\r\n                                  \/\/ conversion to radians first before subtracting\r\n                                  \/\/ from the current angle.\r\n    while (player.angle &lt; 0) {        \/\/ This actually isn't necessary, but just makes sure\r\n      player.angle += 3.1415926 * 2;  \/\/ the player's angle is always between 0 and 2*pi.\r\n    }                                 \/\/ The drawing routines and other logic will all\r\n                                      \/\/ handle that fine, but it can make things easier for\r\n                                      \/\/ the programmer in writing other parts of the code.\r\n<\/pre>\n<h2>Conclusion<\/h2>\n<p>Those are\u00a0the basic elements in drawing a simple game like Asteroids. The next complication is having an array or arrays of game objects. That&#8217;s necessary to capture all of them that might appear on screen, namely\u00a0the rocks and bullets. A drawing function like that above then needs to be applied to each game object in the array(s), rather than being just hardcoded to a single\u00a0game object instance like\u00a0this example is to the player&#8217;s ship.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I&#8217;ve started\u00a0mentoring a local high school student a bit on implementing a video game, and this is a technical note toward\u00a0that. Drawing basic shapes out of polygons to represent game objects is straightforward and requires just a bit of trigonometry, outlined here. Shapes The core idea is that\u00a0the polygon is located around\u00a0the object&#8217;s current &hellip; <a href=\"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/2015\/02\/asteroids-drawing-objects\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":3020,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[58],"tags":[257,104,162,401,277],"class_list":["post-3012","post","type-post","status-publish","format-standard","hentry","category-code","tag-featured","tag-html5","tag-javascript","tag-math","tag-videogames"],"_links":{"self":[{"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/posts\/3012","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/comments?post=3012"}],"version-history":[{"count":10,"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/posts\/3012\/revisions"}],"predecessor-version":[{"id":3026,"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/posts\/3012\/revisions\/3026"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/media\/3020"}],"wp:attachment":[{"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/media?parent=3012"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/categories?post=3012"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rocketshipgames.com\/blogs\/tjkopena\/wp-json\/wp\/v2\/tags?post=3012"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}