O'Reilly    
 Published on O'Reilly (http://oreilly.com/)
 See this if you're having trouble printing code examples


Creating Games in Ruby (Part 2)

by Andrea O. K. Wright
12/18/2007

Part One of this two-part series begins with the question: Can playing games written in Ruby be as much fun as writing them, or do they run too slowly, with too many breaks in the action at inopportune times? I go over some reasons why it makes sense to ask that question, and I begin to answer by way of kicking off a survey of the Ruby game development landscape. I start with lower-level libraries and progress through higher-level frameworks. The tour continues in Part Two. I will conclude by revisiting the question I posed at the beginning of the series.

Gosu
Co-Creators: Julian Raschke & Jan Lucker
Lead Developer: Julian Raschke

Gosu was written in C++, and a tool called SWIG (Simplified Wrapper and Interface Generator) is used to generate the C++ extension for Ruby. But just because the primary development language for Gosu is C++, it does not mean that the Ruby version is given short shrift. Anything that doesn't have a native Ruby feel is adjusted with custom SWIG directives.

Gosu is polished and compact. Its development team aims to include everything you need to create a game, but nothing more. Every feature it supports was needed for and tested in an existing application. Nothing was added because someone might find it useful in theory.

In keeping with the design rationale behind the code, Gosu is packaged with a well-thought-out tutorial that provides everything you need to get started with Gosu, but nothing more. It takes less than an hour to complete, but at the end of that hour you're ready to start developing games.

Here's what the finished tutorial looks like. When you steer the ship into a star, the star vanishes and you get points.

Tutorial packaged with Gosu
Figure 1. Tutorial packaged with Gosu

Even experienced Gosu developers start out by subclassing Gosu's Window class and taking advantage of its built-in services. Here's a skeletal main window:

class MainWindow < Gosu::Window
  def initialize(width, height, fullscreen)
    super
  end
  
  def update
    # Change display attributes, if necessary.
  end
 
  def draw
    # Display calls go here.
  end

  def button_down(id)
    # Buttons are keys, mouse buttons, and 
	# game pad controls. 
  end
end

You don't need to start a main game loop or set up an event queue in your application-level code because Gosu handles that behind the scenes. Gosu will call the main window's update method and then its draw method, every frame. When the user presses a key on the keyboard or a mouse button, the button_down callback is invoked.

Because Gosu is so streamlined, many developers have found that it's well-suited for timed game development competitions. Below is a video clip of a game that was created for a competition by a team of developers, including Florian Gross, Alexander Post, and Gosu co-creator and lead developer Julian Raschke (click on the screenshot to view the video clip). It was created in 72 hours. The version shown here was polished a little after the competition ended.

The object of this game is to lure the sleepwalking witch back to bed, primarily using chocolate as bait. The fairy is actually the mouse cursor, and if you click on the chocolate bar, a tiny chocolate bar will appear in her hands. If you feed her a hot chili pepper and scare her with a plush orc toy in her sleep, she becomes red-tinted and infused with firepower that enables her to zap anything the gets in her way. You can feed her an ice cream cone when she needs to throw ice bolts to neutralize fiery cauldrons or turn pools of water into bridges of ice. On some levels you need to scare her with a spider so she will hit a light switch when she tries to swat the spider away. If lights are left on, they could potentially wake her up.

Video thumbnail. Click to play
Click to watch a game created for a timed game development competition

Gosu was built to be easily integrated with external libraries. It is packaged with a tutorial that shows how Gosu can be integrated with Scott Lembcke's 2D physics library, Chipmunk. Chipmunk is written in C, but it is distributed with its own Ruby bindings. Chipmunk enables you to imbue your sprites with virtual mass so that when they collide, they behave according to the laws of natural physics. The tutorial not only shows how to use Chipmunk to handle collision detection, it shows how nicely Gosu plays with others.

