Ruby and SDL

[ LiB ]

Ruby and SDL

SDL has been a common thread throughout the book, first in Chapter 4 with the Pygame SDL wrapper for Python, and then in Chapter 7 with LuaSDL. It would stand to reason that SDL, being the progressive library that it is, also has its fingers in Ruby.

To use SDL with Ruby, you first need to install the SDL library, which can be found in its entirety at its home page, http://www.libsdl.org.

Once SDL is installed, Ruby needs an interface into SDL; there are several different interfaces to choose from. Most of the interfaces can be found in the Ruby Application Archive at http://raa.ruby-lang.org/.

To make it a bit easier for Windows users, a bundled SDL package is contained in a nifty executable included on this book's CD; it is called pack-rubysdl.exe, and you can find it on the CD in the Chapter 10 folder. The pack-rubysdl.exe package is distributed under the GNU Public License and, for Win32, includes the following:

  • Ruby 1.6.4. The Ruby version.

  • Rubysdl-0.6. The actual SDL package.

  • Rubywin-0.0.3.2. An IDE for Ruby on Windows platforms.

  • Rb2exe-0.2. A program for converting Ruby scripts into executable files.

  • Opengl-0.32. The version of OpenGL.

The package is built with Cygwin and comes with a few SDL samples, including those for using the keyboard and joystick, loading sound files from disk, and manipulating a CD. The package also includes one fairly complete sample game by Ohbayashi Ippei.

The caveat to this bundle is that the documentation and installation are in Japanese. You will not be able to read the install files without the proper Japanese character set installed. This is inconvenient for English speakers , as the install files may look like Figure 10.1, depending on the platform used.

Figure 10.1. The pack-rubysdl install may look strange without the right Japanese character set.

graphic/10fig01.gif


Whether your platform displays the characters correctly or not, choosing the left-hand confirmation button means you agree to install the RubySDL folder and files on your C:\ drive (see Figure 10.2).

Figure 10.2. Choosing the left-hand button at this screen after launching .pack- rubysdl.exe will install the package.

graphic/10fig02.gif


RubyWin is one of the big bonuses in this package. A GUI developed by Masaki Suketa that bundles Ruby 1.6.4 and Scintilla 1.38 (by Neil Hodgsen), RubyWin creates an environment for running Ruby SDL scripts without having to change or manipulate local environment variables . Launching the executable brings up the RubyWin GUI (see Figure 10.3) and the Run File command, accessible via the Ctrl-R shortcut or through the Ruby menu, can be used to launch and test Ruby SDL applications

Figure 10.3. The RubyWin GUI

graphic/10fig03.gif


Commonly Used Ruby SDL Modules and Classes

The common Ruby SDL modules and classes are listed in Table 10.1.

Table 10.1. Common Ruby SDL Modules and Classes

Component

Module or Class

Description

SDL::CD

Class

Represents the CD-ROM drive

SDL::Error

Class

Error class; handles Ruby/SDL errors

SDL::Event

Class

Handles events

SDL::Event2

Class

Handles events

SDL::Joystick

Class

Represents a joystick

SDL::Key

Module

Defines key constants and gets the key state

SDL::Mixer

Module

Holds sound functions and constants

SDL::Mixer::Wave

Class

Handles WAV files

SDL::MPEG

Class

Handles MPEG streams

SDL::Mouse

Module

Contains mouse constants and functions

SDL::PixelFormat

Class

Parent to SDL::Surface (obsolete)

SDL::Screen

Class

Displays the screen image

SDL::SKK

Class

Handles Japanese input

SDL::Surface

Class

Contains methods for creating SDL surfaces (images)

SDL::TTF

Class

Handles TrueType fonts

SDL::WM

Module

Handles windows


Ruby SDL includes all sorts of classes for supporting window management, MPEG streaming, joysticks, CD-ROMs, and different fonts. More commonly used are the tools for initializing and SDL environments, creating SDL surfaces, handling events, audio, time, and Japanese character support.

