I’ve been working with JRuby to build high level systems on top of powerful Java libraries for about 2 years now.  My most successful endeavor has been Monkeybars, a libarary for making Swing a lot easier to use.  My other project has been in the game development arena.  The project name is Gemini, and although it’s not nearly as advanced as Monkeybars, I was able to get to a significant point over the weekend.  Yes, here in all its glory, the simple “crapton of sprites bouncing back and forth demo”.

1000 dukes bouncing back and forth on screen

Now you’re probably saying, big deal, I can do that in like 40 lines of code in <insert favorite programming language/framework> and no doubt you would be correct.  What’s interesting about the way Gemini does things is in how it gives you an extremely flexible way of compositing together your game objects and your application as a whole.

The code to get this going using Gemini is pretty straightforward.  We have a game state that is loaded by Gemini when it starts up.  That game state then instantiates 1000 of our game objects and they proceed to load their images and care for their own logic of bouncing around on the screen.

Here’s the code in main.rb that kicks off the app:

require 'gemini'
Gemini::Main.new("Test game", 640, 480)

And here is the state that is loaded by default when Gemini runs: main_state.rb

class MainState < Gemini::BaseState
  def load
    @sprites = []
    1000.times {@sprites << Duke.new}
  end

  def update(delta)
    @sprites.each { |sprite| sprite.update(delta) }
  end

  def render(graphics)
    @sprites.each { |sprite| sprite.draw }
  end
end

Finally we have the Duke class that was instantiated in the MainGame class:

class Duke < Gemini::GameObject
  has_behavior :Movable2D
  has_behavior :Sprite
  has_behavior :UpdatesAtConsistantRate

  def load
    self.image = "duke.png"
    self.updates_per_second = 30
    @direction = :right
    self.x = rand(640 - image.width)
    self.y = rand(480 - image.height)
  end

  def tick
    if x > (640 - image.width) || x < 0
      @direction = @direction == :right ? :left : :right
    end
    self.x = x + (@direction == :right ? 1 : -1)
  end
end

Apart from some hackish ternary ifs in the Duke class I think the whole thing is pretty darn readable, especially considering all that it is doing. As you can see, everything is extremely declarative including those has_behavior calls in the Duke class. Those nifty bits of metaprogramming bring in all the crunchy goodness of the class and make the x, y, image and updates_per_second methods appear. Movable2D gives us a move method and also pulls in the Positional2D behavior that provides the x and y methods. UpdatesAtConsistantRate gives us an update method that calls tick at the rate specified by updates_per_second. Finally the Sprite behavior gives us the image method and the draw method that gets called in the MainGame class.

Most of this is bare bones at the moment. What if you don’t have the UpdatesAtConsistantRate? When update is called you asplode. Same for Sprite / render. So clearly a more flexible game state is needed, probably involving the game object behaviors auto-registering themselves with the game state to be called during an update/render cycle. But as a whole I’m quite happy with the way Gemini is starting to shape up. Next on the agenda is getting keyboard mapping hooked into the RecievesKeyboardEvents behavior so you can control duke, then we’ll have what could pass for a real game.

One Comment

  1. Very very cool! I’m gonna give it a try.


2 Trackbacks/Pingbacks

  1. [...] layers a Ruby call had to go through to get to Java. The net result: my test case for Gemini that I posted about earlier when from a frame rate of 27 fps to 40 fps. None of my actual code changed, I just swapped out the [...]

  2. By Gemini dev update #1 « Does Not Compute on 29 Jul 2008 at 5:21 pm

    [...] of my previous posts regarding Gemini seemed to have garnered some attention thanks to someone posting one of them [...]

Post a Comment

*
*