<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jess</title>
    <description>The latest articles on Forem by Jess (@robotspacefish).</description>
    <link>https://forem.com/robotspacefish</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F16778%2F7bcd7061-2b87-494e-9385-e37ac2c3fb47.jpeg</url>
      <title>Forem: Jess</title>
      <link>https://forem.com/robotspacefish</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/robotspacefish"/>
    <language>en</language>
    <item>
      <title>Ghost Trap: My TeenyTiny DragonRuby MiniGameJam Entry</title>
      <dc:creator>Jess</dc:creator>
      <pubDate>Mon, 30 Nov 2020 04:16:46 +0000</pubDate>
      <link>https://forem.com/robotspacefish/ghost-trap-my-teenytiny-dragonruby-minigamejam-entry-5ee6</link>
      <guid>https://forem.com/robotspacefish/ghost-trap-my-teenytiny-dragonruby-minigamejam-entry-5ee6</guid>
      <description>&lt;p&gt;A few weeks ago I wrote a &lt;a href="https://dev.to/robotspacefish/getting-to-know-the-dragonruby-game-engine-263h"&gt;post&lt;/a&gt; about the Dragonruby game engine. I mentioned that there was a month-long game jam to make a 20 second game. This post is about my entry: Ghost Trap.&lt;/p&gt;

&lt;p&gt;Try it out on &lt;a href="https://robotspacefish.itch.io/ghost-trap" rel="noopener noreferrer"&gt;itch&lt;/a&gt;. If the html version is a little wonky you can download your OS specific version. I tried it on 3 different computers and it was fine on 2 of them, but the older Windows machine could not handle it.&lt;/p&gt;




&lt;p&gt;When I was a kid I really liked the &lt;a href="https://www.youtube.com/watch?v=vdllC3DD_dc&amp;amp;feature=emb_logo" rel="noopener noreferrer"&gt;Ghostbusters game for the Sega Master System&lt;/a&gt;. It's not a great game, but I liked stopping the ghosts from entering buildings, driving and catching the ghosts, and trapping the ghosts at the homes they were haunting.  I decided I would make a game that was similar to trapping the ghosts.&lt;/p&gt;

&lt;p&gt;In the Sega game, you get to your destination and you have 2 ghostbusters to move. You move one where you want him, then use the other to guide the ghosts between the two of them toward the trap. &lt;/p&gt;

&lt;p&gt;I didn't want to make a clone, so I decided to make it so a single "ghostcatcher" catches ghosts in their beam, which get sucked into their backpack. The backpack can only hold 10 ghosts, so the ghostcatcher has to periodically deposit ghosts into the ghost disposal canister. The objective is to trap as many ghosts as you can in 20 seconds. You can earn a combo the more ghosts you catch on your beam at once and your beam energy depletes over time to keep the player from holding the button down the entire 20 seconds. You have to stop shooting to regenerate energy.&lt;/p&gt;

&lt;p&gt;Oh yeah, this is a world of dogs instead of people, because why not? Truthfully I didn't want to draw a ghost that looked like a traditional emoji looking 👻 ghost, so I made a dog-ghost. Plus, this gave me an excuse to use my dog Pixel as a reference for my ghostcatcher. &lt;/p&gt;

&lt;center&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fghost-trap%2Fghost80.png%3Fraw%3Dtrue" alt="ghost dog" width="80" height="204"&gt;
&lt;/center&gt;

&lt;center&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fghost-trap%2Fplayer.gif%3Fraw%3Dtrue" alt="Ghostcatcher Pixel" width="114" height="300"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  Artwork
&lt;/h2&gt;

&lt;p&gt;I made the art with a combination of Affinity Designer and Procreate on the iPad and Affinity Photo on the Mac. I never used the Affinity software before so I was learning as I went along. Affinity Designer is really cool and I enjoyed working with it once I started to get the hang of it, but I really miss having access to Photoshop which is something I'm quite comfortable with.&lt;/p&gt;

&lt;center&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fghost-trap%2Fscreenshot01.png%3Fraw%3Dtrue" alt="Gameplay screenshot" width="800" height="400"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  Code Organization
&lt;/h2&gt;