Initializing SDL

The init module is used to initiate SDL. A flag that triggers which portion of SDL needs to be initialized is included when initializing:

SDL::INIT_AUDIO. Initialize system audio.

SDL::INIT_VIDEO. Initialize system video.

SDL::INIT_CDROM. Initialize the CD-ROM.

SDL::INIT_JOYSTICK. Initialize a joystick device.

The line of code that will initialize video looks like the following:

 SDL.init(SDL::INIT_VIDEO) 

A particular game's video mode is set with SDL.set_video_mode() , which takes as arguments the width and height of the screen, bits-per-pixel (0=s current or local display), and any necessary flags:

 SDL.set_video_mode(640, 480, 0, SDL_FLAG) 

Possible flags for SDL.set_video_mode include the following:

SDL::SWSURFACE. Creates video surface in system memory.

SDL::HWSURFACE. Creates video surface in video memory.

SDL::FULLSCREEN. Attempts to use the full screen.

SDL::SDL_DOUBLEBUF. Enables double buffering.

To find out if a particular video mode is supported, there is also an SDL.checkVideoMode() command that uses the same syntax.

Surfaces

After setting up a video mode, SDL::Surface.new will create an empty SDL surface. Its new method also keeps an eye out for several flags:

SDL::SWSURFACE. Creates the surface in system memory.

SDL::HWSURFACE. Creates the surface in video memory.

SDL::SRCALPHA. Chooses the location with the best hardware alpha support.

SDL::SRCOLORKEY. SDL chooses the location with the best hardware colorkey blitting.

Surface.new also needs width, height, and format. The format must be the instance of SDL::Surface and have the same bits per pixel as the specified surface.

There are dozens of methods that can be used on SDL surfaces. Some of the more commonly used ones are listed in Table 10.2.

Table 10.2. Common SDL Surface Methods

Method

Equivalent To

Purpose

alpha

Returns surface alpha

bpp

Return bits per pixel

colorkey

Returns surface colorkey

drawCircle

draw_circle

Draws a circle

drawEllipse

draw_ellipse

Draws an ellipse

DrawFilledCircle

draw_filled_circle

Draws a circle filled with specified color

drawFilledEllipse

draw_filled_ellipse

Draws an ellipse filled with specified color

drawLine

draw_line

Draws a line between the given coordinates

drawRect

draw_rect

Draws a rectangle

displayFormat

display_format

Makes a copy of itself on a new surface; used for fast blitting

displayFormatAlpha

display_format_alpha

As displayFormat wtih alpha value per pixel

fillRect

fill_rect

Fills given rectangle with specified color

flags

Returns surface flags

format

Returns pixel format

getClipRect

get_clip_rect

Returns clipping rectangle for the given surface

getPalette

get_palette

Returns the palette of the specified surface

GetPixel

get_pixel

Gets color of the specified pixel

GetRGBget_rgb

Returns RGB component values of specified pixel in an array

getRGBA

get_rgba

Like getRGB , but includes an alpha value

h

Return height

load

Loads image (such as a BMP) and returns instance of SDL::Screen

loadBMP

load_bmp

Loads given bitmap

lock

Sets up a surface for directly accessing pixels

makeCollisionMap

Creates a collision map

mapRGB

map_rgb

Maps the RGB color value to the pixel format of specified surface and returns the pixel value as an integer

mapRGBA

map_rgba

Same as MapRGB but also includes an alpha value

mustLock?

must_lock?

Returns true if surface must be locked to directly access pixels

put

Draw given image in self

PutPixel

put_pixel

Writes pixel to the specified position

rotateScaled Surface

rotate_ scaled_surface

Rotates surface instance with given angle and scale. Note: method is considered obsolete; it's been superceded by transformSurface .

rotateSurface

rotate_surface

As rotateScaledSurface but scale is set to 1.0

saveBMP

save_bmp

