A look at the first Rebirth demo game

I finished a demo game yesterday, as an aid in designing the still-imaginary Rebirth API. I actually meant to post this last night, but I ended up staying up until past 2AM trying to find a way to do syntax highlighting on Mephisto, to no avail.

The game is a simple one: bounce a ball in the air by clicking it, and don't let it fall to the ground. You get a point for each time you click it, but lose a point for each time it hits the ground. If you get up to 15 points, you win. Not the most challenging thing in the world, but it has all the elements of a complete game.

The code for the entire game is about 100 lines long. Rather than cop out and just post the whole file, I'll show and explain a few highlights. Keep in mind that the code to support this doesn't exist yet; I'm just threshing it out, and imagining how it might be used. As I explained in my previous post, once I've got some sample games written, I'll codify the API in specs, and then implement it.

First up, is the class for the Ball object. Here's the entire class definition:

# Represents a bouncing ball.
class Ball < GameObject
  # Set up the physical and visual properties of the ball.
  def initialize( params = {} )
    base_params = { 
      :center  => v(0,0),
      :radius  => 15.0,
      :density => 5.0,
      :elast   => 0.9,
      :color   => :red
    }
    
    add_shape Circle.new( base_params.merge(params) )
  end
end

As you can see, it just creates a Circle instance with certain parameters, and adds the Circle as a shape. The hash of base parameters defines what the default ball is like, but you can override / augment them by passing a hash to the initializer. There will be other possible parameters besides just the ones shown there, but these are the ones that are important to the ball (Circle will use defaults for the rest).

Circle will be one of a handful of built-in shape classes, which provide a visual appearance and a physical behavior for game objects (i.e. for collision detection and physics). By default, objects are both visible and collidable, but you can turn either (or both) off by providing certain parameters. That way, you can have non-collidable objects, or collidable invisible objects. And, yes, all the shape classes will be part of the physics engine, powered by Chipmunk.

So far, it has a ball that we can see, and that acts physically. Later on, it adds a hook to perform a certain action when the player clicks on the ball:

$game[:ball] = Ball.new

$game[:ball].when_clicked do |click|
  $game[:score] += 1
  $game[:ball].shove( :from => click.pos, :strength => 30.0 )
end

Here, it makes an instance of Ball, and stores the instance in a namespace for later use ($game is an instance of Game, which I'll cover later). Then, it uses the when_clicked method to add a hook which will be triggered when the user clicks on the ball. The block adds one point to the player's score, and also applies a physical impulse to the ball to shove it away from the cursor, sending the ball back into the air. (By the way, the hook could have just as well been set up in the Ball's initialize method, for behaviors that you want every instance to have.)

Elsewhere, it sets up a when_collided hook on the floor, to call a block to remove one point from the player's score. It also creates a when_true hook on $game itself; when_true periodically evaluates a Proc to see if the statement is true, in this case to test if the player has 15 points yet.

There's other interesting code that I'd show you, but I don't want this post to get really long. I expect to put this project up on Github eventually, so you'll be able to get all the code you want. If the demand is there, I might do another post tomorrow to demo another part.

P.S. Rebirth is still strictly theoretical. I'd give it a 50:50 chance that Rebirth sees a release, and only a 1-in-20 chance that it will result in any major functionality removal from core Rubygame.


Comments

Shawn Anderson submitted a comment on #

What is a game that doesn’t want physics going to look like. Will it still have a strict requirement on chipmumk?

John Croisant submitted a comment on #

@Shawn: Yes, Chipmunk would be a requirement of Rebirth, since it will be used for collision detection as well. But, objects don’t have to use the physics engine. I’m currently working on a Pong example demonstrating non-physical objects.

Edwin submitted a comment on #

I think the following syntax is a bit ugly:

$game[:ball].when_clicked do |click|
  $game[:score] += 1
  $game[:ball].shove( :from => click.pos, :strength => 30.0 )
end

I think it would be cleaner to be able to do something like:

require "game"
include Game

ball.add_event( clicked ) do
   score += 1
   ball.shove( :strength => 30.0 )
end

(the :from parameter would be a default).

I think this looks a bit more rubyish, and at least for me is easier to read/type than your implementation. Don’t get me wrong, I love hashes, but they can be overdone.

Edwin submitted a comment on #

darn blog ate my formatting and I don’t know how to fix it, sorry.

[Fixed for you –jacius]

John Croisant submitted a comment on #

@Edwin: Yeah, the global variable with hash and symbol is a bit of punctuation overload. I’ll try to clean that up. One possibility would be for the Game class to have open-struct like abilities, so you could do game.score += 1, game.ball.shove, etc. I need to refresh my memory on scoping rules for blocks and closures, to see if the global variable can be eliminated.

Regarding when_clicked vs add_event – there will be both; when_clicked will be a convenience method for creating an event hook of that type.

As for shove( :from => click.pos ), there’s no good way to have the click event’s position be the default, and I wouldn’t want it to be, anyway. In fact, there will be no default; instead, you must specify either :from => point, or :direction => radians or vector, or it will give an error.

Edwin submitted a comment on #

What you say about defaults makes sense. Also, thanks for cleaning up the code.

Have something interesting to add to the discussion? Email your thoughtful comments to comments@rubygame.org.