&lt;p&gt;I broke my code down into 6 classes and a few helper methods. I think some of the code I wrote was possibly handled by Dragonruby, but since I'm still learning how it all works I didn't take advantage of all of its features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entity
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Entity&lt;/code&gt; class is the parent for my other game object classes. It has properties for sprite dimensions and attributes, collision, and rendering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# an excerpt of the Entity class&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt;  
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sprite_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:flip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:alpha&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sprite_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;
    &lt;span class="vi"&gt;@h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;
    &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="vi"&gt;@sprite_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sprite_path&lt;/span&gt;
    &lt;span class="vi"&gt;@flip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flip&lt;/span&gt;
    &lt;span class="vi"&gt;@alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rect&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;
     &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sprite_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# ANGLE&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# ALPHA&lt;/span&gt;
      &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# RED SATURATION&lt;/span&gt;
      &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# GREEN SATURATION&lt;/span&gt;
      &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# BLUE SATURATION&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# TILE X&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# TILE Y&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;# TILE W&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;# TILE H&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# FLIP HORIZONTALLY&lt;/span&gt;
      &lt;span class="kp"&gt;false&lt;/span&gt;           &lt;span class="c1"&gt;# FLIP VERTICALLY&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_colliding_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intersect_rect?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;#... etcc&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I made the &lt;code&gt;rect&lt;/code&gt; method to pass to the built-in &lt;code&gt;intersect_rect&lt;/code&gt; to check for collision, but I believe it might be something I could have handled with Dragonruby natively. That's something I have to research further.&lt;/p&gt;

&lt;h3&gt;
  
  
  Player
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Player&lt;/code&gt; class is a subclass of &lt;code&gt;Entity&lt;/code&gt; and handles all the player specific stuff like moving, shooting, catching ghosts, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# an excerpt of the Player class&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Entity&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:total_ghosts_held&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:backpack_limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:beam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:is_shooting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ghosts_on_beam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:beam_power&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:beam_cooldown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:speed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:is_walking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sprite_frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:shoot_sound_playing&lt;/span&gt;

  &lt;span class="no"&gt;MAX_BEAM_POWER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
  &lt;span class="no"&gt;BEAM_COOLDOWN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;
    &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$WIDTH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sprites/player_green_1.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@total_ghosts_held&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# total ghosts in pack&lt;/span&gt;
    &lt;span class="vi"&gt;@backpack_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="vi"&gt;@beam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;h: &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;w: &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="vi"&gt;@is_shooting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

    &lt;span class="vi"&gt;@ghosts_on_beam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="vi"&gt;@beam_power&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MAX_BEAM_POWER&lt;/span&gt;
    &lt;span class="vi"&gt;@beam_cooldown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vi"&gt;@speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="vi"&gt;@is_walking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="vi"&gt;@sprite_frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vi"&gt;@shoot_sound_playing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_sprite&lt;/span&gt;
    &lt;span class="n"&gt;status_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;space_in_pack?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"green"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"red"&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sprite_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sprites/player_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;status_color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sprite_frame&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.png"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tick_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sprite_frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_walking&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;tick_count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idiv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_shoot?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_shooting&lt;/span&gt;
      &lt;span class="n"&gt;play_sound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:shoot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;shoot_sound_playing&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot_sound_playing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot_sound_playing&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tick_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_ghosts_on_beam?&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot_sound_playing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;add_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_ghosts_on_beam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store_ghosts_from_beam_to_pack&lt;/span&gt;

      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ghosts_on_beam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;

    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refill_beam&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_shooting&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beam_power&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;MAX_BEAM_POWER&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ghost
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ghost&lt;/code&gt; class handles spawning ghosts, and each instance can keep track of whether or not it has free will (if it's caught in the player's beam or can move freely) and if it's invulnerable. Ghosts flicker in and out and are invulnerable to being caught if they are transparent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# an excerpt of the Ghost class &lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ghost&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Entity&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:is_flickering&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:is_invulnerable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:has_free_will&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:is_in_beam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tick_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# use different sprite if ghost is on beam&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sprite_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_in_beam&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"sprites/ghost80.png"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"sprites/ghost_on_beam.png"&lt;/span&gt;

    &lt;span class="c1"&gt;# keep in bounds&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$HEIGHT&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;$HEIGHT&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;$WIDTH&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle_flickering&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tick_count&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flicker&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_flickering&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move_freely&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tick_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_free_will&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop_flickering&lt;/span&gt;
    &lt;span class="c1"&gt;# play_sound(:flicker_in)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_flickering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_invulnerable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_flickering&lt;/span&gt;
    &lt;span class="c1"&gt;# play_sound(:flicker_out)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_free_will&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_flickering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_invulnerable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;Ghost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disposal
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Disposal&lt;/code&gt; is the canister where the player deposits ghosts. It keeps track of how many ghosts it contains and its open/closed status to handle which sprite should display.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# An excerpt of the Disposal class&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Disposal&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Entity&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:total_ghosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:is_open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:timer&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;566&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;221&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;154&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sprites/canister.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@is_open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="vi"&gt;@total_ghosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vi"&gt;@timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deposit_ghosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_to_add&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;play_sound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:dispose&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_ghosts&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;total_to_add&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tick_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_open&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;tick_count&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;open_canister&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sprite_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OPEN&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:sprite_path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OPEN&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:w&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OPEN&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close_canister&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sprite_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:sprite_path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:w&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;
    &lt;span class="c1"&gt;# set sprite based on open status&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_open&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;open_canister&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;close_canister&lt;/span&gt;

    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GhostTrap