Saves file in BMP format

setAlpha

set_alpha

Used to set alpha and per-pixel alpha blending

setColorKey

set_color_key

Sets the colorkey of a blit-able surface

setColors

set_colors

Same as setPalette but with different flags

setPalette

set_palette

Sets a portion of the palette for the given 8-bit surface

transformSurface

transform_surface

Creates a rotated and scaled image of given surface

unlock

Unlocks a surface

w

Returns width


Events

Ruby SDL has two event classes, event and event2 , for handling events. Each has a number of methods; these methods are outlined in Tables 10.3 and 10.4.

Table 10.3. Event Methods

Method

Equivalent To

Purpose

appState

Event.app_state

Returns current stat.enableUNICODE

Event.enable_unicode

Enable UNICODE

Keyboard translation (disabled by default)

Event.disableUNICODE

Event.disable_unicode

Disables Unicode keyboard translation

Event.enableUNICODE?

Event.enable_unicode?

Returns whether Unicode keyboard translation is enabled

gain?

Returns true when gaining focus

info

Returns event information in an array

keyMod

key_mod

Returns the current key modifiers

keyPress ?

key_press?

Returns true when a key is pressed down in a key event

keySym

key_sym

Returns SDL virtual keysym

mouseButton

mouse_button

Returns the mouse button index

mousePress?

mouse_press?

Returns true during a mouse button down event

mouseX

mouse_x

Returns the x coordinate of the mouse

mouseXrel

mouse_xrel

Returns the relative mouse motion on the x-axis

mouseY

mouse_y

Returns the y coordinate of the mouse

mouseYrel

mouse_yrel

Returns the relative mouse motion on the y-axis

new

Creates a new SDL::Event object

poll

Polls for currently pending events

type

Returns the type of a given stored event

wait

Waits for the next available event

appState

app_state

Returns the kind of ActiveEven


Table 10.4. Event2 Methods

Method

Equivalent To

Purpose

Active

Event that occurs when mouse/keyboard focus gains/loss

appState

Event2.app_state

Same as Event.appState

enableUNICODE

enable_unicode

Same as Event.enableUNICODE

enableUNICODE?

Event2.enable_unicode?

Same as Event.enableUNICODE?

disableUNICODE

disable_unicode

Same as Event.disableUNICODE

JoyAxis

Event that occurs when axis of joystick is moved

JoyBall

Event that occurs when a joystick trackball moves

JoyButtonDown

Event that occurs when joystick button is pressed

JoyButtonUp

Event that occurs when joystick button is released

JoyHat

Event that occurs when joystick hat moves

KeyDown

Event that occurs when a key is pressed

KeyUp

Event that occurs when a key is released

MouseButtonDown

Event that occurs when a mouse button is pressed

MouseButtonUp

Event that occurs when a mouse button is pressed

MouseMotion

Event that occurs when the mouse is moved

poll

Same as Event.poll

quit

Event that occurs when a quit or exit is requested

SysWM

Event that occurs when plaform-dependent window manager occurs

VideoResize

Event that occurs when windows are resized

wait

Same as Event.wait


Ruby SDL also has mouse and key classes and methods for mouse and keyboard events; these are outlined in Table 10.5.

Table 10.5. Mouse and Keyboard Events

Method

Equivalent To

Purpose

Key.disableKeyRepeat

Key.disable_key_repeat

Disables key repeat

Key.enableKeyRepeat

Key.enable_key_repeat

Sets keyboard repeat rate

Key.getKeyName

Key.get_key_name

Returns the string of key name

Key.modState

Key.mod_state

Returns the current of the modifier keys

Key.press?

Return true if given key is pressed

Key.scan

Scans key state

Mouse.hide

Hides mouse cursor

Mouse.setCursor

Mouse.set_cursor

Used to change the mouse cursor

Mouse.show

Shows a mouse cursor

Mouse.state

Returns mouse state in array

Mouse.warp

