Let’s Build a Simple Video Game with JRuby: A Tutorial »

Created at: 15.12.2011 02:34, source: Ruby Inside, tagged: Cool jruby Tutorials

Ruby isn't known for its game development chops despite having a handful of interesting libraries suited to it. Java, on the other hand, has a thriving and popular game development scene flooded with powerful libraries, tutorials and forums. Can we drag some of Java's thunder kicking and screaming over to the world of Ruby? Yep! - thanks to JRuby. Let's run through the steps to build a simple 'bat and ball' game now.

The Technologies We'll Be Using

JRuby

If you're part of the "meh, JRuby" brigade, suspend your disbelief for a minute. JRuby is easy to install, easy to use, and isn't going to trample all over your system or suck up all your memory. It will be OK!

One of JRuby's killer features is its ability to use Java libraries and generally dwell as a first class citizen on the JVM. JRuby lets us use performant Java powered game development libraries in a Rubyesque way, lean on Java-based tutorials, and basically have our cake and eat it too.

To install JRuby, I recommend RVM (Ruby Version Manager). I think the JRuby core team prefer you to use their own installer but rvm install jruby has always proven quick and effective for me. Once you get it installed, rvm use jruby and you're done.

Slick and LWJGL

The Slick library is a thin layer of structural classes over the top of LWJGL (Lightweight Java Game Library), a mature and popular library that abstracts away most of the boring system level work.

Out of the box LWJGL gives us OpenGL for graphics, OpenAL for audio, controller inputs, and even OpenCL if we wanted to do heavy parallelism or throw work out to the GPU. Slick gives us constructs like game states, geometry, particle effects, and SVG integration, while allowing us to drop down to using LWJGL for anything we like.

Getting Started: Installing Slick and LWJGL