The Gosu/Chipmunk integration tutorial, which was written by Dirk Johnson, uses the standard Gosu spaceship tutorial as a starting point. There are numerous changes to the internals of the original Gosu tutorial in the Chipmunk-enhanced version. But when you run the application, it looks almost exactly like the original version. The stars disappear before you can see the impact of the collision. I think the most magical thing about Chipmunk is watching the stylized 2D sprites move in a natural way. So I modified the Gosu/Chipmunk integration tutorial by commenting out the code that removes the stars when the spaceship steers into them. That way you could see what happens when the spaceship hits the Chipmunk-enhanced stars. I also made the star field more dense and added a couple of Chipmunk logos. The video clip below shows the sort of thing that can happen when you mix Gosu and Chipmunk (click on the screenshot to view the video clip):

Video thumbnail. Click to play
Click to watch a video clip of a game based on Dirk Johnson's Gosu/Chipmunk tutorial

Integration points for RMagick were recently built into Gosu after users requested the ability to dynamically modify the landscape during a game. The video clip below shows a sample app that uses special effects that are not possible without RMagick. RMagick comprises the Ruby bindings for the ImageMagick libraries, which can be used to transform or combine images and apply dozens of special effects, like mirroring, flipping and even emulating a watermark or Polaroid instant picture. In the video clip below (click on the screenshot to view the video clip), the two toy soldiers take turns blasting holes in the landscape, revealing more of the sky in the background.

Video thumbnail. Click to play this video clip.
Click to watch a video clip of a game that uses RMagick with Gosu

For comparison's sake here are a couple of screenshots of a game packaged with the RUDL library that was covered in Part One. It is similar in that it also involves blasting holes in the landscape. It features tanks instead of toy soldiers. The little square boxes are the tanks. Its remove_dirt method makes it look like dirt was displaced by rendering a black circle where the projectile dropped. This only works with a black background. With an ImageMagick-based implementation, there are no such restrictions. RMagick integration opens up so many possibilities.

Demo game packaged with RUDL
Figure 2. Progressive screenshots of a demo game packaged with RUDL (written by Brian Palmer, Pocket Martion Software)

Below is the code from the Gosu demo that makes it look like craters with charred rims are left in the earth or the star. In the top code block, circle and fill create a black circle, and shadow creates a blurred version of the circle. The second code block shows how these shapes are used in conjunction with the RMagick composite! method to complete the effect. After each shot is fired, the application loops through the tiles that comprise the background (@rmagick_images) and determines which ones were near the blast. The sequence of calls in the second code block is made for each tile that was "hit." The shadow is placed on the background image by passing the Atop flag to composite!. Next the DstOutcompositeOp flag is passed to composite! to add the smaller smoother crater image. The DstOutcompositeOp flag indicates that a space the size and shape of the image should be erased.

if not @crater_image then
  @crater_image = Magick::Image.new(50, 50)
    {self.background_color = 'none'}
  gc = Magick::Draw.new
  gc.fill('black').circle(25, 25, 25, 0)
  gc.draw(@crater_image)
  @crater_shadow = @crater_image.shadow(0,0,5,1)   
end

Circle and its shadow
Figure 3. The circle and its shadow created with calls to circle() and shadow()

@rmagick_images[index].composite!(@crater_shadow,
  center_x - 35, center_y - 35,  
  Magick::AtopCompositeOp)

@rmagick_images[index].composite!(@crater_image,
  center_x - 25, center_y - 25, 
  Magick::DstOutCompositeOp)

A tile with a solid shadow and a tile with an
Figure 4. A tile with a solid shadow and a tile with an "erased" circle

The very latest release of Gosu features another variation on the standard Gosu tutorial. The new demo (Figure 5) incorporates a 3D background, courtesy of OpenGL. Gosu has long used OpenGL for rendering behind the scenes (recently for Windows, longer for other platforms), shielding developers from the OpenGL API. Now developers can access the OpenGL API selectively to add 3D effects to their 2D games.

 The standard Gosu tutorial with a 3D background
