Wednesday, August 27, 2008

Control Your Monkey: JRuby + JMonkeyEngine

Continuing our previous example, here's how we can use JRuby's Java Integration to create a Ruby class that extends a Java class. We'll use it to override the default DebugGameState first-person controls and add our own. In the process, we'll add an input handler to listen for the Escape key and use it to quit the program, removing the need for a Swing quit button.


game=com.jmex.game.StandardGame.new 'test game'
game.start

class InputGameState < com.jmex.game.state.DebugGameState
def initialize sphere, game
super false
@ih = com.jme.input.InputHandler.new
@keyboard = com.jme.input.KeyBindingManager.getKeyBindingManager
@keyboard.set "forward", com.jme.input.KeyInput::KEY_UP
@ih.add_action com.jme.input.action.KeyNodeForwardAction.new(sphere,1), 'forward', true
@keyboard.set "quit", com.jme.input.KeyInput::KEY_ESCAPE
@ih.add_action com.jme.input.action.KeyExitAction.new(game), "quit",false
end

def update t
super
@ih.update t
end
end

sphere = com.jme.scene.shape.Sphere.new('my sphere',10,10,5)
state = InputGameState.new sphere, game

sphere.set_random_colors
sphere.update_render_state
state.root_node.attach_child sphere
com.jmex.game.state.GameStateManager.instance.attach_child state
state.active=true


So what we're doing is pretty much the same as before. Create a StandardGame and start it, to start up the engine and OpenGL. Then we define our InputGameState class, which adds a custom input handler. Calling super false invokes the DebugGameState constructor, which takes a boolean that says whether or not it should handle input for us. We're using our own input handler, so we pass it false.

The KeyBindingManager is a singleton that maps virtual key codes to action name strings. An InputHandler then uses its update loop (invoked by the game state's update method every frame) to check which of its registered actions are currently being executed.

KeyNodeForwardAction is a utility action that moves the specified scene graph node (our sphere) forward (toward the camera in our setup) by the specified rate of units per second. The last boolean parameter specifies whether or not the action will repeat if the key is held down.

KeyExitAction is another utility action that shuts down the specified game. So pressing ESC will now quit the game, and as long as no other Java threads are running, it will exit the VM.

The two KeyAction classes are examples of a wide range of utility classes that jME provides. They all implement an InputActionInterface, so to create a custom action in JRuby, you can simply say:


a = com.jme.input.action.InputActionInterface.impl do |m,event|
puts event
end


That will create a Java proxy class implementing the interface. The m symbol will be filled in with the name of the implemented method, in this case performAction.