&lt;/h3&gt;

&lt;p&gt;Finally, there is the &lt;code&gt;GhostTrap&lt;/code&gt; class, which is the game class that handles all the game functionality and pieces everything together. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;defaults&lt;/code&gt; method sets up all of the properties in &lt;code&gt;state&lt;/code&gt;. Normally I would use instance variables, but Dragonruby has a built-in variable called &lt;code&gt;state&lt;/code&gt; that is passed from tick to tick.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;defaults&lt;/span&gt;
   &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;player&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
   &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disposal&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Disposal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
   &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ghosts&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;MAX_GHOSTS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Ghost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;
   &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
   &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
   &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countdown&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All rendering (drawing to the screen) are organized within the &lt;code&gt;render&lt;/code&gt; method, and all updates are handled within the &lt;code&gt;calc&lt;/code&gt; method. User inputs are handled in &lt;code&gt;process_inputs&lt;/code&gt;. I broke everything down further depending on what game screen the user is on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;
   &lt;span class="n"&gt;render_title&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;
   &lt;span class="n"&gt;render_instructions&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:instructions&lt;/span&gt;
   &lt;span class="n"&gt;render_credits&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:credits&lt;/span&gt;
   &lt;span class="n"&gt;render_play&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:play&lt;/span&gt;
   &lt;span class="n"&gt;render_game_over&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:game_over&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problems
&lt;/h2&gt;

&lt;p&gt;I had the most trouble working with sound effects. I was able to get basic sound effects working, but I couldn't figure out how to get an effect that wasn't music to loop and stop when I wanted it to. I wanted a whirring effect for the player's beam but I settled for an initial effect each time the player first presses the spacebar to shoot. I did this by adding an additional variable, &lt;code&gt;shoot_sound_playing&lt;/code&gt; which I set to true when the player shoots, as long as it's not already true. Then I set it to false when the player stops shooting.&lt;/p&gt;

&lt;p&gt;The Dragonruby docs recommend you go through each sample game thoroughly to really learn the engine, so I went through what I could but I didn't have time to really dig into everything. I think if I go back and look through the code when I get a chance I can probably do some refactoring and make some improvements to my game.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_shoot?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_shooting&lt;/span&gt;
      &lt;span class="n"&gt;play_sound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:shoot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;shoot_sound_playing&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot_sound_playing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot_sound_playing&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tick_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;# ... etc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;You can find the full game code on my &lt;a href="https://github.com/robotspacefish/ghost-trap" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Side note: I know I used &lt;code&gt;self&lt;/code&gt; sometimes and not other times. Personally I prefer to use it because then I know exactly where a method is coming from and to me that's more readable, but Dragonruby doesn't use it and I know Ruby is all about being free of clutter. I'm still in the process of cleaning everything up and making it consistent, but I wanted to get this post out asap. :) &lt;/p&gt;