Figure 5. The standard Gosu tutorial with a 3D background

The first code fragment below shows that a block of OpenGL API calls can be passed to the new gl method. Most of the OpenGL API calls are encapsulated in the exec_gl method, which creates the mountain range effect by displaying the same texture over and over with a random height.

The bottom fragment is excerpted from the exec_gl code. The background is composed of multiple triangles, which have vertexes in common. Instead of making a separate set of glVertex3d calls for each surface, which would result in specifying the same vertex more than once, the GL_TRIANGLE_STRIP OpenGL API specifier is used. With GL_TRIANGLE STRIP you declare 3 or more vertexes and OpenGL takes care of connecting them all with triangular surfaces. This code listing shows just one of the 4 vertexes defined in the application. The scrolling background is composed of multiple GL_TRIANGLE_STRIPs.

def draw
  gl do
    glClearColor(0.0, 0.2, 0.5, 1.0)
    glClearDepth(0)
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    @gl_background.exec_gl
  end   
  ... 
end
def exec_gl
  info = @image.gl_tex_info
  ...
    0.upto(POINTS_Y - 2) do |y|
      0.upto(POINTS_X - 2) do |x|
        glBegin(GL_TRIANGLE_STRIP)
          z = @height_map[y][x]
          glColor4d(1, 1, 1, z)
          glTexCoord2d(info.left, info.top)
          glVertex3d(-0.5 + (x - 0.0) / (POINTS_X-1),
            -0.5 + (y - offs_y - 0.0) / (POINTS_Y-2),
            z)
          ...
        glEnd
      end
    end
end

The OpenGL API method names in the Gosu code look different than the names used in the RUDL example in Part One because Gosu is integrated with the ruby-opengl library, while the RUDL sample uses an older Ruby/OpenGL library. The way the OpenGL API calls are structured, however, is identical in the RUDL example and the Gosu example.

Look for the ability to input text so high scores can be entered and socket support in future Gosu releases.

Ogre.rb
Creator and Lead Developer: Jason Roelofs

Shattered Ruby
Co-Creators and Co-Lead Developers: Martyn Garcia and Mikkel Garcia

Given what it takes to hand code 3D effects using OpenGL, I'm sure you can see why people use 3D modeling software for more complex characters and scenes. Now we're going to explore two Ruby projects that leverage full-blown 3D models: Ogre.rb, a 3D graphics rendering engine, and Shattered Ruby, a 3D game development framework that is built on top of Ogre.rb.

Ogre.rb is a Ruby wrapper around OGRE, which stands for Object-Oriented Graphics Rendering Engine. It is used for combining 3D models, textures, and other kinds of graphical content into a scene. There are ways to incorporate special effects including wind, rain, smoke, photo-realistic explosions, and sophisticated lighting techniques.

Ogre.rb comes with a testing helper that sets up a generic scene manager and leverages class variables so that a new window does not need to instantiated for each test.

Below is a video clip (click on the screenshot to view the video clip) from one of the sample scenes packaged with Ogre.rb. It's actually a Ruby port of one of the demos packaged with OGRE. The green ogre head is an example of a 3D model. Using arrow keys or the mouse, you can navigate around the screen and view the ogre head from the side or from the back. The front is not just a facade. It's fully rendered in fine detail 360 degrees around.

Video thumbnail. Click to play
Click to watch a video clip of a Ruby port of an OGRE demo that is packaged with Ogre.rb

OGRE is written in C++, and Ogre.rb creator and lead developer Jason Roelofs initially used SWIG to help generate the C++ extensions for Ruby. Because SWIG does not provide support for wrapping nested classes, however, he found he could not move forward with Ogre.rb after he finished wrapping the main OGRE classes. After researching different binding strategies, he decided to put Ogre.rb development on hold to build a Ruby/C++ interoperability suite in the mold of luabind and Boost.Python. He plans to rebuild Ogre.rb from scratch using the new toolset. In the meanwhile, he will continue to support the most recent release of Ogre.rb.