Rather than waste precious time on theory, let's get down to the nitty gritty of getting a basic window and some graphics on screen:

  • First, create a folder in which to store your game and its associated files. From here I'll assume it's /mygame
  • Go to the Slick homepage and choose "Download Full Distribution" (direct link to .zip here).
  • Unzip the download and copy the lib folder into your /mygame as /mygame/lib - this folder includes both LWGWL and Slick.
  • In /mygame/lib, we need to unpack the natives-[your os].jar file and move its contents directly into /mygame.

    Mac OS X: Right click on the natives-mac.jar file and select to unarchive it (if you have a problem, grab the awesome free The Unarchiver from the App Store) then drag the files in /mygame/lib/native-mac/* directly into /mygame.

    Linux and Windows: Running jar -xf natives-linux.jar or jar -xf natives-win32.jar and copying the extracted files back to /mygame should do the trick.

  • Now your project folder should look a little like this:

    If so, we're ready to code.

A Bare Bones Example

Leaping in with a bare bones example, create /mygame/verybasic.rb and include this code:

$:.push File.expand_path('../lib', __FILE__)

require 'java'
require 'lwjgl.jar'
require 'slick.jar'

java_import org.newdawn.slick.BasicGame
java_import org.newdawn.slick.GameContainer
java_import org.newdawn.slick.Graphics
java_import org.newdawn.slick.Input
java_import org.newdawn.slick.SlickException
java_import org.newdawn.slick.AppGameContainer

class Demo < BasicGame
  def render(container, graphics)
    graphics.draw_string('JRuby Demo (ESC to exit)', 8, container.height - 30)
  end

  # Due to how Java decides which method to call based on its
  # method prototype, it's good practice to fill out all necessary
  # methods even with empty definitions.
  def init(container)
  end

  def update(container, delta)
    # Grab input and exit if escape is pressed
    input = container.get_input
    container.exit if input.is_key_down(Input::KEY_ESCAPE)
  end
end

app = AppGameContainer.new(Demo.new('SlickDemo'))
app.set_display_mode(640, 480, false)
app.start

Ensure that ruby actually runs JRuby (using ruby -v) and then run it from the command line with ruby verybasic.rb. Assuming all goes well, you'll see this:

If you don't see something like the above, feel free to comment here, but your problems most likely orient around not having the right 'native' libraries in the current directory or from not running the game in its own directory in the first place (if you get probable missing dependency: no lwjgl in java.library.path - bingo).

Explanation of the demo code

  • $:.push File.expand_path('../lib', __FILE__) pushes the 'lib' folder onto the load path. (I've used push because my preferred << approach breaks WordPress ;-))
  • require 'java' enables a lot of JRuby's Java integration functionality.
  • Note that we can use require to load the .jar files from the lib directory.
  • The java_import lines bring the named classes into play. It's a little like include, but not quite.
  • We lean on Slick's BasicGame class by subclassing it and adding our own functionality.
  • render is called frequently by the underlying game engine. All activities relevant to rendering the game window go here.
  • init is called when a game is started.
  • update is called frequently by the underlying game engine. Activities related to updating game data or processing input can go here.
  • The code at the end of the file creates a new AppGameContainer which in turn is given an instance of our game. We set the resolution to 640x480, ensure it's not in full screen mode, and start the game.

Fleshing Out a Bat and Ball Game

The demo above is something but there are no graphics or a game mechanic, so it's far from being a 'video game.' Let's flesh it out to include some images and a simple pong-style bat and ball mechanic.

Note: I'm going to ignore most structural and object oriented concerns to flesh out this basic prototype. The aim is to get a game running and to understand how to use some of Slick and LWJGL's features. We can do it again properly later :-)

All of the assets and code files demonstrated here are also available in an archive if you get stuck. Doing it all by hand to start with will definitely help though.

A New Code File

Start a new game file called pong.rb and start off with this new bootstrap code (very much like the demo above but with some key tweaks):

$:.push File.expand_path('../lib', __FILE__)

require 'java'
require 'lwjgl.jar'
require 'slick.jar'

java_import org.newdawn.slick.BasicGame
java_import org.newdawn.slick.GameContainer
java_import org.newdawn.slick.Graphics
java_import org.newdawn.slick.Image
java_import org.newdawn.slick.Input
java_import org.newdawn.slick.SlickException
java_import org.newdawn.slick.AppGameContainer

class PongGame < BasicGame
  def render(container, graphics)
    graphics.draw_string('RubyPong (ESC to exit)', 8, container.height - 30)
  end

  def init(container)
  end

  def update(container, delta)
    input = container.get_input
    container.exit if input.is_key_down(Input::KEY_ESCAPE)
  end
end

app = AppGameContainer.new(PongGame.new('RubyPong'))
app.set_display_mode(640, 480, false)
app.start

Make sure it runs, then move on to fleshing it out.

A Background Image

It'd be nice for our game to have an elegant background. I've created one called bg.png which you can drag or copy and paste from here (so it becomes /mygame/bg.png):

Now we want to load the background image when the game starts and render it constantly.

To load the game at game start, update the init and render methods like so:

def render(container, graphics)
  @bg.draw(0, 0)
  graphics.draw_string('RubyPong (ESC to exit)', 8, container.height - 30)
end

def init(container)
  @bg = Image.new('bg.png')
end

The @bg instance variable picks up an image and then we issue its draw method to draw it on to the window every time the game engine demands that the game render itself. Run pong.rb and check it out.

Adding A Ball and Paddle

Adding a ball and paddle is similar to doing the background. So let's give it a go:

def render(container, graphics)
  @bg.draw(0, 0)
  @ball.draw(@ball_x, @ball_y)
  @paddle.draw(@paddle_x, 400)
  graphics.draw_string('RubyPong (ESC to exit)', 8, container.height - 30)
end

def init(container)
  @bg = Image.new('bg.png')
  @ball = Image.new('ball.png')
  @paddle = Image.new('paddle.png')
  @paddle_x = 200
  @ball_x = 200
  @ball_y = 200
  @ball_angle = 45
end

The graphics for ball.png and paddle.png are here. Place them directly in /mygame.

We now have this:

Note: As I said previously, we're ignoring good OO practices and structural concerns here but in the long run having separate classes for paddles and balls would be useful since we could encapsulate the position information and sprites all together. For now, we'll 'rough it' for speed.

Making the Paddle Move

Making the paddle move is pretty easy. We already have an input handler in update dealing with the Escape key. Let's extend it to allowing use of the arrow keys to update @paddle_x too:

def update(container, delta)
  input = container.get_input
  container.exit if input.is_key_down(Input::KEY_ESCAPE)

  if input.is_key_down(Input::KEY_LEFT) and @paddle_x > 0
    @paddle_x -= 0.3 * delta
  end

  if input.is_key_down(Input::KEY_RIGHT) and @paddle_x < container.width - @paddle.width
    @paddle_x += 0.3 * delta
  end
end

It's crude but it works! (P.S. I'd normally use && instead of and but WordPress is being a bastard - I swear I'm switching one day.)

If the left arrow key is detected and the paddle isn't off the left hand side of the screen, @paddle_x is reduced by 0.3 * delta and vice versa for the right arrow.

The reason for using delta is because we don't know how often update is being called. delta contains the number of milliseconds since update was last called so we can use it to 'weight' the changes we make. In this case I want to limit the paddle to moving at 300 pixels per second and 0.3 * 1000 (1000ms = 1s) == 300.

Making the Ball Move

Making the ball move is similar to the paddle but we'll be basing the @ball_x and @ball_y changes on @ball_angle using a little basic trigonometry.

If you stretch your mind back to high school, you might recall that we can use sines and cosines to work out the offset of a point at a certain angle within a unit circle. For example, our ball is currently moving at an angle of 45, so:

Math.sin(45 * Math::PI / 180)   # => 0.707106781186547
Math.cos(45 * Math::PI / 180)   # => 0.707106781186548

Note: The * Math::PI / 180 is to convert degrees into radians.

We can use these figures as deltas by which to move our ball based upon a chosen ball speed and the delta time variable that Slick gives us.

Add this code to the end of update:

@ball_x += 0.3 * delta * Math.cos(@ball_angle * Math::PI / 180)
@ball_y -= 0.3 * delta * Math.sin(@ball_angle * Math::PI / 180)

If you run the game now, the ball will move up and right at an angle of 45 degrees, though it will continue past the game edge and never return. We have more logic to do!

Note: We use -= with @ball_y because sines and cosines use regular cartesian coordinates where the y axis goes from bottom to top, not top to bottom as screen coordinates do.

Add some more code to update to deal with ball reflections:

if (@ball_x > container.width - @ball.width) || (@ball_y < 0) || (@ball_x < 0)
  @ball_angle = (@ball_angle + 90) % 360
end

This code is butt ugly and pretty naive (get ready for a nice OO design assignment later) but it'll do the trick for now. Run the game again and you'll notice the ball hop through a couple of bounces off of the walls and then off of the bottom of the screen.

Resetting the Game on Failure

When the ball flies off of the bottom of the screen, we want the game to restart. Let's add this to update:

if @ball_y > container.height
  @paddle_x = 200
  @ball_x = 200
  @ball_y = 200
  @ball_angle = 45
end

It's pretty naive again, but does the trick. Ideally, we would have a method specifically designed to reset the game environment, but our game is so simple that we'll stick to the basics.

Paddle and Ball Action

We want our paddle to hit the ball! All we need to do is cram another check into update (poor method - promise to refactor it later!) to get things going:

if @ball_x >= @paddle_x and @ball_x < = (@paddle_x + @paddle.width) and @ball_y.round >= (400 - @ball.height)
  @ball_angle = (@ball_angle + 90) % 360
end

Note: WordPress has borked the less than operator in the code above. Eugh. Fix that by hand ;-)

And bingo, we have it. Run the game and give it a go. We have a simple, but performant, video game running on JRuby.

If you'd prefer everything packaged up and ready to go, grab this archive file of my /mygame directory.

What Next?

Object orientation

As I've taken pains to note throughout this article, the techniques outlined above for maintaining the ball and paddle are naive - an almost C-esque approach.

Building separate classes to maintain the sprite, position, and the logic associated with them (such as bouncing) will clean up the update method significantly. I leave this as a task for you, dear reader!

Stateful Games

Games typically have multiple states, including menus, game play, levels, high score screens, and so forth. Slick includes a StateBasedGame class to help with this, although you could rig up your own on top of BasicGame if you really wanted to.

The Slick wiki has some great tutorials that go through various elements of the library, including a Tetris clone that uses game states. The tutorials are written in Java, naturally, but the API calls and method names are all directly transferrable (I'll be writing an article about 'reading' Java code for porting to Ruby soon).

Packaging for Distribtion

One of the main reasons I chose JRuby over the Ruby alternatives was the ability to package up games easily in a .jar file for distribution. The Ludum Dare contest involves having other participants judge your game and since most participants are probably not running Ruby, I wanted it to be relatively easy for them to run my game.

Warbler is a handy tool that can produce .jar files from a Ruby app. I've only done basic experiments so far but will be writing up an article once I have it all nailed.

Ludum Dare

I was inspired to start looking into JRuby and Java game libraries by the Ludum Dare game development contest. They take place every few months and you get 48 hours to build your own game from scratch. I'm hoping to enter for the first time in just a couple of days and would love to see more Rubyists taking part.


more »

RubyDrop: A Dropbox Clone in Ruby »

Created at: 26.11.2010 22:56, source: Ruby Inside, tagged: Cool Miscellaneous

rubydrop.png Ever used Dropbox? It's awesome. A cross-platform (Windows, Mac, Linux, and even mobile) file syncing and backup service with 2GB for free (or 2.25GB if you sign up with this link). Well, if you'd like to roll out your own system on your own infrastructure, send some thanks to Ryan LeFevre, the creator of RubyDrop, an open source Dropbox clone based on Ruby and git.

Cloning Dropbox sounds like a tall order and in reality, "Dropbox clone" is a big stretch. RubyDrop is quite simple under the hood and focuses on the file syncing part of the problem. A Ruby process monitors a folder for changes and uses Git to do the heavy lifting and change management between your clients and a defined central server.

RubyDrop is not without its flaws. What happens when files are changed on multiple nodes and then clash? Your mileage may vary. What's good, though, is that Ryan seems keen to improve the system and has already planned a centralized server and is considering integrating rsync to make file syncing smoother. Ryan is a systems engineer at TwitPic with a background in PHP, JavaScript, and C but RubyDrop is his first Ruby project. I look forward to seeing more both from him and RubyDrop.

[sponsor] MinuteDock is a fast and streamlined time tracking/logging app that makes tracking time and sending invoices painless and easy. Check it out.


more »

RDropbox: A Ruby Client Library for Dropbox »

Created at: 06.05.2010 02:05, source: Ruby Inside, tagged: Cool

Dropbox is a popular file hosting service (4m+ users) that provides synced backup and file hosting to OS X, Windows, and Linux users. You get up to 2GB of space for free. RDropbox is a library by Tim Morgan (of Autumn fame) that takes advantage of the official Dropbox API from Ruby.

With RDropbox you can log into a Dropbox account using OAuth and then upload and download files. A requirement, however, is that you apply for Dropbox API access and are approved, as the API is not fully open to the public without going through the approval process (this appears to be in order to avoid overloading their service). The API was, notably, worked on by notable Ruby alumnus, Zed Shaw.

Once you've made it into the Dropbox API program, RDropbox gives you the benefit of writing code as simple as:

# STEP 1: Authorize the user
session = Dropbox::Session.new('your_consumer_key', 'your_consumer_secret')
puts "Visit #{session.authorize_url} to log in to Dropbox. Hit enter when you have done this."
gets
session.authorize
session.sandbox = true

# STEP 2: Play!
session.upload('testfile.txt')
uploaded_file = session.file('testfile.txt')
puts uploaded_file.metadata.size

uploaded_file.move 'new_name.txt'
uploaded_file.delete

An alternative, unofficial route: tvongaza's DropBox

If you want to be using Dropbox from Ruby right now, there's an alternative: tvongaza/DropBox. This alternative library predates the official Dropbox API and uses the same techniques as the official Dropbox clients instead. For this you'll need to use your Dropbox e-mail username and password to log in (this could be a problem if you want to use third party Dropbox accounts!) and you can then create, delete and rename folders and files, as well as check usage statistics.


more »

3 New Date and Time Libraries for Rubyists »

Created at: 05.05.2010 17:30, source: Ruby Inside, tagged: Compilation Posts Cool Ruby Tricks Tools

In the UK there's a cliché that goes: "You wait hours for a bus, and then three come along at once!" So it went with these three Ruby date and time libraries. They all made an appearance on RubyFlow last week and are all useful in their own ways, depending on how you're working with dates and times.

ice_cube - Fast querying and expansion of event recurrence rules

ice_cube is a library by John Crepezzi that provides "fast querying and expansion of recurrence rules in Ruby." What this means is that you can create schedules powered by date recurrence rules that can be quite complex (e.g. every 4 years on a Tuesday in the first week of November). Rules like these are defined by chaining methods together, rather than using natural language.

To install:

gem install ice_cube

To use:

require 'ice_cube'
rule = IceCube::Rule.yearly(4).month_of_year(:november).day(:tuesday).day_of_month(2, 3, 4, 5, 6, 7, 8)
schedule = IceCube::Schedule.new(Time.now)
schedule.add_recurrence_rule rule
schedule.first(3)
# => [Tue Nov 02 05:04:38 +0000 2010, Tue Nov 04 05:04:38 +0000 2014, Tue Nov 06 05:04:38 +0000 2018]

ice_cube also supports exporting rules into iCal and YAML formats as well as a natural language equivalent.

John has put together a PDF presentation that shows off more usage, and there are some simple examples on the official site too.

tickle - A natural language parser for recurring events

tickle is a natural language parser for recurring events by Joshua Lippiner that stands in contrast to ice_cube's method driven approach. It depends on the popular chronic natural language date parser and appears (through my experience) to be for Ruby 1.9+ only.

tickle lets you throw it things like every 4 days starting next saturday, every other week, the tenth of the month and similar (there are a lot of examples on tickle's GitHub page). You pass these to the Tickle.parse method and you get the next occurrence of the rule.

To install:

gem install tickle

To use:

require 'tickle'
Tickle.parse('every 4 days starting next saturday')
# => 2010-05-01 12:00:00 +0000

tickle isn't particularly mature yet and it only makes it easy to get the next occurrence of your rule, but the developer suggests that once an event has occurred, you automatically run Tickle again to get the next date. In this way, it seems tickle is well suited for situations where only the next occurrence needs to be stored and the rule can be kept in a separate database column or similar.

business_time - Time and date offsets based on "business time/hours"

business_time is a new library that works with the concept of "business time" or "business hours." Rather than just letting you perform operations on dates by absolute numbers of days or hours, you can now work with business days and hours of your own definition. business_time depends heavily on Active Support.

To install:

gem install business_time

Note: business_time depends on Active Support (gem: activesupport)

To use:

require 'active_support'
require 'business_time'
# Examples "from now"
4.business_hours.from_now
5.business_days.from_now
# Using user supplied dates
my_birthday = Date.parse("August 4th, 2010")
10.business_days.before(my_birthday)
# Add a day to not count as a business day
BusinessTime::Config.holidays << my_birthday
# Overlapping days are OK
6.business_hours.after(Time.parse("August 3rd, 3:00pm"))


more »

Nestful: A Simple Ruby HTTP/REST Client Library »

Created at: 28.04.2010 13:11, source: Ruby Inside, tagged: Cool Miscellaneous

Nestful is a simple HTTP/REST client library for Ruby, developed by Alex MacCaw (of Juggernaut) fame. Nestful allows you to consume basic Web services easily, usually in a single line of code. It can deal with JSON, buffered downloads, and callbacks out of the box.

HTTParty is the current, de-facto simple HTTP/REST client library used by most Rubyists (when net/http won't do or when Typhoeus is too overkill) but Nestful differs enough from HTTParty to live alongside it. While HTTParty encourages you to build up some structure and separate the types of resources you're accessing into classes (that HTTParty then extends), Nestful offers a simpler, "just call a method from anywhere" approach.

Some basic Nestful examples:

Nestful.get 'http://example.com' #=> "body"
Nestful.post 'http://example.com', :format => :form #=> "body"
Nestful.get 'http://example.com', :params => {:nestled => {:params => 1}}
Nestful.get 'http://example.com', :format => :json #=> {:json_hash => 1}
Nestful::Resource.new('http://example.com')['assets'][1].get(:format => :xml) #=> {:xml_hash => 1}

Nestful's features include:

  • Simple API
  • File buffering
  • Before/Progress/After Callbacks
  • JSON & XML requests
  • Multipart requests (file uploading)
  • Resource API
  • Proxy support
  • SSL support


more »