</description>
      <category>dragonruby</category>
      <category>ruby</category>
      <category>gamedev</category>
      <category>gamejam</category>
    </item>
    <item>
      <title>Getting to Know the DragonRuby Game Engine</title>
      <dc:creator>Jess</dc:creator>
      <pubDate>Sun, 08 Nov 2020 23:00:38 +0000</pubDate>
      <link>https://forem.com/robotspacefish/getting-to-know-the-dragonruby-game-engine-263h</link>
      <guid>https://forem.com/robotspacefish/getting-to-know-the-dragonruby-game-engine-263h</guid>
      <description>&lt;p&gt;DragonRuby Game Toolkit is a 2D game engine for making games using the Ruby programming language. I happened to get a license for it as part of the &lt;a href="https://itch.io/b/520/bundle-for-racial-justice-and-equality" rel="noopener noreferrer"&gt;Bundle for Racial Justice and Equality&lt;/a&gt; on itch.io back in June but never got around to trying it out. Last week I received an email from one of the creators and it mentioned that there is a beginner friendly Game Jam so I figured now would be a good time to dive in! &lt;/p&gt;

&lt;p&gt;The &lt;a href="http://docs.dragonruby.org/" rel="noopener noreferrer"&gt;docs&lt;/a&gt; are pretty extensive but here I'll attempt to sum up some of the very basics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Game Loop
&lt;/h2&gt;

&lt;p&gt;The main function in DragonRuby is called &lt;code&gt;tick&lt;/code&gt; and it runs 60 times per second (about every 16ms). There is no &lt;code&gt;delta time&lt;/code&gt; to worry about; the loop just works at 60fps.&lt;/p&gt;