Shattered Ruby is a 3D game development framework inspired by Ruby on Rails and built on top of Ogre.rb.

Somehow, the creators and lead developers of Shattered Ruby, Martyn and Mikkel Garcia, find ways to talk about every aspect of 3D game development in terms of Ruby on Rails. For example, there's a post in the Shattered Ruby forum in which Martyn manages to liken Shattered to Rails, even as he's pointing out a way in which Rails and Shattered are not only different, but diametrically opposed. He points out that the Model in Rails is more complex than the typical Shattered Model, while the View in Shattered, which is typically a 3D View, is way more complex than the View in a Rails app. He concludes that Rails gets a lot of mileage out of being able to make assumptions about the Model, while Shattered can do the same with its Views.

Shattered can reduce the amount of code needed to describe a scene considerably by making assumptions about where the media files can be found in the application directory structure and what they are called, about how objects created from a View are likely to be grouped, and about the relationship between the View and the Model. Below is a code snippet from one of the sample apps that comes with Ogre.rb, followed by a code snippet that shows how the same scene directives could be expressed using Shattered.

# from an Ogre.rb demo
class SkyPlaneApplication < Application
  def create_scene 
    scene_manager.set_ambient_light ColourValue.new(0.5,
      0.5, 0.5)
	plane = Plane.new
	plane.d = 5000
	plane.normal = -Vector3.UNIT_Y
	scene_manager.set_sky_plane(true,
	plane,"SpaceSkyPlane", 10000, 3)
	light = scene_manager.create_light("MainLight")
	light.set_position(20, 80, 50)
	dragon = scene_manager.create_entity("dragon",
	  "dragon.mesh")
	scene_manager.root_scene_node.attach_object(dragon) 
  end
end
# using Shattered Ruby to define the above scene...
class SkyPlane
  light "Mainlight", 
        :position => (20,80,50)
end

class Dragon
end

But Shattered is not only about reducing code size, it's also about having fun with the code you do write and being more expressive.

Vectors are most commonly created by passing the three coordinates to v: an x coordinate, a y coordinate, and a z coordinate. I like the way Shattered provides a core extension for Symbol to add to_v support for symbols that describe changing direction. The expression :backward.to_v evaluates to v(0,0,-1)and :up.to_v evaluates to v(0,1,0).

You begin building a game by typing the command shatter and the name of your game. Shattered Ruby generates the following directory structure, which will be very familiar to anyone familiar with Ruby on Rails:

Shattered Ruby directory structure
Figure 6. Shattered Ruby directory structure

When you type script/generate actor, and the name of your Actor, View code, and Model code is generated. An Actor is made up of a Model and a View. The Model contains the game logic and the user input handlers. When a method is called on the Model, and a method with the same name exists on the View, the View's method is automatically invoked when the corresponding Model method finishes executing. If you're playing 3D checkers and crown is called on a particular checker's Model, crown will automatically be invoked on that checker's View if it's defined. Most likely it's defined to top that checker with another checker.

OGRE material scripts can be used to control textures, light settings, and positioning for 3D models. They can be difficult to format by hand, but Shattered Ruby supplies a mechanism for using erb to generate scripts with the OGRE material format.

Here's a sample Shattered template that takes care of formatting material scripts automatically:

material < %= name % >
{
   technique 
   { 
      pass 
      { 
        lighting off
        texture_unit 
        { 
            texture < %= texture % >
        } 
     }
  }
}

Martyn and Mikkel are currently at the tail end of the refactoring effort they began last year, and the most of the tutorials on the Shattered Ruby Wiki still need to be updated to reflect the changes.

