Status Update (October 24)
(October 24, 2008 @ 06:42 PM)
I’ve finished the massive cleanup of the panda demo. I moved all the random shape drawing to another demo (demo_draw.rb), and turned the panda demo into a pretty good example of game structure. It has a Game class which organizes the clock, screen, sprites, event queue and handler, etc. The Game, the sprites, and the sprite group all use HasEventHandler, and nearly everything is event-based, even updating and redrawing the sprites! Plus everything’s commented to explain it, so it might actually be a pretty useful practical example.
I also decided to partially revert my simplification of MethodAction from the other day. Specifically, I added back in the behavior of calling the method first with the event as the argument, and then if that fails (raises ArgumentError), tries again with no argument. So, it’s less picky about whether the method takes an argument or not, which makes it easier to use, and reduces the number of confusing errors people will see. All good things.
Rubygame 2.4 will be released later tonight. I’m cleaning up, updating the NEWS and ROAPMAP, and other stuff right now in preparation to package it up!
Status Update (October 23)
(October 24, 2008 @ 02:04 AM)
Worked on porting the infamous panda demo to use the new event classes and handler system. I’m also cleaning it up and doing some reorganization and documentation of it – about time, too! I don’t think that demo has even seen a proper cleanup in 3 years.
I also made a few changes to the API as I was working on the demo:
First, I renamed AllTrigger to AndTrigger and AnyTrigger to OrTrigger. “And” and “Or” just feel more natural to me. I actually wrote AndTrigger instinctively while coding the demo, before I remembered that it wasn’t called that. “And” and “Or” are more familiar logical concepts, too: “This trigger and that trigger match the event”, or “This trigger or that trigger matches the event.” So, they should be memorable.
Second, HasEventHandler no longer needs to be initialized; that means, no need for super() in your #initialize method. Before I made the change, I ran into a mysterious “nil has no method” error while coding the demo, because I forgot to initialize it. Well, having to explicitly initialize it is just silly and leads to errors like I myself ran into. So, now it initializes itself the first time it needs to.
The mechanism for that is simple. For example:
1 2 3 4 5 6 |
def handle( event ) @event_handler.handle( event ) rescue NoMethodError @event_handler = EventHandler.new() if @event_handler.nil? retry end |
Anyway, I’m still planning to release the new version tomorrow. There will also be another important announcement tomorrow.
Status Update (October 21)
(October 21, 2008 @ 06:17 PM)
Finished the 3 things I mentioned yesterday:
- Added _make_magic_action and _make_magic_trigger to HasEventHandler. I moved the conversion behavior from magic_hooks to those methods, now you can use method overriding and inheritance to add custom conversion rules. (Oh, and I also renamed magic_hooks to make_magic_hooks.)
- Put actions in EventActions, and triggers in EventTriggers. Added docs describing how to write your own action or trigger classes.
- Simplified MethodAction. No more pass_event (it always passes), and no more rescuing ArgumentError, either.
I also merged the event_handler git branch into the dev-2.4 branch, and updated the NEWS file.
I’ll have to look everything over, but I think it’s done. I might do some extra fiddling around between now and the 24th, but I think I could release it as-is now.
Status Update (October 20)
(October 21, 2008 @ 01:14 AM)
Finished the last two things on my list:
- Specs for the mouse event triggers.
- Add Joystick.activate_all and Joystick.deactivate_all.
But I thought up more stuff to add to the list:
- Add _magic_trigger and _magic_action methods to HasEventHandler. These would be private methods that you can override to define your own custom “magic” conversion rules when using magic_hook. They would take precedence over the built-in rules, if there was a match.
- Organize the trigger and action classes to be inside EventTriggers and EventActions modules, for obsessive cleanliness and so I can have a good place for documentation about making your own triggers and actions.
- Change MethodAction to remove its pass_event argument. Just always try to pass the event, and if that fails try again without it. No need for pass_event (besides, I can add it back later if needed, but I can’t take it away after release or it would break compatiblity).
Also, I’ve set a deadline / release date for version 2.4: October 24, because the timing is right, and it has a 2 and 4 in it!
Status Update (October 19)
(October 19, 2008 @ 10:02 PM)
Well, ”tomorrow” didn’t quite turn out, but I’ve got atiaxi’s ACTIVEEVENT fix merged in. Although he might want to read up a bit on the effects and uses of bitwise AND (“&”) versus bitwise OR (“|”). ;-)
My next step was to revamp the function so that it could possibly return multiple Rubygame events from a single SDL_ACTIVEEVENT. That wasn’t much trouble at all, and the results are now pushed to the dev-2.4 branch on github.
Here are the remaining to-dos for the Rubygame 2.4 release:
- Specs for the mouse event triggers
- Making joysticks enabled automatically (?)
I’m not sure about the joysticks thing, though. That would start sending joystick events to the event queue without any Joystick instances having been created, which could be an unexpected change in behavior. So, it might be something to consider for 3.0, but not for 2.4.
I could add Joystick.activate_all and deactivate_all (or some other similar named methods) to make it easier to activate the joysticks, though. Hrmm, I’ll think about it. Leave a comment if you’ve got thoughts about that.
Status Update (October 16, again)
(October 16, 2008 @ 08:57 PM)
Added docs for the event actions, and also finally merged in atiaxi’s fix for Ftor.new_from_to.
I’ll merge his ACTIVEEVENT fix tomorrow, although I’m going to make so it can return multiple events, instead of just returning the first match.
Status Update (October 16)
(October 16, 2008 @ 03:43 AM)
Even with the reduced number of commitments, I’ve been busy as always. Actually, I have a tendency to make new commitments, and a little side project distracted me this past week.
But, I made some progress tonight. I’ve finished specs for HasEventHandler #magic_hooks, and also made so you can pass already-made trigger or actions to it, as I mentioned last time.
I’ll have to look around some more to be sure, but I think all that’s left is:
- Specs for the mouse event triggers
- Docs for the event actions
- Making joystick support enabled automatically
- Maybe test that the new joystick events actually work. (I wonder if I have a working joystick somewhere?)
An October release still looks certain.
Status Update (October 5)
(October 05, 2008 @ 08:55 PM)
EventHandler specs are done. I also added a couple methods (#has_hook?, #remove_hook) that were conspicuously missing. Pushed everything to Github.
Realized I haven’t specced HasEventHandler. Not sure I will. Maybe #magic_hooks.
Speaking of magic hooks, I need to make so you can pass it trigger or action instances.
Another status update
(October 01, 2008 @ 12:05 AM)
It has been a while since my last post, so I figured I had better make a post to show I’m still alive!
I’m making progress on the documentation and specs for the event system.:
- EventHook is fully specced and documented.
- All of the event triggers are documented. All except the mouse ones are specced.
- The event actions are specced, but not documented yet.
- EventHandler is fully documented, and I’ve just started on the specs.
Hopefully development should pick up a little bit now, because I have freed up some stressful and time-consuming commitments recently. No set date for release of 2.4, of course, but I’d be very surprised if it wasn’t in October. I’m eager to get this update released soon!
Status update
(September 11, 2008 @ 08:41 PM)
I just wanted to make a quick post about where Rubygame is now.
I finished the changes I mentioned last time about :left_shift and :right_shift being matched by :shift for keyboard triggers. Here’s how I wrote the code, if you’re curious:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# @mods is the array of modifiers for the trigger. # evmods is the array of modifiers for the event. def _mods_match?( evmods ) @mods.all? { |mod| case mod when :alt, :ctrl, :meta, :shift evmods.include?("left_#{mod}".intern) or evmods.include?("right_#{mod}".intern) else evmods.include?(mod) end } end |
In addition to supporting general modifiers (vs left/right specific), this code is a lot more forgiving than before. Originally, I had just checked evmods == @mods, which was rather stupid of me, because [:shift, :ctrl] == [:ctrl, :shift] would fail, even though they are equivalent in this context.
All the work that remains for 2.4 is writing documentation and specs for the event actions, event hook, and the HasEventHandler mixin. Unfortunately, that’s not the kind of work that motivates me to take time out of my day to finish it. I’ve got the 2 hours per week scheduled, but I may need to increase that and push hard until 2.4 is done.
Keyboard Triggers
(September 05, 2008 @ 11:37 PM)
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.
Working towards Rubygame 2.4
(July 30, 2008 @ 11:26 PM)
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.
Quack, quack! (Duck Typing)
(July 13, 2008 @ 08:24 PM)
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:
1 2 3 |
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:
1 2 3 4 5 |
# 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:
1 2 3 4 5 |
# 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:
1 2 |
# 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!
Working on new event classes
(June 20, 2008 @ 02:40 AM)
It’s amazing how much you can get done when you’re not banging your head against Autotest. I made 54 commits today, implementing the RSpec Rake tasks I wrote about earlier, and added 5 event classes, with specs:
- KeyPressed
- KeyReleased
- MousePressed
- MouseReleased
- MouseMoved
(Hrmm, I still need to add documentation. That’ll have to wait until tomorrow.)
As a reminder, version 2.4 will contain an alternative suite of event classes, as well as a new event hook system. The new event classes make obsolete the old ones, which will be removed in 3.0. So far, the new classes are pretty similar to the old ones, and I expect almost all of them to be drop-in replacements for the old classes.
P.S. It has been fun working with Git, practicing my branching and merging, and making small commits. If you want to check out the new event classes, I’m working in the cleverly-named new_event_classes branch. When I’m done with that, I’ll be merging it back into master.
Not digging Github's Lighthouse integration
(June 19, 2008 @ 08:05 PM)
Lighthouse by itself seems nice. Mostly, I like the simplicity and ease of use, but Github’s integration with Lighthouse seemed interesting as well. The idea of closing Lighthouse tickets by putting a special notice in the Git commit log sounds like it might be a vaguely useful feature. (Rather than, say, a useless gimmick.)
But I’m really not a fan of how it floods the Lighthouse project with every single commit I make. There’s a commit log on Github, where I can actually see the diff; why would I want a second, crippled, spammy commit log on Lighthouse?
Unfortunately there doesn’t seem to be any way to disable sending the commit logs while still keeping the ticket-closing, so I’m just going to have to disable Lighthouse integration altogether.