Keyboard Triggers

Just posting a reminder to myself to improve keyboard triggers to be better about modifier keys. Right now you have to specify (as one example) either :left_shift or :right_shift, but there's no way to say "I don't care which shift it is, as long as shift is being pressed. So I need to make it able to take :shift and have that match either the right or left versions. The same applies for ctrl, alt, etc. (I was writing the documentation for KeyPressTrigger, and I noticed I couldn't write an example of a trigger that matches Ctrl+Shift+A, without caring about left/right, without using two triggers nested in an AnyTrigger.)

I also want to improve magic hooks so that you can specify "Ctrl+Shift+A" and it will translate that into KeyPressTrigger.new( :a, [:ctrl, :shift] ). Because that would be really handy. But maybe that can wait until after 2.4? Dunno.

Update: Finished the docs for the event triggers, and pushed them to Github in the event_handler branch.

Schedules

Schedules are nice. You'd think I would have already figured this out, but having a schedule / weekly planner is quite good for making sure you actually put some time into the things you need to do. Like, say, working on Rubygame.

I have been occupied as always, but I'm learning to manage it and keep a regular schedule, and I've fit in a 2-hour slot for working on Rubygame every Saturday afternoon. I might pop into IRC now and again during that time, but I want most of it to go towards getting stuff done, and using IRC pretty much kills productivity entirely.

Hopefully this will mean that Rubygame keeps rolling along, slowly but surely.

RubyWeekend #2 Results

It's time to announce the winners of the RubyWeekend #2 Contest!

Third Place: Mizutoka by jlnr!

Second Place: Cheese Master by elcugo!

First Place: Opposite Islands by ippa!

Congratulations to our winners, and thanks to everyone who competed or voted! It's no easy task to create a game in 48 hours, but I hope everybody had fun and learned something new!

Here are the full rankings, with a breakdown of the points each game received.

  1. Opposite Islands by ippa: 34 points (10 + 7 + 7 + 5 + 5)
  2. Cheese Master by elcugo: 28 points (10 + 7 + 4 + 4 + 3)
  3. Mizutoka by jlnr: 24 points (10 + 5 + 5 + 2 + 2)
  4. DungeonFarmer by Yahivin: 23 points (10 + 10 + 2 + 1 + 0*)
  5. Cyber Deathmatch by Venut: 19 points (5 + 4 + 4 + 3 + 3)
  6. Solunaria by jacius: 19 points (7 + 7 + 3 + 1 + 1)
  7. Playground Wars by kiba and qubodup: 12 points (4 + 3 + 2 + 2 + 1)

* 0 = the voter did not rank the game at all

See you next time!

Working towards Rubygame 2.4

Slowly but surely, I'm working towards Rubygame 2.4.

As a reminder, 2.4 is going to be about events. In particular, a new Events module with a suite of SDL-based event classes to replace the old event classes.

The reason for this is to clean up the API; all the events are tucked away in their own cozy little module instead of cluttering up the Rubygame namespace, and they've got nicer class names. The documentation and code are also nice and clean, and all the event classes are thoroughly specced. The class interfaces are also more tightly controlled, for example freezing attributes to prevent modification.

The new event classes also do away with the massive list of integer constants (K_ESCAPE, MOUSE_BUTTON_LEFT, etc.) that were cluttering up the code. Instead, they use symbols for key names, mouse buttons, etc. This also makes the code you write a lot nicer to read.

Another nice improvement is that the KeyPressed event now has an attribute with a Unicode string containing the glyph (if any) that was generated by the keypress. This is very nice for text input, for example if you're programming a GUI.

All the new event classes are finished, written in Ruby. I'm currently implementing the C-side event conversion. The code is much cleaner and nicer to read this time around, let me tell you! (One would hope so, given the years of extra C experience I have under by belt.) After the new classes are in place, I'll mark the old event classes as deprecated; they'll be removed entirely in Rubygame 3.

The new event classes are only half of 2.4, though. The other half is the new hook-based event handler system, including the much-anticipated "magic hooks". The code for that stuff has been complete for a while, but it still needs to be cleaned up, documented, and specced. Once that rolls out, you can kiss your huge case-when blocks goodbye, my friends.

Given how often and suddenly I get swamped with work, predicting a release date for 2.4 is futile. But, I'd say it would take only 3 or 4 more afternoon/evening sessions to get it polished off. Whether that will take a week or two months is impossible to say.

Vote for your favorite RubyWeekend #2 games!

The RubyWeekend #2 contest period is over, and now it's time to vote for the winners!

We had seven official entries this time:

  • Mizutoka by jlnr
  • Cyber Deathmatch by Venut
  • Playground Wars by kiba and qubodup
  • Opposite Islands by ippa
  • DungeonFarmer by Yahivin
  • Solunaria by jacius
  • Cheese Master by elcugo

We also had a late entry, which you are encouraged to play (even though you can't vote for it):

  • Super StarHawks Gaiden by misspledd

Check out the entries thread for info, screenshots, and downloads, then rank and vote for your favorites!

RubyWeekend #2: "Opposites" has begun!

The theme of the RubyWeekend #2 game contest has just been announced, marking the start of the contest period! If you're participating, you've got 60 hours to work on your game before you have to submit it!

The theme is "Opposites". Will your game be about fire and ice, or black and white, or big and small... or something else? I expect to see lots of different interpretations on this very abstract theme!

Remember, we'll be hanging out in #rubygame on the freenode.net IRC network all weekend, and posting in the Contests forum at rubygameforums.com. Drop by and visit us between coding sessions. :)

Good luck to all the contest participants, and don't forget to have fun!

RubyWeekend #2 contest this weekend!

The second RubyWeekend game creation contest is this weekend! The official rules and time have been posted, and everybody's gearing up for some programming fun this weekend!

The contest starts at Friday, July 25, 2008 @ 16:00 UTC (find it in your time zone) and ends Monday, July 28, 2008 @ 04:00 UTC (find it in your time zone)! The theme will be announced at the start of the contest period.

We'll be hanging out in #rubygame on freenode.net all weekend, and posting in the contests forum too. Hope you'll join us!

July 25 : RubyWeekend #2 Game Contest

The second RubyWeekend Game Contest is almost here! The weekend of July 25 - 27, compete with other Ruby programmers to create a game in 2 days!

A theme will be announced on July 25 at the start of the contest period. Then, create a game based on the theme, working alone or in pairs (2 people). You can use any Ruby library available (Rubygame, Gosu, Shoes, etc.) for your game, as long as your game code is written in Ruby.

For more info as the contest date approaches, or to check out the winning games from RubyWeekend #1, check out the Contests forum on rubygameforums.com:

http://www.rubygameforums.com/viewforum.php?f=10

Hope to see you there on July 25!

- John

Quack, quack! (Duck Typing)

I'm pondering Duck Typing tonight as I write event classes and their associated specs.

Here's an abbreviated spec for the WindowResized event, which holds an [x, y] array with the new size of the window:

it "should reject non-array objects as size" do
    lambda { WindowResized.new( 20 ) }.should raise_error(ArgumentError)
end

Giving the number 20 as the size doesn't make sense, and you'd run into an error sooner or later. In this case, without any checks, the error would probably show up when you tried to set the Screen mode again to the new size. Because of the delayed effect of the event queue, the backtrace wouldn't have anything to do with the real problem — that you gave the event an invalid argument.

So, the rationale behind this spec is that the class should complain loudly at the soonest opportunity, so that the location of the error is easy to find.

To make this spec pass, I might write the following:

# this is not duck typing
unless size.kind_of? Array
    raise ArgumentError, "size has to be an Array"
end
@size = size

The spec would pass, but the code would be pretty inflexible. Suppose someone wrote a Size class which had 2 elements, but wasn't actually an Array. This check would reject it, even though it might have been usable. A conundrum!

Duck typing to the rescue, right?

After thinking about it for a while, I realized that the size parameter doesn't have to be an Array per se, it just has to be something that can be converted into an Array before I store it. Since anything that can be converted to an array should have a #to_ary method, I could check that it has that:

# getting warmer...
unless size.respond_to?( :to_ary )
    raise ArgumentError, "size has to be convertable to an Array"
end
@size = size.to_ary

The spec still passes, and I'm getting more duck-typey. Or at least chicken-typey. We could go a step further:

# quack, quack!
@size = size.to_ary

Since the goal was just to complain loudly if the argument is invalid, and we've decided that an argument is invalid if it can't be converted to an Array, then we don't really need to check that it responds to anything. Just try calling the method! If it doesn't have that method, you'll get an error, just like you're supposed to (although we'd have to tweak the spec to expect NoMethodError instead of ArgumentError). If it does have the method, you have a (presumably) valid parameter. So, it's all good.

It would be even more duck-typey to have no checks at all (and no specs for the checks), and instead just assume that everybody who uses the method has read the docs and knows to pass the right sort of thing. But as I explained earlier, not checking is not a good idea here. (Well, maybe in this specific case it would work out fine, since users wouldn't usually be creating their own instances of this event. But in general, no.)

So, what's my point? I don't have one. I just wrote this as a thought experiment to help me decide what to do. Thanks for listening!