Sets the position of the mouse cursor


Audio

Ruby's SDL has a Mixer module that is used to serve up music files, change volume, and set up sound effects like fading. Mixer has a class for handling WAV files, SDL::Mixer::Wave , and a class for loading music, SDL::Mixer::Music. Wave handles standard WAV files, while Music can load mod, S3M, it, XM, MID, and MP3 file formats. Mixer 's methods are outlined in Table 10.6.

Table 10.6. Mixer Methods

Method

Equivalent To

Purpose

allocateChannels

allocate_channels

Dynamically change the number of channels managed by the mixer

fadeInMusic

fade_in_music

Fade in the given music in milliseconds

fadeOutMusic

fade_out_music

Fade out the given music in milliseconds

halt N/A

Halt playing of a particular channel

haltMusic

halt_music

Halt music

load

Load a music file and return the object of Mixer::Music

open

Initialize SDL_mixer

play?

Return whether specific channel is playing or not

playChannel

play_channel

Play a WAV on a specific channel

playMusic

play_music

Play music

playMusic?

play_music?

Return whether the music is playing

pause

Pause on a particular channel

pause?

Return whether a particular channel is paused

pauseMusic

pause_music

Pause music

pauseMusic?

pause_music?

Return whether the music is paused

resume

Resume a particular channel

resumeMusic

resume_music

Resume music

rewindMusic

rewind_music

Rewind music

setVolume

set_volume

Set the volume

setVolumeMusic

set_volume_music

Set volume

spec

Return the audio spec in array


Time

SDL uses the notion of ticks to keep track of time. The getTicks/get_ticks method will get the number of milliseconds that have passed since SDL was initialized. There is also a delay method that will wait a given number of milliseconds before returning; it is used to process scheduled jobs and events.

Japanese Input

Ruby's SDL comes equipped with an SSK module for encoding the Japanese character set. This module relies on the SDLSSK library, and can set the encoding to the Japanese character system (EUCJP), the ASCII- preserving Unicode system (UTF8), or the Shift-JIS Japanese system (SJIS). SSK has a handful of methods; these are outlined in Table 10.7.

Table 10.7. SSK Methods

Method

Purpose

Context

Super class that represents the state of input

Dictionary

Super class for manipulating user dictionaries

encoding

Returns encoding

EUCJP

Sets encoding to EUCJP

Keybind

Represents the keybind in SDLSKK input system

RomKanaRuleTable

Represents the rule of conversion from Alphabet to Japanese kana

SJIS

Sets encoding to SJIS

UTF8

Sets encoding to UTF8


A Sample Ruby SDL Program

All of the tables given in this chapter aren't enoughyou need to try an example of using SDL and Ruby together. In the Chapter 10 section of the accompanying CD is a sample RubyBounce folder with five Ruby files. They are as follows :

  • CONST.RB

  • PLAYER.RB

  • RUBYBOUNCE.RB

  • STATE.RB

  • SYSTEM.RB

These five files are explained in the next few subsections. Each has a part to play in setting up a quick SDL Ruby environment where a player manipulates a small bouncing ruby (see Figure 10.4).

Figure 10.4. A bouncing ruby is displayed in the RubyBounce program.

graphic/10fig04.gif


This program can be run from the RubyWin application. Open up rubywin.exe in your new C:\RUBYSDL\BIN folder, choose the Ruby menu, select Run, and then choose the RUBY-BOUNCE.RB file.

The CONST.RB File

The simplest of the five Ruby files, CONST.RB is used to hold any specific game constants that need to be defined (see Figure 10.5). In this example, the file holds four constants, each of which defines a wall in the playing surface. Changing these values later on changes where the player can travel onscreen:

Figure 10.5. File relationship for RubyBounce

graphic/10fig05.gif


 LWALL_X=40 RWALL_X=600 FLOOR_Y=440 CEIL_Y=60 

