This weekend, I played around with gosu after a talk at RubyConf, making a copy of Missile Command in Ruby. Without some important points from the talk, I would be up the creek, but there were some design patterns not brought up that felt emergent when writing game code.

Use constants for “magic numbers”
You’ll use numbers everywhere.  How many pixels should something move each frame?  What is the Z-index of assets?  I found it useful to put every number into a constant and put them in the same place (where reasonable).  My Utility module ended up looking like this:
module Utility
  FIRING_RATE = 500 # Lower equals higher firing rate
  PROJECTILE_SPEED = 5  # If this gets too high and bullets don't arrive, increase PROJECTILE_PROXIMITY
  PROJECTILE_PROXIMITY = 2
  CURSOR_SPEED = 4
  EXPLOSION_SPEED = 2
  SPACESHIP_SPEED = 1
  module ZIndex
    BACKGROUND = 0
    GROUND     = 1
    HEALTH_BAR = 1
    BUNKER     = 2
    SPACESHIP  = 2
    DEFENDER   = 2
    EXPLOSION  = 3
    CURSOR     = 4
    PROJECTILE = 5
    SCORE      = 6
    DEBUG      = 99
  end
end
When parts of the game feel unrealistic (how fast an enemy moves, how big explosions get), all my numbers are in one place for tweaking.
Draw your hit boxes when debugging collision
I wrote a collision library and, like all parts of my game, I did not test it. When I couldn’t figure out why two objects weren’t registering a collision when they were overlapping, I added a utility function to take an object and draw its width/height around it.
module Utility
  module Debug
    def self.trace(obj, c = nil)
      c ||= Explosion::COLOR
      # Left
      Gosu.draw_line(*obj.top_left, c, obj.top_left[0], obj.bottom_right[1], c, ZIndex::DEBUG, mode = :default)
      # Top
      Gosu.draw_line(*obj.top_left, c, obj.bottom_right[0], obj.top_left[1], c, ZIndex::DEBUG, mode = :default)
      # Right
      Gosu.draw_line(*obj.bottom_right, c, obj.bottom_right[0], obj.top_left[1], c, ZIndex::DEBUG, mode = :default)
      # Bottom
      Gosu.draw_line(*obj.bottom_right, c, obj.top_left[0], obj.bottom_right[1], c, ZIndex::DEBUG, mode = :default)
    end
  end
end
Use keyword args everywhere
Using keyword args makes the message fail in the sender, not the receiver. Usually it’s the sender that’s responsible for sending the wrong arguments, so this shifts the responsibility to the sender by ensuring that every expected argument will be present. Avoid default arguments and nil checks as well.
Abstract common behaviors into modules
This is a given in programming for all applications, but can be super useful when multiple classes behave in more or less the exact same way. If you have 3 types of enemies that are all starships – they change direction when they hit the end of the screen, they fire at a certain rate, they have a particular speed – that can all be abstracted into a module and methods overwritten as needed.
module Mixins
  module Ship
    def self.included(base)
      base.class_eval do
        def damage!(projectile)
          @damage += projectile.damage_value
          if damage >= health
            # Handle being destroyed
          end
        end
        private
        def speed
          # Look up speed from CONSTANT in Utility module
          table_name = "Utility::" + "#{self.class}".split('::').last.upcase + "_SPEED_TABLE"
          Object.const_get(table_name)[@health]
        end
        def health
          # Look up health from CONSTANT in Utility module
          # ...
        end
        def move
          # Move back and forth at speed in direction
        end
      end
    end
  end
end
Then when I make a new class that behaves like a Ship, I just have to include the module, add a speed entry in the Utility CONSTANT, and overwrite methods as necessary:
module Enemy
  class Fortress
    include Mixins::Ship
    WIDTH = 60
    HEIGHT = 60
    private
    # Health in this case is a fixed value, always
    def health
      @health ||= 100
    end
    # Change the image
    def image
      @image ||= Gosu::Image.new("assets/death_star.png")
    end
  end
end
Store levels in a collection
In your main loop, delegate the current action (update, draw) to the current_level variable.  Levels should all have a common API, like a .done? method, so in your main loop you say:
current_level = levels.shift if current_level.done?
Generate levels from a config file
Use YML, JSON, or some other format to store information for each level.  When the game loads, instantiate a collection of levels that correspond to entries in the file.  This way, you can easily change what’s in a particular level by modifying the config file.  Like the idea of constants in a Utility module, it’s one place where you can modify far away behavior.
class Levels::Collection
  def initialize(game:)
    @levels = YAML
              .load_file("./config/levels.yml")
              .collect do |_, details|
                Levels::Base.new(game: game, details: details)
              end
  end
end
one:
  spaceships:
    number: 3
  battleships:
    number: 0
  fortresses:
    number: 0
  bunkers:
    number: 1
    ammo: 10
    health: medium
  defenders:
    number: 0
two:
  spaceships:
    number: 5
    height: medium
    weapons: easy
  battleships:
    number: 0
  fortresses:
    number: 0
  bunkers:
    number: 1
    ammo: 10
    health: medium
  defenders:
    number: 0
The main loop of my game just looks like this:
class Game < Gosu::Window
  # ...
  # Called once each frame
  def update
    return if game_over
    current_level.update
    check_game_over
    if !game_over && current_level.over?
      @current_level = levels.shift 
    end
  end
  # ...
end
The code for my game is available on my Github and thanks for reading!
 
      
    