One main reason for the refactoring was to make everything supported by Ogre.rb available to Shattered developers. When the framework was first written, only wrappers for the subset of Ogre.rb features that were needed for Shattered's game DSL were packaged with Shattered. On the Shattered blog, Mikkel explains this decision in terms of Ruby on Rails. He explains that Shattered and Ogre.rb are analogous to ActiveRecord and Rails. Just as Rails developers have the option of using straight SQL when there's no ActiveRecord support for something they need to do, Shattered developers can use straight Ogre.rb calls when Shattered doesn't handle something out of the box. At this point, the development branch of Shattered Ruby is fully integrated with the latest release of Ogre.rb, 0.2.

Last year it was possible to build this 3D version of Tetris by following a tutorial on the Shattered Ruby Wiki:

 Tetris built with Shattered Ruby
Figure 7. Tetris built with Shattered Ruby

The best way to find out when the Shattered Ruby tutorials have been updated and when the next release is out is to join the Shattered Ruby Google Group.

The next release of Shattered Ruby will not just be a refactored version of last year's model. New features that are already checked into the development branch include "3D sound" (volume automatically increases and decreases as figure moves forward or backwards), high level helpers for using the Chipmunk physics library, and integration with the Navi library for building GUIs with HTML syntax on panels in 3D space.

Railgun
Creator and Lead Developer: David Koontz

Like Shattered Ruby, Railgun is a 3D framework that aims to provide a Rails-like development experience. It's JRuby-based and wraps JMonkeyEngine, a 3D game framework written in Java. It is in it's beginning stages, but I think it will be interesting project to watch.

Its creator, David Koontz, also created Monkeybars, which leverages JRuby to create Swing GUIs.

The GGZ Gaming Zone Project
Founders: Brent M. Hendricks and Rich Gade
Developed and Maintained by an International Team

The GGZ Gaming Zone has recently bolstered its support for Ruby, which is both indicative of Ruby's rising profile in the gaming space, and something that has the potential to further improve Ruby's standing as a game programming language. As the official online game development platform for both KDE and Gnome, GGZ is now packaged with most Linux distros.

GGZ is a recursive acronym, like GNU. Actually, when GGZ was founded around eight years ago, it was initially called Gnu Gaming Zone, or GGZ. The founders anticipated becoming part of the GNU project, but when they later decided against that, they decided to go with the name GGZ Gaming Zone.

The GGZ Gaming Zone project includes a lot of useful resources for game developers, like protocols for deploying your game over a network and libraries that provide services ranging from sign in to reserving seats to saving high scores. The GGZ source repository also contains dozens of games and a web portal designed for gaming communities, complete with RESTful APIs for accessing information like tournament schedules and player rankings. The portal software is running on the GGZ community site, where you can find current tournament schedules. Spades is particularly popular. There are Spades tournaments on a regular basis.

You don't need to install anything or even sign up for an account to play the games installed on the GGZ community site. Just hit the "Play Now" button, and log on as a guest. If you install GGZ software on your computer and run the core launching client, you can choose the GGZ community server to play the games installed there, you can choose the GGZ server on your own local machine, the GGZ server on a friend's machine... or any number of other public GGZ servers running all over the world.

The core clients packaged with GGZ are basically chat clients that can launch games. The core client lists games that are available for launching and provides information about the status of any virtual tables (think "bridge table," not database table) where games are in progress or where people are waiting for additional players to show up. When you launch a multiplayer game, you are prompted to indicate which seats should be open to anyone, which seats should be reserved for particular players, and which should be assigned to the computer. You can join games as a player or a spectator.

Below is a sampling of games packaged with GGZ. GGZ game code is usually broken down into game server code and game client code. GGZ offers more than one client for several of the games. Three very different tic-tac-toe clients are on the bottom row. If there are multiple front ends available, the core launching client will prompt you to pick one.

Games distributed with GGZ
Figure 8. Games distributed with GGZ

This is architecture that makes it easy for GGZ to support multiple game clients and multiple game servers for the same game.

GGZ architecture
Figure 9. GGZ architecture