These values are x- and y-set pixel ranges that define the edges of the playing surface in pixels (see Figure 10.6).

Figure 10.6. Playing field x and y boundaries.

graphic/10fig06.gif


The SYSTEM.RB File

The functions set up in the SYSTEM.RB file should look familiar, as they are similar to the functions you used in earlier chapters. The only difference between the first define , setup_bmp , and earlier endeavors to load bitmaps is Ruby's own unique twist:

 def setup_bmp(filename)   graph=SDL::Surface.loadBMP(filename)   graph.setColorKey SDL::SRCCOLORKEY, graph[0,0]   graph=graph.displayFormat end 

Here SDL::Surface.loadBMP is used to grab a .BMP file, the colorkey is set with the setColorKey method, and finally, displayFormat is used to display the surface.

Also included in this file are two functions for keeping track of where an object travels in the two-dimensional screen. The x_out function and the send_loc function help determine if the object tries to travel past the LWALL and RWALL constants set in CONST.RB:

 def x_out?(x,w)   x+w+10<LWALL_X  x-10>RWALL_X end def send_loc?(x,w)   return true if LWALL_X+SEND_FIELD_WIDTH>x+w   return true if RWALL_X-SEND_FIELD_WIDTH<x   false end 

Then you define the system class with the initialize and continue_game methods. In a full version game, this would be a good place to set important variables like player score and number of lives, but in this case just one instance variable is set; @life :

 class System   def initialize     @life=3   end   def continue_game?     @life > 0   end end 

The STATE.RB File

There are three classes defined in STATE.RB: State , StateInitializer , and StateDriver . Each is used to keep track of the game state, and each is stored within the jt (just in time) module. The State class has two methods, initialize and move_state . State.initialize is probably the most important method in this script. It first calls the constructor and sets three important instance variables: state_hash , state_driver , and state . Using each variable, State.initialize then sets a loop that iterates over each entry in state_hash :

 class State def initialize(first_state) state_initializer = StateInitializer.new yield state_initializer @state_hash = state_initializer.state_hash @state_driver = StateDriver.new(self) @state = first_state @state_hash.each do key,val self.instance_eval <<-EOS def self.#{key.id2name}(*arg) if @state_hash[:#{key.id2name}][@state] then @state_hash[:#{key.id2name}][@state].call(@state_driver,*arg) end end EOS end end 

The move_state method is used to create new states and assign them to @state :

 def move_state(new_state) @state=new_state end 

The StateInitializer class defines both initialize which creates the state_hash instance variableand add_event :

 class StateInitializer def initialize @state_hash={} end attr_reader :state_hash def add_event(state,event,&block)                 if not @state_hash[event] then @state_hash[event]={}  end         @state_hash[event][state]=block end end 

Finally, define the class StateDriver with two methods, initialize and move_state :

 class StateDriver def initialize(state_obj) @state_obj=state_obj end def move_state(new_state) @state_obj.move_state(new_state) end end 

The PLAYER.RB File

Now the fun stuffthe player must be defined with a constructor method ( initialize ). You need methods to display the player onscreen ( w , h , and draw ) and move around the screen ( act and move_lr ). But first, the PLAYER.RB file needs help from SYSTEM.RB and STATE.RB:

 require 'system.rb' require 'state.rb' 

Next, designate the class Player and define a few player constants:

 class Player   INIT_DY=-50   DX=20   H=32;W=32   G=20   GRAPH_P1 = setup_bmp 'ruby.bmp' 

These constants initialize the height and width and name of the bitmap image of the player piece. After that, call the initialize method. This method not only calls the SYSTEM.RB code but it also establishes keyboard events for moving the player's ruby piece around the screen, including moving left and right and jumping the piece up:

 def initialize(system)     @system=system     @x=320;@y=200     @dy=0     @state=JT::State.new(:jumping) do i       i.add_event(:walking,:act) do d,key,dt        move_lr(key,dt)        if key.jump then           @dy= INIT_DY          d.move_state :jumping        end       end 