&lt;p&gt;The code below will create a new instance of &lt;code&gt;Dog&lt;/code&gt; every 16ms and store it in &lt;code&gt;args.state&lt;/code&gt; (I'll come back to this in a bit).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def tick args
  args.state.dog = Dog.new
end

class Dog
  def initialize
    puts "I am a new dog"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fdragonruby%2Fnew_dog_repeat.png%3Fraw%3Dtrue" alt='"I am a new dog!" written repeatedly in the console"' width="800" height="400"&gt;
&lt;/center&gt;

&lt;p&gt;Being that you only want to create a new &lt;code&gt;Dog&lt;/code&gt; instance once and set it to &lt;code&gt;args.state.dog&lt;/code&gt;, this isn't ideal. Luckily, there is an easy way to fix this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def tick args
  args.state.dog = Dog.new if !args.state.dog
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I am using a conditional to set &lt;code&gt;args.state.dog&lt;/code&gt; if it does not already exist. By doing this, it will only set &lt;code&gt;args.state.dog&lt;/code&gt; the first time and will only create the &lt;code&gt;Dog&lt;/code&gt; instance once. &lt;/p&gt;

&lt;p&gt;This is repetitive and long-winded, however. Below, you see an example that accomplishes the same thing by using &lt;code&gt;||=&lt;/code&gt; (or equals), which means to only set the value if its value is &lt;code&gt;false&lt;/code&gt; or &lt;code&gt;nil&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def tick args
  args.state.dog ||= Dog.new
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  args
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;args&lt;/code&gt; contains all the functionality for making your game, such as a way handle input and output, and store your variables. &lt;/p&gt;

&lt;h3&gt;
  
  
  args.state
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;args.state&lt;/code&gt; is a data structure where you can store your game data, such as an instance of &lt;code&gt;Dog&lt;/code&gt;, or your player's position. What's stored in state is carried over from tick to tick.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def tick args 
    args.state.player.x ||= 120
    args.state.player.y ||= 200
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  args.outputs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;args.outputs&lt;/code&gt; is used for rendering to the screen.&lt;/p&gt;

&lt;p&gt;This creates a red 100x100 square at (100, 250). You can change the color by adjusting the RGB values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#                               [x, y, width, height, red, green, blue]
args.outputs.solids &amp;lt;&amp;lt; [100, 250, 100, 100, 255, 0, 0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Quick Note on the coordinate sytem:&lt;/strong&gt; &lt;code&gt;(0, 0)&lt;/code&gt; is at the bottom left corner. Positive numbers go up and to the right and negative numbers go down and to the left.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;args.labels&lt;/code&gt; is used to write text to the screen.&lt;/p&gt;

&lt;p&gt;This adds the black text 'This is a red square' slightly below the red square.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#                               [x, y, text, red, green, blue]
args.outputs.labels &amp;lt;&amp;lt; [100, 240, "This is a red square", 0, 0, 0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fdragonruby%2Fred_square_with_text.png%3Fraw%3Dtrue" alt="red square" width="800" height="400"&gt;
&lt;/center&gt;

&lt;h3&gt;
  
  
  args.inputs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;args.inputs&lt;/code&gt; is used for keyboard, mouse, and gamepad input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mouse Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This code initializes a &lt;code&gt;click_count&lt;/code&gt; to 0 and displays the total clicks to the &lt;br&gt;
screen. Every time the user clicks and releases their mouse button, the &lt;code&gt;click_count&lt;/code&gt; is increased by 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; args.state.click_count ||= 0

 args.outputs.labels &amp;lt;&amp;lt; [100, 240, "Click count: #{args.state.click_count}", 0, 0, 0]

 args.state.click_count += 1 if args.inputs.mouse.up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Keyboard Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This code initializes a player's (x,y) position to (100, 100) and creates a red square (just like above) at those coordinates. Then, it increases or decreases the (x,y) values by 1, depending on which directional key was pressed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def tick args
  args.state.player.x ||= 100
  args.state.player.y ||= 100
  args.outputs.solids &amp;lt;&amp;lt; [args.state.player.x, args.state.player.y, 100, 100, 255, 0, 0]

  args.state.player.x += 1 if args.inputs.keyboard.d
  args.state.player.x -= 1 if args.inputs.keyboard.a
  args.state.player.y += 1 if args.inputs.keyboard.w
  args.state.player.y -= 1 if args.inputs.keyboard.s
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;There is a lot more to DragonRuby that I haven't even touched on. If you're interested in trying it out I highly recommend you check out the docs &lt;a href="http://docs.dragonruby.org/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and &lt;a href="https://dragonruby-docs.readthedocs.io/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, which also link to some tutorial videos. There is also a free &lt;a href="https://wndx.school/p/dragon-ruby-game-toolkit-tutorial" rel="noopener noreferrer"&gt;course&lt;/a&gt; you can check out to get started.&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href="https://itch.io/jam/teenytiny-dragonruby-minigamejam-2020" rel="noopener noreferrer"&gt;link&lt;/a&gt; to the Game Jam if you'd like to join. You can also claim a &lt;a href="http://teenytiny.dragonruby.org/" rel="noopener noreferrer"&gt;free Game Jam license&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dragonruby</category>
      <category>ruby</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>My Experience Entering JS13k</title>
      <dc:creator>Jess</dc:creator>
      <pubDate>Tue, 15 Sep 2020 01:58:57 +0000</pubDate>
      <link>https://forem.com/robotspacefish/my-experience-entering-js13k-80i</link>
      <guid>https://forem.com/robotspacefish/my-experience-entering-js13k-80i</guid>
      <description>&lt;p&gt;I found out about &lt;a href="https://js13kgames.com/" rel="noopener noreferrer"&gt;JS13k&lt;/a&gt;, a competition to make a JavaScript/HTML5 game in under 13kb, back in July. &lt;/p&gt;

&lt;p&gt;I've always wanted to make games; it's a big part of why I got into programming. However, being in the midst of a job search, I feel like I never have enough time to get into it since I'm trying to keep up with learning all of the web technologies and frameworks, practice algorithms, etc. My "hobby" has to sit on the sidelines.&lt;/p&gt;

&lt;p&gt;I figured since I primarily code in JavaScript I wouldn't have to overwhelm myself trying to quickly learn a bunch of extra stuff on top of my already stacked list of things I'm trying to learn, so I could definitely try to enter. The competition lasted 1 month, from August 13th - September 13th. &lt;/p&gt;

&lt;h2&gt;
  
  
  Theme / Brainstorming
&lt;/h2&gt;

&lt;p&gt;This year's theme was 404, as in the Not Found error. I came up with a concept that would be sort of like a point-and-click but I scrapped it because I didn't have a lot of time to come up with puzzles and a story. Then I thought, what about a killer robot with a missing kill.exe file? The player could play as the robot being swarmed by some type of enemy and they would need another way to defeat them. How about bringing them food? I figured I could make a game where food spawns in random locations as enemies swarm around and you have to bring them whichever food they want. &lt;/p&gt;

&lt;center&gt;
    
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fjs13k%2Fidea1.png%3Fraw%3Dtrue" alt="thumbnail sketches of robot being swarmed by enemies and bringing them food" width="714" height="1156"&gt;
    
&lt;/center&gt;

&lt;p&gt;Then I thought, what about that old Bugs Bunny game Crazy Castle? I could make something like that, but instead of avoiding enemies and collecting carrots, you find and bring them the foods they want. If you come into contact with them without the correct food you lose. I really liked this idea, but again I didn't have a lot of time to come up with a bunch of different levels. Finally I thought, what about something like Pac-Man where there is a single map? This idea worked for me; there could be a "food court" and the player could zip around snagging the right food for the right enemies. Oh yeah, I decided the enemies would be aliens who really like tacos and donuts.&lt;/p&gt;

&lt;center&gt;
    
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fjs13k%2Fidea2.png%3Fraw%3Dtrue" alt="thumbnail sketches of game level similar to Bugs Bunny Crazy Castle" width="800" height="400"&gt;
    
&lt;/center&gt;

&lt;center&gt;
    
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fjs13k%2F54994-Bugs_Bunny_Crazy_Castle%2C_The_%28USA%29-3.jpg%3Fraw%3Dtrue" alt="thumbnail sketches of robot being swarmed by enemies and bringing them food" width="523" height="437"&gt;Bugs Bunny Crazy Castle  (image from Emuparadise)
        
    
&lt;/center&gt;

&lt;h2&gt;
  
  
  Creating the Game Art
&lt;/h2&gt;

&lt;p&gt;After settling on an idea I used &lt;a href="https://www.aseprite.org/" rel="noopener noreferrer"&gt;Aseprite&lt;/a&gt; to make my game art. I don't have a lot of pixel art experience so I was just kind of winging it. Thankfully Aseprite is fairly easy and fun to use so I was able to come up with designs that I liked.&lt;/p&gt;

&lt;center&gt;
    
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fjs13k%2F404_spritesheet.png%3Fraw%3Dtrue" alt="thumbnail sketches of robot being swarmed by enemies and bringing them food" width="800" height="400"&gt;This is my final spritesheet. So small, so cute.
        
    
&lt;/center&gt;

&lt;h2&gt;
  
  
  Code Time
&lt;/h2&gt;

&lt;p&gt;I decided to make everything from scratch, as opposed to using one of the game engines from the JS13k resource page. I just felt like even if I didn't come up with the best game, I wanted to learn how to make everything myself. &lt;/p&gt;

&lt;h3&gt;
  
  
  Organization
&lt;/h3&gt;

&lt;p&gt;I created a &lt;code&gt;main.js&lt;/code&gt; where I handled the main game loop as well as any event listeners and a few global variables to instantiate a game object and to import sprites and sounds. Depending on the current &lt;code&gt;game.state&lt;/code&gt;, &lt;code&gt;gameLoop()&lt;/code&gt; either displayed the title screen, game over screen, or called &lt;code&gt;requestAnimationFrame&lt;/code&gt; and looped &lt;code&gt;game.update()&lt;/code&gt; (which handled game logic) and &lt;code&gt;game.draw()&lt;/code&gt; (which handled drawing to the &lt;code&gt;canvas&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Just about every other class, such as Player, Enemy, Food, EnemySpawn, etc extended off of a &lt;code&gt;GameObject&lt;/code&gt; class which handled collisions, animations, and variables like x,y, width, height, and the source location and size of each object on the sprite sheet. I'll probably eventually go back and clean some of this up, because many of the child classes don't do anything special or different. It was just easier in the moment to create the child class and keep track of all objects of a certain type using static class variables. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GameObject&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./GameObject.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FoodCourt&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;GameObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;srcH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;FoodCourt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way I could just use &lt;code&gt;FoodCourt.all&lt;/code&gt; to loop through only food courts when the player was within a certain range to check for collisions as opposed to looping through all game objects. I could probably go  back and just use &lt;code&gt;static foodCourts = []&lt;/code&gt; in the &lt;code&gt;GameOject&lt;/code&gt; class, and push all newly instantiated foodCourts to that instead of creating a whole child class. &lt;/p&gt;

&lt;center&gt;
    
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fjs13k%2Fkillexe_dryeraseboard.png%3Fraw%3Dtrue" alt="thumbnail sketches of robot being swarmed by enemies and bringing them food" width="800" height="400"&gt;Planning collision distance to the food courts on my dry erase board
        
    
&lt;/center&gt;

&lt;h2&gt;
  
  
  Struggles
&lt;/h2&gt;

&lt;p&gt;I made my sprites 16x16 or smaller and therefore they needed to be scaled up. I also wanted the entire game map to scale depending on window size but it needed to stay in proportion. I spent a lot of time struggling with this. I got the sprites to scale up but then the collisions weren't working correctly and my character kept shooting off the sides of the screen.  I did some research and found that basically you're dealing with 2 canvas sizes: the native height and width used for all your game logic, and the canvas height and width used for display. If you're curious how I went about this, I wrote a post &lt;a href="https://dev.to/robotspacefish/resizing-html5-canvas-and-scaling-sprites-5cpe"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As the deadline was swiftly approaching I had to cut a lot of my ideas and just get something working. I added mobile controls, which are okay but could be a lot better, and I was able to create a few sounds using &lt;a href="http://github.grumdrig.com/jsfxr/" rel="noopener noreferrer"&gt;jsfxr&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The game was due at 7AM my time (13:00CEST) on Sunday, September 13. I was as done as I was going to be at around 9:30PM Saturday night. I had used some tips I found in a &lt;a href="https://dev.to/yvonnickfrin/setting-up-the-development-environment-20f2"&gt;post&lt;/a&gt; by Yvonnick FRIN to compile and zip the game and it was under the required 13kb. Yay! &lt;/p&gt;

&lt;p&gt;I started to fill out the form to submit my game and I ran into an error because I didn't have the index.html in the root. It was in &lt;code&gt;src&lt;/code&gt; along with all my js files, which is not how I normally organize my file structure, but that's how the guide told me to do it. I didn't follow the entire guide though, so maybe I missed something further on. Anyway, I re-organized the file structure and I could no longer get the game to find my images and sounds. Then when I got it working it wouldn't compress to under 13kb anymore. I was very confused. I tried multiple methods to compress and get everything working. I even copied all my code from separate files into 1 file and tried online minifiers, and I tried another &lt;a href="https://github.com/mtmckenna/js13kgames-parcel-starter" rel="noopener noreferrer"&gt;JS13k&lt;/a&gt; starter that has scripts to do some high file compression and zip for you. Nothing I did was working and I couldn't figure out why. I dug into the scripts and started trying to learn what they were doing, how &lt;a href="https://www.archiverjs.com/" rel="noopener noreferrer"&gt;Archiver&lt;/a&gt; works, etc. &lt;/p&gt;

&lt;p&gt;At around 4:00AM I was feeling defeated and I was preparing to give up and just be happy that I made a game. I closed my computer and went to wash up for bed. While I was brushing my teeth I thought about how  the first time I zipped the build it was fine, except for the file structure being wrong. I decided I would go back to my computer and give it one last-ditch effort by reverting back to that first attempt and using what I learned by messing around with the scripts. It...worked... IT WORKED! I can't believe I was ready to give up, but I'm so glad I didn't.&lt;/p&gt;

&lt;center&gt;
    
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fjs13k%2Ftitlescreen.png%3Fraw%3Dtrue" alt="robot gif" width="934" height="879"&gt;
    
&lt;/center&gt;

&lt;center&gt;
    
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frobotspacefish%2Fblog-photos%2Fblob%2Fmaster%2Fjs13k%2F404robot-export.gif%3Fraw%3Dtrue" alt="robot gif" width="48" height="48"&gt;
    
&lt;/center&gt;

&lt;p&gt;You can play it here if you'd like: &lt;a href="https://js13kgames.com/entries/killexe-not-found" rel="noopener noreferrer"&gt;Kill.exe Not Found&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>gamedev</category>
      <category>js13k</category>
    </item>
  </channel>
</rss>