GGZ publishes protocols that define message types and describe the kinds of data that must accompany each message type for communication between clients and servers. For example, GAME_SPECTATOR_SEAT is a type of message a core client can send to a game client to indicate that a user wants to join the game as a spectator. The protocol specifies that the seat id (an integer) and the player name (a String) must be sent with a GAME_SPECTATOR_SEAT message.

For everything other than the communication between a game client and a game server, GGZ provides utility libraries that encapsulate both the GGZ-specific protocols and the network protocols used to send the messages. These libraries are represented by the highlighted green text in the diagram. C is the primary development language for GGZ, but there are Ruby bindings for these libraries. The Ruby bindings as well as a high-level pure Ruby wrapper for one of the Ruby C extensions are highlighted in red.

Since each individual game has a different protocol for communication between the game client and the game server, GGZ can't offer a pre-packaged library that will work for every game. What it does offer, instead, is a flexible code generation utility called ggzcomgen. You define your game protocol in an XML document and specify your network protocol as a command line argument, and the utility will generate a file that contains a method for each message type in your game protocol that uses the specified network protocol to send the appropriate data types over the network. The generated networking code is represented as net.rb and highlighted in Ruby red in the diagram, but the utility can generate Python and C source as well as s Ruby.

The code generator itself, ggzcommgen, is also highlighted in red because it is written in Ruby. It is the only part of the GGZ infrastructure that is written in Ruby.

Most of GGZ's Ruby bindings were added within the last 6 months. As a proof of concept, GGZ lead developer Josef Spillner wrote a tic-tac-toe game server called Rubytoe in Ruby and a rudimentary tic-tac-toe client using QtRuby.

QtRuby tic-tac-toe client
Figure 10. QtRuby tic-tac-toe client

Both the experimental Ruby client and the experimental Ruby server are pretty basic, but they signal the potential for a whole GGZ Ruby package. There's currently a GGZ Python package, which is tightly integrated with Pygame. GGZ developers are thinking about integration with a Ruby game programming framework, possibly one of the frameworks covered in this series!

I will conclude my survey of Ruby game development resources with that thought.

Can playing games written in Ruby be as much fun as writing them, or do they run too slowly, with too many breaks in the action at inopportune times?
Is Ruby a legitimate player in the gaming space?

As promised at the beginning of Part One, I will now address whether performance issues, including the fact that Ruby's garbage collector is of the so-called stop-the-world variety, can and should stop developers from playing with and working with Ruby game development frameworks. I will also touch on Ruby's status in the commercial video game marketplace.

None of these issues have stopped the developers of the game frameworks covered in this series.

There are several implementations of Ruby in the works that have the potential to make Ruby programs run orders of magnitude faster than they do today. While Ogre.rb author Jason Roelofs is confident that Ruby performance will improve significantly by the time either Ogre.rb or Shattered Ruby are ready for a 1.0 release, he says he has "yet to experience any serious lag from GC" under Ruby 1.8.6. He's seen a "one or two frame stutter."

Martyn and Mikkel Garcia, the authors of Shattered Ruby, force garbage collection every frame. GC has not been a show-stopper for them.

Gosu author Julian Raschke recently tried modifying Gosu to call garbage collection every 10 seconds. But he took out that code before the latest release because he didn't think it made a big difference. He has worked on improving Gosu performance for recent releases, but garbage collection was not one of the issues he identified as major.

And it also did not stop a multimedia company from starting to use the Ruby version of Gosu recently. The company is called Thinking Pictures.

So ... I can't say anyone's building a commercial video game with a Ruby game development framework, but I can say a Ruby game development framework is being used commercially -- which is a start.

Click "START" to begin playing...

Andrea O. K. Wright enjoys organizing weekly Ruby Tuesday tech lunch-and-learns for her colleagues at Chariot Solutions, a consulting firm based in Fort Washington, PA. She has given presentations about developing games with Ruby at several conferences, including RubyConf 2007.


Return to O'Reilly Network Ruby.

Copyright © 2009 O'Reilly Media, Inc.