The player pieces must also track the constants set in CONST.RB so that the piece cannot leave the playing field:

 i.add_event(:jumping,:act) do d,key,dt move_lr(key,dt) @y += @dy*dt/100         @dy += G*dt/100         if @y > FLOOR_Y - H then @y = FLOOR_Y - H d.move_state :walking end end 

Included in the Player.initialize method are sample event handlers to track the player piece in case it collides with any other sprites /rects on the playing surface:

 @damage_state = JT::State.new(:normal) do i i.add_event(:normal,:act) { } i.add_event(:normal,:collision_enemy) do d @system.collision_enemy @damage_time=0 d.move_state(:damaged) end i.add_event(:damaged,:act) do d,dt @damage_time+=dt d.move_state(:normal) if @damage_time > DAMAGE_TIME end i.add_event(:damaged,:collision_enemy) { } end end 

After Player.initialize come two quick methods that define the width and height of the player piece:

 def w ;W;end; def h ;H;end; 

You need a draw method to put the previously defined bitmap (in GRAPH_P1 ) onto the screen. Drawing the bitmap is accomplished with the put method:

 def draw(screen) screen.put(GRAPH_P1,@x,@y) end 

The act method is a worker method that checks with STATE.RB and establishes the state.act and damage_state.act instance variables so that the player piece has the functionality from STATE.RB:

 def act(key,dt) @state.act(key,dt) @damage_state.act(dt) end 

Finally, define the player's movement within a move_lr method. Move_lr checks whether the player's key presses move the actual game piece off of the predefined playing surface:

 def move_lr(key,dt) @x-=DX*dt/100 if key.left @x+=DX*dt/100 if key.right @x = LWALL_X if @x< LWALL_X @x = RWALL_X-W if @x > RWALL_X-W end 

The RUBYBOUNCE.RB File

It's in RUBYBOUNCE.RB that SDL is opened and initialized and the actual game loop runs. First, SDL and the other defined files are required:

 require 'sdl' require 'system.rb' require 'state.rb' require 'const.rb' require 'player.rb' 

Initialize SDL with its init method, define the video mode, and establish the surface area with the following two lines:

 SDL.init( SDL::INIT_VIDEO ) screen = SDL::setVideoMode(640,480,16,SDL::SWSURFACE) 

A new structure is established that holds each keypress available to the player:

 Key = Struct.new("Key",:left,:right,:jump,:send) 

The new method constructor is called for each object that must be initialized:

 system=System.new player=Player.new(system) event=SDL::Event.new key=Key.new 

Now that every object you need is established, the game loop is created. First, use tick to establish the time:

 before=now=SDL::getTicks-1 

Then establish a while loop that uses the poll method to check for events from the keyboard:

 while system.continue_game?   if  event.poll != 0 then     if event.type==SDL::Event::QUIT then       break     end     if event.type==SDL::Event::KEYDOWN then       exit if event.keySym==SDL::Key::ESCAPE     end   end 

Each possible key press is queried for by Key::press? :

 SDL::Key::scan key.left = SDL::Key::press?(SDL::Key::LEFT) key.right = SDL::Key::press?(SDL::Key::RIGHT) key.jump = SDL::Key::press?(SDL::Key::UP) key.send = SDL::Key::press?(SDL::Key::DOWN) 

The SDL ticks are checked for in the loop as time moves forward:

 before=now now=SDL::getTicks dt=now-before 

Any actions are fulfilled by calling player.act :

 player.act(key,dt) 

The screen is filled, and the player redrawn with each iteration of the loop:

 screen.fillRect(0,0,640,480,0) player.draw(screen) 

All that is left to do is make sure the SDL screen is flipped and that any garbage is collected:

 ObjectSpace.garbage_collect screen.flip 

[ LiB ]


Game Programming with Pyton, Lua and Ruby
Game Programming with Pyton, Lua and Ruby
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net