├── .gitignore ├── CREDITS.rdoc ├── LICENSE.txt ├── NEWS.rdoc ├── README.rdoc ├── ROADMAP.rdoc ├── Rakefile ├── doc ├── custom_sdl_load_paths.rdoc ├── getting_started.rdoc ├── keyboard_symbols.rdoc ├── macosx_install.rdoc ├── managing_framerate.rdoc └── windows_install.rdoc ├── lib ├── rubygame.rb └── rubygame │ ├── audio.rb │ ├── clock.rb │ ├── color.rb │ ├── color │ ├── models │ │ ├── base.rb │ │ ├── hsl.rb │ │ ├── hsv.rb │ │ ├── rgb.rb │ │ └── rgb255.rb │ └── palettes │ │ ├── css.rb │ │ ├── palette.rb │ │ └── x11.rb │ ├── constants.rb │ ├── deprecated_mixer.rb │ ├── event.rb │ ├── event_actions.rb │ ├── event_handler.rb │ ├── event_hook.rb │ ├── event_triggers.rb │ ├── events.rb │ ├── events │ ├── clock_events.rb │ ├── joystick_events.rb │ ├── keyboard_events.rb │ ├── misc_events.rb │ └── mouse_events.rb │ ├── ftor.rb │ ├── gfx.rb │ ├── gl.rb │ ├── hotspot.rb │ ├── image.rb │ ├── imagefont.rb │ ├── joystick.rb │ ├── keyconstants.rb │ ├── main.rb │ ├── mediabag.rb │ ├── mixer.rb │ ├── music.rb │ ├── named_resource.rb │ ├── new_rect.rb │ ├── queue.rb │ ├── rect.rb │ ├── screen.rb │ ├── sfont.rb │ ├── shared.rb │ ├── sound.rb │ ├── sprite.rb │ ├── surface.rb │ ├── ttf.rb │ └── vector2.rb ├── samples ├── FreeSans.ttf ├── GPL.txt ├── README ├── chimp.bmp ├── chimp.rb ├── demo_draw.rb ├── demo_imagefont.rb ├── demo_music.rb ├── demo_opengl.rb ├── demo_palette.rb ├── demo_rubygame.rb ├── demo_ttf.rb ├── demo_utf8.rb ├── fist.bmp ├── framerate.rb ├── image_viewer.rb ├── load_and_blit.rb ├── palettized.png ├── panda.png ├── punch.wav ├── ruby.png ├── rubygame.png ├── song.ogg ├── term16.png └── whiff.wav └── spec ├── audio_spec.rb ├── clock_spec.rb ├── color_spec.rb ├── event_actions_spec.rb ├── event_handler_spec.rb ├── event_hook_spec.rb ├── event_queue_spec.rb ├── event_triggers_spec.rb ├── ftor_spec.rb ├── has_event_handler_spec.rb ├── image.png ├── image_8bit.png ├── joystick_events_spec.rb ├── keyboard_events_spec.rb ├── misc_events_spec.rb ├── mouse_events_spec.rb ├── music_spec.rb ├── named_resource_spec.rb ├── rect_spec.rb ├── screen_spec.rb ├── short.ogg ├── sound_spec.rb ├── spec_helper.rb ├── surface_spec.rb └── vector2_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | 3 | /pkg/ 4 | /html/ 5 | /ri/ 6 | /coverage/ 7 | -------------------------------------------------------------------------------- /CREDITS.rdoc: -------------------------------------------------------------------------------- 1 | == Developers 2 | 3 | - John Croisant / jacius 4 | Email:: jacius at gmail.com 5 | Role:: Creator; primary developer 6 | Date:: 2004 - present 7 | 8 | == Past Developers 9 | 10 | - Ash Wilson / smashwilson 11 | Email:: smashwilson at gmail.com 12 | Role:: Assistant developer 13 | Date:: 2007 - 2008 14 | 15 | - Roger Ostrander / denor 16 | Email:: atiaxi at gmail.com 17 | Role:: Assistant developer 18 | Date:: 2007 - 2008 19 | 20 | - Rusterholz Stefan / apeiros 21 | Email:: apeiros at gmx.net 22 | Role:: Assistant developer 23 | Date:: 2007 - 2008 24 | 25 | - Bjorn De Meyer / beoran 26 | Email:: beoran at gmail.com 27 | Role:: Assistant developer 28 | Date:: Nov 2007 - 2008 29 | 30 | == Other Source Contributors 31 | 32 | - Randy Eckenrode 33 | Email:: randy.eckenrode at gmail.com 34 | Contribution:: Patch to improve Win32 compatibility (memory allocation, 35 | extconf.rb improvements, misc. other changes). 36 | Date:: received 2006-01-19; applied 2006-02-04 (r143). 37 | 38 | - Yun, Jonghyouk / ageldama 39 | Email:: ageldama at gmail.com 40 | Contribution:: Patch to add TTF#render_utf and TTF#render_unicode methods; 41 | UTF8 demo application. 42 | Date:: received 2007-03-04; applied 2007-04-13 (r235). 43 | 44 | - Johan Berntsson 45 | Email:: johan at microheaven.com 46 | Contribution:: Patch to add Mixer::Music class (music playback). 47 | Date:: 2007-04-27 (r304). 48 | 49 | - Daniel Neis Araujo / danielneis 50 | Email:: danielneis at gmail.com 51 | Contribution:: Patch to add Sprite#undraw (based on UpdateGroup#undraw). 52 | Date:: 2007-07-29 (r460). 53 | 54 | - ippa 55 | Email:: admin at rubylicio.us 56 | Contribution:: Patch to optimize Rect#collide_rect? 57 | Date:: 2007-11-21 (r595) 58 | 59 | - Michael Swiger / mokkan 60 | Email:: mokkan at users.sourceforge.net 61 | Contribution:: Patch to add Screen.icon= 62 | Date:: 2007-11-21 (r597) 63 | 64 | - Elio Cuevas Gómez / elcugo 65 | Email:: elcugo at gmail.com 66 | Contribution:: Patch to fix color conversion segfault on x86_64. 67 | Date:: 2008-07-31 68 | 69 | - Tyler Church 70 | Email:: tylertrain at yahoo.com 71 | Contribution:: Patch to add Rubygame.get_key_state 72 | (now Rubygame.pressed_keys) 73 | Date:: 2010-09-06 74 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Rubygame README 2 | 3 | == What is Rubygame? 4 | 5 | Rubygame is a cross-platform game-development library for Ruby, 6 | inspired by Pygame. Rubygame strives to empower game developers by 7 | providing them with powerful and flexible mid-to-high level tools. 8 | Instead of worrying about low-level technical details, you can focus 9 | your energy on more interesting things (like making a fun game). 10 | 11 | === Relevant Links 12 | 1. Rubygame: http://rubygame.org 13 | 2. Ruby: http://www.ruby-lang.org 14 | 3. Pygame: http://www.pygame.org 15 | 16 | 17 | == Requirements 18 | 19 | You will definitely need these software packages to use Rubygame: 20 | 21 | * Ruby >= 1.8.6 22 | * Or, JRuby >= 1.4 23 | * Ruby-SDL-FFI >= 0.4 24 | * SDL >= 1.2.7 (1.2.14 on MacOS X 10.6) 25 | 26 | It's highly recommended that you have these packages as well, or some 27 | cool features won't be available! 28 | 29 | * SDL_gfx >= 2.0.13 30 | * SDL_image >= 1.2.3 31 | * SDL_mixer >= 1.2.7 32 | * SDL_ttf >= 2.0.6 33 | 34 | For running the spec suite ('rake spec'), you will need: 35 | 36 | * RSpec >= 2.4 37 | * RCov >= 0.9 (only needed for 'rake rcov') 38 | 39 | == Install 40 | 41 | === Basic Install 42 | 43 | The simplest way to install is with RubyGems: 44 | 45 | $ sudo gem install rubygame 46 | 47 | Another way is to install directly from the source: 48 | 49 | $ sudo rake install 50 | 51 | Or generate and install a gem from the source: 52 | 53 | $ rake gem 54 | $ sudo gem install pkg/rubygame-*.gem 55 | 56 | === Generating documentation 57 | 58 | You can generate documentation for Rubygame's API by running: 59 | 60 | $ rake rdoc 61 | 62 | Documentation will be generated as HTML in the 'html' directory. 63 | Open 'html/index.html' in a web browser to get started. 64 | 65 | 66 | == Usage 67 | 68 | See the documentation (online at http://docs.rubygame.org, or 69 | generated locally with the 'rake rdoc' command). 70 | 71 | We also recommend that you take a peek at the demo applications in 72 | the 'samples' directory, especially image_viewer.rb, 73 | demo_rubygame.rb, and chimp.rb. 74 | 75 | 76 | == About the version number 77 | 78 | The version number of Rubygame has a strict meaning, to help you 79 | decide whether the new version might break your application. The 80 | version number has 3 parts: the major number, the minor number, and 81 | the patch number. For the example of Rubygame 2.6.0: 82 | 83 | 2 . 6 . 0 84 | MAJOR . MINOR . PATCH 85 | 86 | When a new release of Rubygame is made, one of the version numbers 87 | will go up, and the numbers that come after it will be reset to 0. 88 | Which number will go up depends on how much the Rubygame API changed: 89 | 90 | - *MAJOR*: API has changed, and old apps _must_ be updated. 91 | An example of this is when a class has been significantly changed, 92 | or a method has renamed/moved to another module and the old name is 93 | removed. 94 | 95 | - *MINOR*: API has changed, but old apps will still work. 96 | An example of this is when a new feature has been added, or a 97 | method has been renamed but the old name is still supported. 98 | 99 | - *PATCH*: API has not changed at all. 100 | An example of this is when a bug has been fixed, or existing code 101 | has been improved beneath the surface. 102 | 103 | 104 | == License 105 | 106 | Rubygame is distributed under the terms of the GNU Lesser GPL. 107 | See LICENSE for more details. 108 | 109 | Some of the sample files are distributed under licenses other than 110 | the GNU Lesser GPL. See 'samples/README' and 'samples/GPL' for more 111 | details. 112 | 113 | 114 | John Croisant (jacius at gmail.com) 115 | -------------------------------------------------------------------------------- /ROADMAP.rdoc: -------------------------------------------------------------------------------- 1 | = ROADMAP 2 | 3 | This represents the direction Rubygame will be going in, but 4 | keep in mind that plans and details may change over time. 5 | 6 | 7 | == 2.X (_possible_ minor releases before 3.0) 8 | 9 | === Focus: Surface & Rect 10 | 11 | - Surface improvements 12 | - Make Surface instances copyable and marshalable 13 | - Support for palettes on 8-bit Surfaces 14 | - Export as OpenGL-ready 32bit pixel data. 15 | - To Fix: New Surfaces never have alpha channels 16 | - To Add: Surface#pixels= (set pixel data directly) 17 | - To Add: Surface.load_from_pixels (create from pixel data) 18 | 19 | - Optimize Rect for speed 20 | 21 | 22 | === Focus: Sprites 23 | 24 | - New sprite system 25 | - New Sprite, Scene, and Camera classes. 26 | - More powerful and simpler to use. 27 | - Integration with the Chipmunk physics library? 28 | 29 | 30 | 31 | == 3.0 (next major release) 32 | 33 | - Backwards compatibility is broken: 34 | - Deprecated classes / methods retired: 35 | - Rubygame::Mixer module. 36 | - Rubygame::Mixer::Sample class. Use Rubygame::Sound. 37 | - Rubygame::Mixer::Music class. Use Rubygame::Music. 38 | - Rubygame::Surface.load_image method. Use Rubygame::Surface.load. 39 | - Rubygame::Ftor class. An alternative class will be provided. 40 | - Rubygame::MediaBag class. Use Surface#[], Sound#[], and Music#[]. 41 | - Surface#get_at 42 | - Sprites::Group#collide_group 43 | 44 | - Change Sprites::Group#collide_group to scrap the 'killa/killb' stuff, 45 | take a block instead. 46 | 47 | 48 | 49 | == Possible future developments 50 | 51 | - Switch from SDL_gfx to SPriG for graphics primitives. 52 | - SPriG provides more functionality and flexibility, and a more 53 | consistent interface. 54 | - How does speed compare? 55 | - Is it buggy? 56 | 57 | - Alternative back-ends: 58 | - OpenGL (faster rendering, better Mac support) 59 | - Allegro? 60 | 61 | - Polygon, circle, and other geometric primitives 62 | - Drawing, with styles? 63 | - OpenGL drawing? 64 | - Collision detection? 65 | - Physical shapes w/ Chipmunk? 66 | - Generate bounding Polygon from an image automatically? 67 | 68 | - Path (Curve?) (graphs/plots for tracing position and other uses) 69 | - Can find the position N units along the path. 70 | - Can draw a line showing the path. 71 | - LinearPath (connect the dots, linear interpolation) 72 | - BezierPath (chain of continuous cubic Bézier curves) 73 | - CodePath (calculated from a code block) 74 | 75 | - Effect (Action?) 76 | - "Attach" to an instance, does something over time. 77 | - E.g. moves a sprite from point A to point B over N seconds. 78 | - Many types of effects, easy to define custom ones. 79 | - Takes parameters: start/end time, others depending on effect. 80 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is the Rakefile for Rubygame. It's used for packaging, 3 | # installing, generating the documentation, and running specs. 4 | # 5 | 6 | 7 | # The version number for Rubygame. 8 | # If you update this, also update lib/rubygame/main.rb. 9 | RUBYGAME_VERSION = [2,6,4] 10 | 11 | 12 | 13 | require 'rubygems' 14 | 15 | require 'rake' 16 | require 'rake/gempackagetask' 17 | require 'rake/rdoctask' 18 | 19 | require "rbconfig" 20 | include Config 21 | 22 | 23 | 24 | ####### 25 | # GEM # 26 | ####### 27 | 28 | gem_spec = Gem::Specification.new do |s| 29 | s.name = "rubygame" 30 | s.version = RUBYGAME_VERSION.join(".") 31 | s.author = "John Croisant" 32 | s.email = "jacius@gmail.com" 33 | s.homepage = "http://rubygame.org/" 34 | s.summary = "Clean and powerful library for game programming" 35 | s.rubyforge_project = "rubygame" 36 | 37 | s.files = FileList.new do |fl| 38 | fl.include("{lib,samples,doc}/**/*") 39 | end 40 | 41 | s.require_paths = ["lib"] 42 | 43 | s.has_rdoc = true 44 | s.extra_rdoc_files = FileList.new do |fl| 45 | fl.include "doc/*.rdoc", "./*.rdoc", "LICENSE.txt" 46 | end 47 | 48 | s.required_ruby_version = ">= 1.8" 49 | s.add_dependency( "rake", ">=0.7.0" ) 50 | s.add_dependency( "ruby-sdl-ffi", ">=0.4.0" ) 51 | s.requirements = ["SDL >= 1.2.7", 52 | "SDL_gfx >= 2.0.10 (optional)", 53 | "SDL_image >= 1.2.3 (optional)", 54 | "SDL_mixer >= 1.2.7 (optional)", 55 | "SDL_ttf >= 2.0.6 (optional)"] 56 | 57 | end 58 | 59 | 60 | Rake::GemPackageTask.new(gem_spec) do |pkg| 61 | pkg.need_tar_bz2 = true 62 | end 63 | 64 | 65 | 66 | ######## 67 | # RDOC # 68 | ######## 69 | 70 | Rake::RDocTask.new do |rd| 71 | rd.main = "README.rdoc" 72 | rd.title = "Rubygame #{RUBYGAME_VERSION.join(".")} Docs" 73 | rd.rdoc_files.include("lib/rubygame/**/*.rb", 74 | "doc/*.rdoc", 75 | "./*.rdoc", 76 | "LICENSE.txt") 77 | end 78 | 79 | desc "Generate RI-formatted docs." 80 | task(:ri) do 81 | sh('rdoc --ri --threads=1 --force-update --output "./ri" ./lib') 82 | end 83 | 84 | 85 | ########### 86 | # VERBOSE # 87 | ########### 88 | 89 | desc "Run tasks more verbosely" 90 | task :verbose do 91 | ENV["SPEC_OPTS"] = "--format documentation #{ENV["SPEC_OPTS"]}" 92 | end 93 | 94 | 95 | ######### 96 | # CLEAN # 97 | ######### 98 | 99 | require 'rake/clean' 100 | task(:clean) { puts "Cleaning out temporary generated files" } 101 | task(:clobber) { puts "Cleaning out final generated files" } 102 | 103 | CLOBBER.include("ri") 104 | 105 | 106 | ########### 107 | # INSTALL # 108 | ########### 109 | 110 | task :install do |task| 111 | sitelibdir = (ENV["RUBYLIBDIR"] or CONFIG["sitelibdir"]) 112 | 113 | puts "Installing to #{sitelibdir}" 114 | 115 | files = FileList.new do |fl| 116 | fl.include("lib/**/*.rb") 117 | end 118 | 119 | files.each do |f| 120 | dir = File.join(sitelibdir, File.dirname(f).sub('lib',''), "") 121 | mkdir_p dir 122 | cp f, dir 123 | end 124 | end 125 | 126 | 127 | 128 | ######### 129 | # SPECS # 130 | ######### 131 | 132 | begin 133 | require 'rspec/core/rake_task' 134 | 135 | rspec_opts = '-r spec/spec_helper.rb' 136 | 137 | desc "Run all specs" 138 | RSpec::Core::RakeTask.new do |t| 139 | ENV["RUBYGAME_NEWRECT"] = "true" 140 | t.pattern = 'spec/*_spec.rb' 141 | t.rspec_opts = rspec_opts 142 | end 143 | 144 | 145 | namespace :spec do 146 | desc "Run all specs" 147 | RSpec::Core::RakeTask.new(:all) do |t| 148 | ENV["RUBYGAME_NEWRECT"] = "true" 149 | t.pattern = 'spec/*_spec.rb' 150 | t.rspec_opts = rspec_opts 151 | end 152 | 153 | desc "Run spec/[name]_spec.rb (e.g. 'color')" 154 | task :name do 155 | puts( "This is just a stand-in spec.", 156 | "Run rake spec:[name] where [name] is e.g. 'color', 'music'." ) 157 | end 158 | 159 | desc "Run specific examples from spec/[name]_spec.rb" 160 | task "name:\"example\"" do 161 | puts( "This is just a stand-in spec.", 162 | "Run rake spec:[name]:\"[example]\" where [name] is e.g."+ 163 | "'color', and [example] is a pattern matching an example." ) 164 | end 165 | end 166 | 167 | 168 | rule(/spec:.+/) do |t| 169 | 170 | # Matches e.g. 'spec:foo' and 'spec:foo:"Example pattern"' 171 | spec_regexp = /spec:([^:]+)(?::(.+))?/ 172 | 173 | match = t.name.match(spec_regexp) 174 | 175 | unless match 176 | puts "Invalid spec task: #{t.name}" 177 | exit 1 178 | end 179 | 180 | name = match[1] 181 | example = match[2] # optional 182 | 183 | pattern = File.join('spec', '%s_spec.rb'%name) 184 | path = File.join( File.dirname(__FILE__), pattern ) 185 | 186 | if File.exist? path 187 | RSpec::Core::RakeTask.new(name) do |t| 188 | t.pattern = pattern 189 | t.rspec_opts = rspec_opts 190 | if example 191 | t.rspec_opts += " -e #{example.inspect}" 192 | end 193 | end 194 | 195 | puts "\nRunning %s"%pattern 196 | 197 | Rake::Task[name].invoke 198 | else 199 | puts "File does not exist: %s"%pattern 200 | end 201 | 202 | end 203 | 204 | 205 | ######## 206 | # RCOV # 207 | ######## 208 | 209 | desc "Run all specs with rcov" 210 | RSpec::Core::RakeTask.new(:rcov) do |t| 211 | ENV["RUBYGAME_NEWRECT"] = "true" 212 | t.pattern = 'spec/*_spec.rb' 213 | t.rcov = true 214 | t.rspec_opts = rspec_opts 215 | end 216 | 217 | 218 | namespace :rcov do 219 | desc "Run all specs with rcov" 220 | RSpec::Core::RakeTask.new(:all) do |t| 221 | ENV["RUBYGAME_NEWRECT"] = "true" 222 | t.pattern = 'spec/*_spec.rb' 223 | t.rcov = true 224 | t.rspec_opts = rspec_opts 225 | end 226 | 227 | desc "Run spec/[name]_spec.rb (e.g. 'color') with rcov" 228 | task :name do 229 | puts( "This is just a stand-in spec.", 230 | "Run rake rcov:[name] where [name] is e.g. 'color', 'music'." ) 231 | end 232 | end 233 | 234 | 235 | rule(/rcov:.+/) do |t| 236 | name = t.name.gsub("rcov:","") 237 | 238 | pattern = File.join('spec', '%s_spec.rb'%name) 239 | path = File.join( pattern ) 240 | 241 | if File.exist? path 242 | RSpec::Core::RakeTask.new(name) do |t| 243 | t.pattern = pattern 244 | t.rcov = true 245 | t.rspec_opts = rspec_opts 246 | end 247 | 248 | puts "\nRunning %s"%pattern 249 | 250 | Rake::Task[name].invoke 251 | else 252 | puts "File does not exist: %s"%pattern 253 | end 254 | 255 | end 256 | 257 | rescue LoadError 258 | 259 | error = "ERROR: rspec >= 2.0 is not installed?" 260 | 261 | task :spec do 262 | puts error 263 | end 264 | 265 | rule( /spec:.*/ ) do 266 | puts error 267 | end 268 | 269 | task :rcov do 270 | puts error 271 | end 272 | 273 | rule( /rcov:.*/ ) do 274 | puts error 275 | end 276 | 277 | end 278 | -------------------------------------------------------------------------------- /doc/custom_sdl_load_paths.rdoc: -------------------------------------------------------------------------------- 1 | = Custom SDL Load Paths 2 | 3 | In Rubygame 2.6 and later, you can tell Rubygame to look in custom 4 | locations to find the SDL libraries (e.g. DLL files). This is mostly 5 | intended for when you are packaging your application in a stand-alone 6 | package (e.g. with RubyScript2Exe), and want to distribute the SDL 7 | libraries with your application, so that users do not need to install 8 | SDL. 9 | 10 | == Setting Load Paths 11 | 12 | Setting custom load paths is easy: just set the SDL_PATHS constant 13 | to an Array of directories before you do `require "rubygame"`. 14 | Note: You really must set SDL_PATHS *before* loading Rubygame! 15 | 16 | Rubygame will look first in the paths in SDL_PATHS, in the order they 17 | are given in the array. If Rubygame can't find a SDL library in any of 18 | the SDL_PATHS directories, Rubygame will then search for the library 19 | in some common library paths for the current operating system (e.g. 20 | "C:\windows\system32\" on Windows, "/usr/lib/" on Linux, etc.). 21 | 22 | For example, if the SDL libraries are in "mygame/libs/", and your 23 | main script is "mygame/mygame.rb", you could do the following in 24 | "mygame/mygame.rb": 25 | 26 | main_dir = File.dirname(__FILE__) 27 | SDL_PATHS = [ File.join( main_dir, "libs" ) ] 28 | 29 | require "rubygame" 30 | 31 | # ... the rest of your code ... 32 | 33 | == Per-OS Load Paths 34 | 35 | If you prefer to split the libraries into a separate directory for 36 | each operating system, you can set SDL_PATHS to a Hash instead. For 37 | example: 38 | 39 | main_dir = File.dirname(__FILE__) 40 | SDL_PATHS = { 41 | /linux/ => File.join( main_dir, "libs", "linux" ), 42 | /bsd/ => File.join( main_dir, "libs", "bsd" ), 43 | /darwin/ => File.join( main_dir, "libs", "mac" ), 44 | /windows/ => File.join( main_dir, "libs", "windows" ), 45 | } 46 | 47 | require "rubygame" 48 | 49 | # ... the rest of your code ... 50 | 51 | Each key in the hash should be a Regexp that matches an OS name. As of 52 | this writing (October 2009), the list of recognized OS names is: 53 | 54 | * "darwin" (MacOS X) 55 | * "freebsd" 56 | * "linux" 57 | * "openbsd" 58 | * "solaris" 59 | * "windows" 60 | 61 | == Library File Names 62 | 63 | Rubygame has certain expectations about the names of library files for 64 | each operating system ("[NAME]" is replaced with e.g. "SDL"): 65 | 66 | 'Nix:: lib[NAME].so 67 | Mac:: lib[NAME].dylib or [NAME].framework/[NAME] 68 | Windows:: [NAME].dll 69 | 70 | The specific libraries used by Rubygame are: 71 | 72 | SDL:: libSDL.so; libSDL.dylib or SDL.framework/SDL; SDL.dll 73 | SDL_gfx:: libSDL_gfx.so; libSDL_gfx.dylib or SDL_gfx.framework/SDL_gfx; SDL_gfx.dll 74 | SDL_image:: libSDL_image.so; libSDL_image.dylib or SDL_image.framework/SDL_image; SDL_image.dll 75 | SDL_mixer:: libSDL_mixer.so; libSDL_mixer.dylib or SDL_mixer.framework/SDL_mixer; SDL_mixer.dll 76 | SDL_ttf:: libSDL_ttf.so; libSDL_ttf.dylib or SDL_ttf.framework/SDL_ttf; SDL_ttf.dll 77 | 78 | SDL is absolutely required. The others are optional, but strongly 79 | recommended, because they provide a lot of extra functionality. 80 | -------------------------------------------------------------------------------- /doc/getting_started.rdoc: -------------------------------------------------------------------------------- 1 | = Getting Started with Rubygame 2 | 3 | This guide is also available on the Rubygame wiki: 4 | 5 | * http://rubygame.org/wiki/Getting_started 6 | 7 | == About Rubygame 8 | 9 | Rubygame is a cross-platform game-development library for Ruby, 10 | inspired by Pygame. Rubygame strives to empower game developers by 11 | providing them with powerful and flexible mid-to-high level tools. 12 | Instead of worrying about low-level technical details, you can focus 13 | your energy on more interesting things (like making a fun game). 14 | 15 | == Installing Rubygame 16 | 17 | Installation guides for Mac and Windows are available in the 'doc' 18 | directory: 19 | 20 | Mac:: {doc/macosx_install.rdoc}[link:doc/macosx_install.rdoc] 21 | Windows:: {doc/windows_install.rdoc}[link:doc/windows_install.rdoc] 22 | 23 | You can also find installation instructions for many operating systems 24 | online at the Rubygame wiki: 25 | 26 | * http://rubygame.org/wiki/Installation_Instructions 27 | 28 | == Guides and tutorials 29 | 30 | The Rubygame wiki has a list of guides and tutorials to help you get 31 | started using Rubygame: 32 | 33 | * http://rubygame.org/wiki/Guides_and_tutorials 34 | 35 | == Suggested order of reading 36 | 37 | To get acquainted with Rubygame, we recommend exploring the available classes 38 | and modules in this order. 39 | 40 | First, take a look at the most fundamental classes: 41 | 42 | Rubygame::Surface:: An image canvas that you can load from an image 43 | file, draw shapes on and "blit" (copy) to other 44 | Surfaces (or the Screen). 45 | Rubygame::Screen:: A special Surface that is displayed to the user. 46 | Everything drawn on the Screen appears in the 47 | application window on the user's desktop. 48 | Rubygame::Rect:: A rectangular area, used for choosing what parts 49 | of a Surface to copy from or to, among other uses. 50 | 51 | As a next step, read about the EventQueue, EventHandler, and the user 52 | input event classes. These allow you to handle keyboard, mouse, and 53 | joystick input from the user, among other things. By creating your own 54 | event classes, these systems can also be used to represent things 55 | occuring within your game, such as the player losing a life or the 56 | level being completed. 57 | 58 | Rubygame::EventQueue:: A special Array that caches events to be 59 | handled later. 60 | Rubygame::EventHandler:: A flexible hook-based system for performing 61 | actions when certain types of events occur. 62 | Rubygame::Events:: A module containing all the built-in event 63 | classes in Rubygame. 64 | 65 | Finally, you may be interested in these features: 66 | 67 | Rubygame::Sprites:: A simple and flexible game object framework. 68 | Rubygame::Sound:: Play sound effects to make your game more lively. 69 | Rubygame::Music:: Play music to enhance your game's mood. 70 | Rubygame::Clock:: Monitor and control framerate to lower CPU use. 71 | Rubygame::TTF:: Render text with TrueType fonts. 72 | Rubygame::SFont:: Render text with bitmap-based fonts. 73 | Rubygame::GL:: Enable OpenGL 2D & 3D graphics. 74 | 75 | There are several sample applications in the 'samples' directory 76 | packaged with Rubygame which can also help you get started. 77 | -------------------------------------------------------------------------------- /doc/keyboard_symbols.rdoc: -------------------------------------------------------------------------------- 1 | This is a list of every possible key that a keyboard event can have in 2 | Rubygame. You can view this list in a nicer form at 3 | http://rubygame.org/wiki/Keyboard_Symbols . 4 | 5 | The names on the left represent constants in the Rubygame module, e.g. 6 | Rubygame::K_BACKSPACE. These were used in the legacy (before Rubygame 7 | 2.4) event classes: Rubygame::KeyDownEvent and Rubygame::KeyUpEvent. 8 | 9 | The legacy event classes are deprecated and will be removed in 10 | Rubygame 3.0, so you should not use them. 11 | 12 | The symbols on the right are used in the new event classes: 13 | Rubygame::Events::KeyPressed and Rubygame::Events::KeyReleased. 14 | 15 | K_BACKSPACE :backspace 16 | K_TAB :tab 17 | K_CLEAR :clear 18 | K_RETURN :return 19 | K_PAUSE :pause 20 | K_ESCAPE :escape 21 | K_SPACE :space 22 | K_EXCLAIM :exclamation_mark 23 | K_QUOTEDBL :double_quote 24 | K_HASH :hash 25 | K_DOLLAR :dollar 26 | K_AMPERSAND :ampersand 27 | K_QUOTE :quote 28 | K_LEFTPAREN :left_parenthesis 29 | K_RIGHTPAREN :right_parenthesis 30 | K_ASTERISK :asterisk 31 | K_PLUS :plus 32 | K_COMMA :comma 33 | K_MINUS :minus 34 | K_PERIOD :period 35 | K_SLASH :slash 36 | K_0 :number_0 37 | K_1 :number_1 38 | K_2 :number_2 39 | K_3 :number_3 40 | K_4 :number_4 41 | K_5 :number_5 42 | K_6 :number_6 43 | K_7 :number_7 44 | K_8 :number_8 45 | K_9 :number_9 46 | K_COLON :":" (will become :colon in Rubygame 3.0) 47 | K_SEMICOLON :semicolon 48 | K_LESS :less_than 49 | K_EQUALS :equals 50 | K_GREATER :greater_than 51 | K_QUESTION :question_mark 52 | K_AT :at 53 | K_LEFTBRACKET :left_bracket 54 | K_BACKSLASH :backslash 55 | K_RIGHTBRACKET :right_bracket 56 | K_CARET :caret 57 | K_UNDERSCORE :underscore 58 | K_BACKQUOTE :backquote 59 | K_A :a 60 | K_B :b 61 | K_C :c 62 | K_D :d 63 | K_E :e 64 | K_F :f 65 | K_G :g 66 | K_H :h 67 | K_I :i 68 | K_J :j 69 | K_K :k 70 | K_L :l 71 | K_M :m 72 | K_N :n 73 | K_O :o 74 | K_P :p 75 | K_Q :q 76 | K_R :r 77 | K_S :s 78 | K_T :t 79 | K_U :u 80 | K_V :v 81 | K_W :w 82 | K_X :x 83 | K_Y :y 84 | K_Z :z 85 | K_DELETE :delete 86 | K_WORLD_0 :world_0 87 | K_WORLD_1 :world_1 88 | K_WORLD_2 :world_2 89 | K_WORLD_3 :world_3 90 | K_WORLD_4 :world_4 91 | K_WORLD_5 :world_5 92 | K_WORLD_6 :world_6 93 | K_WORLD_7 :world_7 94 | K_WORLD_8 :world_8 95 | K_WORLD_9 :world_9 96 | K_WORLD_10 :world_10 97 | K_WORLD_11 :world_11 98 | K_WORLD_12 :world_12 99 | K_WORLD_13 :world_13 100 | K_WORLD_14 :world_14 101 | K_WORLD_15 :world_15 102 | K_WORLD_16 :world_16 103 | K_WORLD_17 :world_17 104 | K_WORLD_18 :world_18 105 | K_WORLD_19 :world_19 106 | K_WORLD_20 :world_20 107 | K_WORLD_21 :world_21 108 | K_WORLD_22 :world_22 109 | K_WORLD_23 :world_23 110 | K_WORLD_24 :world_24 111 | K_WORLD_25 :world_25 112 | K_WORLD_26 :world_26 113 | K_WORLD_27 :world_27 114 | K_WORLD_28 :world_28 115 | K_WORLD_29 :world_29 116 | K_WORLD_30 :world_30 117 | K_WORLD_31 :world_31 118 | K_WORLD_32 :world_32 119 | K_WORLD_33 :world_33 120 | K_WORLD_34 :world_34 121 | K_WORLD_35 :world_35 122 | K_WORLD_36 :world_36 123 | K_WORLD_37 :world_37 124 | K_WORLD_38 :world_38 125 | K_WORLD_39 :world_39 126 | K_WORLD_40 :world_40 127 | K_WORLD_41 :world_41 128 | K_WORLD_42 :world_42 129 | K_WORLD_43 :world_43 130 | K_WORLD_44 :world_44 131 | K_WORLD_45 :world_45 132 | K_WORLD_46 :world_46 133 | K_WORLD_47 :world_47 134 | K_WORLD_48 :world_48 135 | K_WORLD_49 :world_49 136 | K_WORLD_50 :world_50 137 | K_WORLD_51 :world_51 138 | K_WORLD_52 :world_52 139 | K_WORLD_53 :world_53 140 | K_WORLD_54 :world_54 141 | K_WORLD_55 :world_55 142 | K_WORLD_56 :world_56 143 | K_WORLD_57 :world_57 144 | K_WORLD_58 :world_58 145 | K_WORLD_59 :world_59 146 | K_WORLD_60 :world_60 147 | K_WORLD_61 :world_61 148 | K_WORLD_62 :world_62 149 | K_WORLD_63 :world_63 150 | K_WORLD_64 :world_64 151 | K_WORLD_65 :world_65 152 | K_WORLD_66 :world_66 153 | K_WORLD_67 :world_67 154 | K_WORLD_68 :world_68 155 | K_WORLD_69 :world_69 156 | K_WORLD_70 :world_70 157 | K_WORLD_71 :world_71 158 | K_WORLD_72 :world_72 159 | K_WORLD_73 :world_73 160 | K_WORLD_74 :world_74 161 | K_WORLD_75 :world_75 162 | K_WORLD_76 :world_76 163 | K_WORLD_77 :world_77 164 | K_WORLD_78 :world_78 165 | K_WORLD_79 :world_79 166 | K_WORLD_80 :world_80 167 | K_WORLD_81 :world_81 168 | K_WORLD_82 :world_82 169 | K_WORLD_83 :world_83 170 | K_WORLD_84 :world_84 171 | K_WORLD_85 :world_85 172 | K_WORLD_86 :world_86 173 | K_WORLD_87 :world_87 174 | K_WORLD_88 :world_88 175 | K_WORLD_89 :world_89 176 | K_WORLD_90 :world_90 177 | K_WORLD_91 :world_91 178 | K_WORLD_92 :world_92 179 | K_WORLD_93 :world_93 180 | K_WORLD_94 :world_94 181 | K_WORLD_95 :world_95 182 | K_KP0 :keypad_0 183 | K_KP1 :keypad_1 184 | K_KP2 :keypad_2 185 | K_KP3 :keypad_3 186 | K_KP4 :keypad_4 187 | K_KP5 :keypad_5 188 | K_KP6 :keypad_6 189 | K_KP7 :keypad_7 190 | K_KP8 :keypad_8 191 | K_KP9 :keypad_9 192 | K_KP_PERIOD :keypad_period 193 | K_KP_DIVIDE :keypad_divide 194 | K_KP_MULTIPLY :keypad_multiply 195 | K_KP_MINUS :keypad_minus 196 | K_KP_PLUS :keypad_plus 197 | K_KP_ENTER :keypad_enter 198 | K_KP_EQUALS :keypad_equals 199 | K_UP :up 200 | K_DOWN :down 201 | K_RIGHT :right 202 | K_LEFT :left 203 | K_INSERT :insert 204 | K_HOME :home 205 | K_END :end 206 | K_PAGEUP :page_up 207 | K_PAGEDOWN :page_down 208 | K_F1 :f1 209 | K_F2 :f2 210 | K_F3 :f3 211 | K_F4 :f4 212 | K_F5 :f5 213 | K_F6 :f6 214 | K_F7 :f7 215 | K_F8 :f8 216 | K_F9 :f9 217 | K_F10 :f10 218 | K_F11 :f11 219 | K_F12 :f12 220 | K_F13 :f13 221 | K_F14 :f14 222 | K_F15 :f15 223 | K_NUMLOCK :numlock 224 | K_CAPSLOCK :caps_lock 225 | K_SCROLLOCK :scroll_lock 226 | K_RSHIFT :right_shift 227 | K_LSHIFT :left_shift 228 | K_RCTRL :right_ctrl 229 | K_LCTRL :left_ctrl 230 | K_RALT :right_alt 231 | K_LALT :left_alt 232 | K_RMETA :right_meta 233 | K_LMETA :left_meta 234 | K_LSUPER :left_super 235 | K_RSUPER :right_super 236 | K_MODE :alt_gr 237 | K_HELP :help 238 | K_PRINT :print_screen 239 | K_SYSREQ :sys_req 240 | K_BREAK :break 241 | K_MENU :menu 242 | K_POWER :power 243 | K_EURO :euro 244 | -------------------------------------------------------------------------------- /doc/macosx_install.rdoc: -------------------------------------------------------------------------------- 1 | Note: You can view the latest version of this guide online at the 2 | Rubygame wiki: http://rubygame.org/wiki/Mac_Installation_Guide 3 | 4 | == Installing Xcode 5 | 6 | Rubygame does not need to be compiled anymore (since version 2.6), 7 | but you will need to install the Xcode development tools in order 8 | to install the dependencies with Fink or Darwinports, as well as 9 | to compile rsdl. 10 | 11 | You can find Xcode on your Mac OS X installation disc. Or, you can 12 | download Xcode online. You must register for a (free) Mac developer 13 | account for this. 14 | 15 | * Mac OS X 10.5 (Leopard) and 10.6 (Snow Leopard): 16 | {Xcode 3.1}[https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=20414] 17 | 18 | * Mac OS X 10.3 (Panther) and 10.4 (Tiger): 19 | {Xcode 2.5}[http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=19907] 20 | 21 | == Installing dependencies... 22 | 23 | You have two options for installing the software libraries that 24 | Rubygame depends on: Fink or Darwinports. Which option you choose is 25 | just a matter of preference. 26 | 27 | === ...with {Fink}[http://www.finkproject.org/] 28 | 29 | 1. {Install Fink}[http://www.finkproject.org/download/index.php?phpLang=en] 30 | and update to the latest version. 31 | 2. Open a new terminal (can be found in /Applications/Utilities). 32 | 3. Install SDL: 33 | 34 | sudo apt-get install sdl 35 | 36 | 4. (Optional, but *highly* recommended:) Install the SDL companion 37 | libraries: 38 | 39 | sudo apt-get install sdl-gfx13 sdl-image sdl-mixer sdl-ttf 40 | 41 | Thanks to Matt Crinklaw for these instructions. 42 | 43 | === ...with {Darwinports}[http://darwinports.com/] 44 | 45 | 1. {Install Darwinports}[http://darwinports.com/download/] (you must 46 | register first). 47 | 2. Open a new terminal (can be found in /Applications/Utilities). 48 | 3. Install SDL: 49 | 50 | sudo port install libsdl 51 | 52 | 4. (Optional, but *highly* recommended:) Install the SDL companion 53 | libraries: 54 | 55 | sudo port install libsdl_gfx libsdl_image libsdl_mixer libsdl_ttf 56 | 57 | Thanks to Raffael Mancini (sepisultrum) for these instructions. 58 | 59 | == Installing rsdl 60 | 61 | In order to use Rubygame applications on Mac OS X, you must use a special wrapper for the Ruby interpreter, called rsdl. 62 | 63 | 1. {Get rsdl}[http://github.com/knu/rsdl]. 64 | 2. From within the rsdl directory: 65 | 66 | ruby ./configure.rb 67 | make 68 | sudo cp rsdl /usr/local/bin/ 69 | 70 | == Installing Rubygame 71 | 72 | The simplest way to install Rubygame is: gem install rubygame 73 | 74 | Or you can {download the source 75 | from Github}[http://github.com/jacius/rubygame/] and follow the installation instructions in README. 76 | 77 | == Running an application 78 | 79 | If all went well, you can now run Rubygame applications like so: 80 | 81 | rsdl a_rubygame_app.rb 82 | 83 | If you have any trouble installing Rubygame, please post in the 84 | {forums}[http://rubygame.org/forums/]. 85 | -------------------------------------------------------------------------------- /doc/windows_install.rdoc: -------------------------------------------------------------------------------- 1 | Note: You can view the latest version of this guide online at the 2 | Rubygame wiki: http://rubygame.org/wiki/Windows_Installation_Guide 3 | 4 | == Gather Dependencies 5 | 6 | * {Ruby}[http://www.ruby-lang.org/en/downloads/], the language itself. 7 | The easiest option here is the one-click installer. This will 8 | install Ruby and a number of useful libraries without any hassle. 9 | 10 | * {SDL}[http://www.libsdl.org/download-1.2.php]. 11 | Download the Win32 binary package, e.g. "SDL-1.2.14-win32.zip". 12 | 13 | The rest are optional dependencies, but highly recommended. If you 14 | don't have them, you won't be able to use certain features of 15 | Rubygame. 16 | 17 | * {SDL_image}[http://www.libsdl.org/projects/SDL_image/]. 18 | Grab the Win32 binary package, e.g. "SDL_image-1.2.8-win32.zip". 19 | 20 | * {SDL_mixer}[http://www.libsdl.org/projects/SDL_mixer/]. 21 | Just like before, grab the Win32 binary package, e.g. 22 | "SDL_mixer-1.2.9-win32.zip". 23 | 24 | * {SDL_ttf}[http://www.libsdl.org/projects/SDL_ttf/]. 25 | Again, get the Win32 binary package, e.g. "SDL_ttf-2.0.9-win32.zip". 26 | 27 | * {SDL_gfx}[http://www.ferzkopp.net/joomla/content/view/19/14/] 28 | ({compiled version}[http://download.rubygame.org/files/extras/]). 29 | Many thanks to a generous Rubygame user (bmatthew1) for providing a 30 | compiled Windows DLL for SDL_gfx! 31 | 32 | Once you have downloaded everything, unzip them and copy the *.dll 33 | files into the C:\windows\system32\ directory. This will 34 | allow the libraries to be detected and loaded by Rubygame. 35 | 36 | == Install Rubygame 37 | 38 | The simplest way to install Rubygame is: gem install rubygame 39 | 40 | Or you can {download the source from 41 | Github}[http://github.com/jacius/rubygame/] and follow the 42 | installation instructions in README. 43 | 44 | == Conclusion 45 | 46 | If all goes well, you have successfully installed Rubygame. Try to 47 | execute require 'rubygame' in an irb session and run the 48 | provided samples to ensure that everything is acceptable. 49 | 50 | If you have any trouble installing Rubygame, please post in the 51 | {forums}[http://rubygame.org/forums/]. 52 | -------------------------------------------------------------------------------- /lib/rubygame.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 4 | # Copyright (C) 2004-2009 John Croisant 5 | # 6 | # This library is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU Lesser General Public 8 | # License as published by the Free Software Foundation; either 9 | # version 2.1 of the License, or (at your option) any later version. 10 | # 11 | # This library is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # Lesser General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public 17 | # License along with this library; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | # 20 | #++ 21 | 22 | 23 | module Rubygame 24 | 25 | # These features are safe to autoload when needed. 26 | autoload :Clock, "rubygame/clock" 27 | autoload :Color, "rubygame/color" 28 | autoload :GL, "rubygame/gl" 29 | autoload :ImageFont, "rubygame/imagefont" 30 | autoload :Joystick, "rubygame/joystick" 31 | autoload :NamedResource, "rubygame/named_resource" 32 | autoload :EventQueue, "rubygame/queue" 33 | autoload :Sprites, "rubygame/sprite" 34 | autoload :Vector2, "rubygame/vector2" 35 | 36 | 37 | # These ones must be required at startup. 38 | # Note: rubygame/screen is intentionally loaded late. 39 | require "rubygame/main" 40 | require "rubygame/shared" 41 | require "rubygame/constants" 42 | require "rubygame/event" 43 | require "rubygame/events" 44 | require "rubygame/event_handler" 45 | require "rubygame/surface" 46 | 47 | 48 | # If RUBYGAME_NEWRECT is set, load the new Rect class. Otherwise load 49 | # the old Rect class, for backwards compatibility. The old Rect class 50 | # will be removed in Rubygame 3.0. 51 | if /^(1|t|true|y|yes)$/i =~ ENV["RUBYGAME_NEWRECT"] 52 | require "rubygame/new_rect" 53 | else 54 | require "rubygame/rect" 55 | end 56 | 57 | 58 | # SDL_gfx is optional, rescue if it fails. 59 | begin 60 | require "rubygame/gfx" 61 | rescue LoadError => e 62 | puts( "Warning: Could not load SDL_gfx! " + 63 | "Continuing anyway, but some Surface methods will be missing.\n" + 64 | "Error message was: #{e.message.inspect}" ) 65 | end 66 | 67 | 68 | # SDL_image is optional, rescue if it fails. 69 | begin 70 | require "rubygame/image" 71 | rescue LoadError => e 72 | puts( "Warning: Could not load SDL_image! " + 73 | "Continuing anyway, but image loading will be missing.\n" + 74 | "Error message was: #{e.message.inspect}" ) 75 | end 76 | 77 | 78 | # SDL_mixer is optional, rescue if it fails. 79 | begin 80 | require "rubygame/mixer" 81 | rescue LoadError => e 82 | puts( "Warning: Could not load SDL_mixer! " + 83 | "Continuing anyway, but audio features will be missing.\n" + 84 | "Error message was: #{e.message.inspect}" ) 85 | end 86 | 87 | 88 | # SDL_ttf is optional, rescue if it fails. 89 | begin 90 | require "rubygame/ttf" 91 | rescue LoadError => e 92 | puts( "Warning: Could not load SDL_ttf! " + 93 | "Continuing anyway, but the TTF class will be missing.\n" + 94 | "Error message was: #{e.message.inspect}" ) 95 | end 96 | 97 | 98 | # Loaded late so Screen can undefine some inherited Surface methods. 99 | require "rubygame/screen" 100 | 101 | 102 | # Handle initialization automatically unless the RUBYGAME_NOINIT 103 | # environmental variable is set to something truthy. 104 | unless /^(1|t|true|y|yes)$/i =~ ENV["RUBYGAME_NOINIT"] 105 | Rubygame.init 106 | at_exit { Rubygame.quit } 107 | end 108 | 109 | end 110 | -------------------------------------------------------------------------------- /lib/rubygame/audio.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2009 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | module Rubygame 23 | 24 | # call-seq: 25 | # open_audio( options={:buffer=>1024, :channels=>2, :frequency=>22050} ) 26 | # 27 | # Initializes the audio device using the given settings. 28 | # 29 | # NOTE: Audio will be automatically opened when Rubygame::Sound or 30 | # Rubygame::Music are first used. You only need to open audio 31 | # manually if you want settings different from the default, or if 32 | # you are using the older, deprecated Music and Sample classes from 33 | # the Rubygame::Mixer module. 34 | # 35 | # If audio is already open, this method has no effect, and returns false. 36 | # If you want to change audio settings, you must #close_audio() and 37 | # then open it again. 38 | # 39 | # options:: A Hash of any of the following options. (Hash, optional) 40 | # 41 | # :frequency:: output sample rate in audio samples per second 42 | # (Hz). Affects the quality of the sound output, at 43 | # the expense of CPU usage. If omitted, the default 44 | # (22050) is used. The default is recommended for 45 | # most games. 46 | # 47 | # :channels:: output sound channels. Use 2 for stereo, 1 for mono. 48 | # If omitted, the default (2) is used. 49 | # 50 | # :buffer:: size of the sound buffer, in bytes. Must be a 51 | # power of 2 (e.g. 512, 1024, 2048). If omitted, 52 | # the default (1024) is used. If your game is 53 | # fast-paced, you may want to use a smaller value 54 | # to reduce audio delay, the time between when you 55 | # play a sound and when it is heard. 56 | # 57 | # Returns:: true if the audio was newly opened by this action, or 58 | # false if it was already open before this action. 59 | # 60 | # May raise:: SDLError, if initialization fails. 61 | # ArgumentError, if an invalid value is given for any option. 62 | # 63 | def self.open_audio( options={} ) 64 | return false if audio_open? 65 | 66 | unless options.kind_of? Hash 67 | raise TypeError, "invalid options Hash: #{options.inspect}" 68 | end 69 | 70 | buff = (options[:buffer] or 1024) 71 | chan = (options[:channels] or 2) 72 | freq = (options[:frequency] or SDL::Mixer::DEFAULT_FREQUENCY) 73 | 74 | # In general, format should always be the default. 75 | frmt = SDL::Mixer::DEFAULT_FORMAT 76 | 77 | 78 | buff = if( buff <= 0 ) 79 | raise ArgumentError, "buffer size must be positive (got #{buff})" 80 | elsif( buff & (buff - 1) != 0 ) 81 | raise( ArgumentError, "buffer size must be a power of 2 "+ 82 | "(e.g. 512, 1024) (got #{buff})" ) 83 | else 84 | buff.to_i 85 | end 86 | 87 | 88 | chan = if( chan != 1 && chan != 2 ) 89 | raise( ArgumentError, 90 | "channels must be 1 (mono) or 2 (stereo) (got #{chan})" ) 91 | else 92 | chan.to_i 93 | end 94 | 95 | 96 | freq = if( freq <= 0 ) 97 | raise ArgumentError, "frequency must be positive (got #{freq})" 98 | else 99 | freq.to_i 100 | end 101 | 102 | result = SDL::Mixer.OpenAudio(freq, frmt, chan, buff) 103 | 104 | if( result < 0 ) 105 | raise Rubygame::SDLError, "Could not open audio: #{SDL.GetError()}" 106 | end 107 | 108 | return true 109 | end 110 | 111 | 112 | 113 | # Deinitializes and closes the audio device. If audio was not open, 114 | # this method does nothing, and returns false. See also #open_audio(). 115 | # 116 | # NOTE: The audio will be automatically closed when the program 117 | # exits. You only need to close audio manually if you want to 118 | # call #open_audio with different settings. 119 | # 120 | # Returns:: true if the audio was open before this action. 121 | # 122 | def self.close_audio 123 | if audio_open? 124 | SDL::Mixer.CloseAudio() 125 | return true 126 | else 127 | return false 128 | end 129 | end 130 | 131 | 132 | def self.audio_open? # :nodoc: 133 | SDL::Mixer.QuerySpec(nil,nil,nil) > 0 134 | end 135 | 136 | 137 | # Returns the name of the audio driver that SDL is using. 138 | # This method opens the audio device if it is not open already. 139 | # 140 | # May raise an SDLError if the audio device could not be opened. 141 | # 142 | def self.audio_driver 143 | open_audio 144 | return SDL.AudioDriverName 145 | end 146 | 147 | end 148 | -------------------------------------------------------------------------------- /lib/rubygame/color.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | require "rubygame/color/models/base" 22 | require "rubygame/color/models/rgb" 23 | require "rubygame/color/models/rgb255" 24 | require "rubygame/color/models/hsv" 25 | require "rubygame/color/models/hsl" 26 | 27 | require "rubygame/color/palettes/palette" 28 | require "rubygame/color/palettes/x11" 29 | require "rubygame/color/palettes/css" 30 | 31 | 32 | module Rubygame 33 | 34 | # The Color module contains classes related to colors. 35 | # 36 | # Available color representations: 37 | # 38 | # ColorRGB:: color class with red, green, and blue components. 39 | # ColorHSV:: color class with hue, saturation, and value components. 40 | # ColorHSL:: color class with hue, saturation, and luminosity components. 41 | # 42 | # The Palette class allows you to conveniently store and access a 43 | # collection of many different colors, with inheritance from 44 | # included Palettes. 45 | # 46 | # The available predefined palettes are: 47 | # 48 | # X11:: palette with the default X11 colors 49 | # CSS:: palette used with HTML and CSS, very similar to X11 50 | # GLOBAL:: special palette used for automatic lookup (see below) 51 | # 52 | # The GLOBAL palette is special; it is used for automatic color lookup 53 | # in functions like Surface#draw_circle and TTF#render.It includes the 54 | # CSS palette by default; you can include other palettes or define new 55 | # custom colors in GLOBAL to make them available for automatic lookup. 56 | # 57 | # For convenience, you can access the GLOBAL palette through the 58 | # #[] and #[]= methods: 59 | # 60 | # include Rubygame 61 | # player_color = Color[:red] 62 | # Color[:favorite] = Color[:azure] 63 | # 64 | module Color 65 | 66 | (GLOBAL = Palette.new()).include(CSS) # :nodoc: 67 | 68 | # Retrieve a color from the GLOBAL palette. 69 | # See Palette#[] 70 | def self.[]( name ) 71 | GLOBAL[name] 72 | end 73 | 74 | # Store a color in the GLOBAL palette. 75 | # See Palette#[]= 76 | def self.[]=( name, color ) 77 | GLOBAL[name] = color 78 | end 79 | 80 | 81 | # Same as Rubygame::Color::ColorRGB.new. 82 | def self.rgb( color ) 83 | Rubygame::Color::ColorRGB.new( color ) 84 | end 85 | 86 | # Same as Rubygame::Color::ColorRGB255.new. 87 | def self.rgb255( color ) 88 | Rubygame::Color::ColorRGB255.new( color ) 89 | end 90 | 91 | # Same as Rubygame::Color::ColorRGB255.hex. 92 | def self.hex( string ) 93 | Rubygame::Color::ColorRGB255.hex( string ) 94 | end 95 | 96 | # Same as Rubygame::Color::ColorHSV.new. 97 | def self.hsv( color ) 98 | Rubygame::Color::ColorHSV.new( color ) 99 | end 100 | 101 | # Same as Rubygame::Color::ColorHSL.new. 102 | def self.hsl( color ) 103 | Rubygame::Color::ColorHSL.new( color ) 104 | end 105 | 106 | 107 | # For use by Rubygame methods only: 108 | 109 | # Convert a color name (string or symbol), hex color string, Color 110 | # instance, or Array to a color array. Color names take precedence 111 | # over hex color strings. 112 | def self.convert_color( color ) # :nodoc: 113 | color = 114 | if color.kind_of?(String) 115 | begin 116 | Rubygame::Color[color].to_sdl_rgba_ary 117 | rescue IndexError => e 118 | # Match 3, 4, 6, or 8-digit hex color. "#" is required. 119 | if /\#([0-9a-f]{3}|[0-9a-f]{4}| 120 | [0-9a-f]{6}|[0-9a-f]{8})/ix =~ color 121 | Rubygame::Color::ColorRGB255.hex(color).to_sdl_rgba_ary 122 | else 123 | raise e 124 | end 125 | end 126 | elsif color.kind_of?(Symbol) 127 | Rubygame::Color[color].to_sdl_rgba_ary 128 | elsif color.respond_to? :to_sdl_rgba_ary 129 | color.to_sdl_rgba_ary 130 | elsif color.respond_to? :to_ary 131 | color.to_ary 132 | else 133 | raise TypeError, "unsupported type #{color.class} for color" 134 | end 135 | 136 | unless color.size.between?(3,4) and color.all?{|n| n.kind_of? Numeric} 137 | raise TypeError, "invalid color: #{color.inspect}" 138 | end 139 | 140 | return color 141 | end 142 | 143 | def self.make_sdl_rgba( color ) # :nodoc: 144 | @rgba_cache ||= {} 145 | @rgba_cache[color] ||= 146 | begin 147 | r,g,b,a = convert_color(color).collect!{ |c| c.to_i }[0,4] 148 | a ||= 255 149 | [r,g,b,a].freeze 150 | end 151 | end 152 | 153 | 154 | def self.remove_from_cache( color_name ) 155 | @rgba_cache ||= {} 156 | @rgba_cache.delete( color_name ) 157 | end 158 | 159 | end 160 | end 161 | 162 | -------------------------------------------------------------------------------- /lib/rubygame/color/models/base.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | module Rubygame 21 | module Color 22 | 23 | # A mix-in module defining methods shared by all Color classes. 24 | module ColorBase 25 | 26 | # Class methods shared by all Color classes. 27 | module ClassMethods 28 | 29 | # Returns the class basename. E.g. the basename of 30 | # Rubygame::Color::ColorRGB is "ColorRGB". 31 | def basename 32 | n = name 33 | i = n.rindex(':') 34 | i ? n[i+1, n.length] : n 35 | end 36 | 37 | # Creates a new instance from an RGBA array of integers 38 | # ranging from 0 to 255. 39 | def new_from_sdl_rgba( rgba ) 40 | new_from_rgba( rgba.collect { |i| i / 255.0 } ) 41 | end 42 | 43 | end 44 | 45 | def self.included( other ) 46 | class << other; include ClassMethods; end 47 | end 48 | 49 | 50 | # Override this in the class 51 | def to_ary # :nodoc: 52 | raise "Cannot convert #{self.class.basename} to array" 53 | end 54 | def to_a # :nodoc: 55 | to_ary 56 | end 57 | 58 | 59 | # Override this in the class 60 | def to_rgba_ary # :nodoc: 61 | raise "Cannot convert #{self.class.basename} to RGBA" 62 | end 63 | 64 | # Converts the color to an RGBA array of integers 65 | # ranging from 0 to 255, like SDL uses. 66 | def to_sdl_rgba_ary 67 | self.to_rgba_ary.collect { |i| clamp(i*255, 0, 255).round } 68 | end 69 | 70 | 71 | # Returns a new Rubygame::Color::ColorRGB matching this color. 72 | def to_rgb 73 | Rubygame::Color::ColorRGB.new(self) 74 | end 75 | 76 | # Returns a new Rubygame::Color::ColorRGB255 matching this color. 77 | # (Note: ColorRGB255 stores each component as an integer ranging 78 | # from 0 to 255, which may cause precision loss.) 79 | # 80 | def to_rgb255 81 | Rubygame::Color::ColorRGB255.new(self) 82 | end 83 | 84 | # Returns a new Rubygame::Color::ColorHSL matching this color. 85 | def to_hsl 86 | Rubygame::Color::ColorHSL.new(self) 87 | end 88 | 89 | # Returns a new Rubygame::Color::ColorHSV matching this color. 90 | def to_hsv 91 | Rubygame::Color::ColorHSV.new(self) 92 | end 93 | 94 | 95 | def to_s # :nodoc: 96 | "#<%s %s>"%[self.class.basename, to_ary.inspect] 97 | end 98 | 99 | def inspect # :nodoc: 100 | "#<%s:%#.x %s>"%[self.class.basename, object_id, to_ary.inspect] 101 | end 102 | 103 | 104 | def hash # :nodoc: 105 | ary = to_ary 106 | @hash ||= ((ary.at(0).hash << 4) + 107 | (ary.at(1).hash << 3) + 108 | (ary.at(2).hash << 2) + 109 | (ary.at(3).hash << 1) + 110 | self.class.hash) 111 | end 112 | 113 | 114 | # True if other has the same class and components 115 | def eql?( other ) 116 | self.hash == other.hash 117 | end 118 | 119 | def ==( other ) 120 | return true if eql?(other) 121 | c2 = begin 122 | self.class.new(other).to_rgba_ary 123 | rescue ArgumentError 124 | return false 125 | end 126 | c1 = to_rgba_ary 127 | return c1.enum_for(:each_index).all? { |i| 128 | (c1.at(i) - c2.at(i)).abs < Float::EPSILON*2 129 | } 130 | end 131 | 132 | 133 | # Perform color addition with another color of any type. 134 | # The alpha of the new color will be equal to the alpha 135 | # of the receiver. 136 | def +(other) 137 | wrap( simple_op(other) { |a,b| a + b } ) 138 | end 139 | 140 | # Perform color subtraction with another color of any type. 141 | # The alpha of the new color will be equal to the alpha 142 | # of the receiver. 143 | def -(other) 144 | wrap( simple_op(other) { |a,b| a - b } ) 145 | end 146 | 147 | # Perform color multiplication with another color of any type. 148 | # The alpha of the new color will be equal to the alpha 149 | # of the receiver. 150 | def *(other) 151 | wrap( simple_op(other) { |a,b| a * b } ) 152 | end 153 | 154 | # Perform color division with another color of any type. 155 | # The alpha of the new color will be equal to the alpha 156 | # of the receiver. 157 | def /(other) 158 | wrap( simple_op(other) { |a,b| (a == 0) ? 0.0 : (a / b.to_f) } ) 159 | end 160 | 161 | # Layer this color over another color. 162 | def over(other) 163 | c1, c2 = self.to_rgba_ary, other.to_rgba_ary 164 | a1, a2 = c1[3], c2[3] 165 | 166 | rgba = [0,1,2].collect do |i| 167 | clamp( a1*c1.at(i) + a2*c2.at(i)*(1-a1) ) 168 | end 169 | 170 | rgba << ( a1 + a2*(1-a1) ) 171 | 172 | wrap( rgba ) 173 | end 174 | 175 | # Average this color with another color. (Linear weighted average) 176 | # 177 | # A weight of 0.0 means 0% of this color, 100% of the other. 178 | # A weight of 1.0 means 100% of this color, 0% of the other. 179 | # A weight of 0.5 means 50% of each color. 180 | # 181 | def average(other, weight=0.5) 182 | c1, c2 = self.to_rgba_ary, other.to_rgba_ary 183 | 184 | rgba = [0,1,2,3].collect do |i| 185 | clamp( c1.at(i)*weight + c2.at(i)*(1-weight) ) 186 | end 187 | 188 | wrap( rgba ) 189 | end 190 | 191 | 192 | private 193 | 194 | def wrap( rgba ) 195 | self.class.new_from_rgba( rgba ) 196 | end 197 | 198 | def simple_op(other, &block) 199 | c1, c2 = self.to_rgba_ary, other.to_rgba_ary 200 | a1, a2 = c1[3], c2[3] 201 | 202 | rgba = [0,1,2].collect do |i| 203 | clamp( block.call( a1*c1.at(i), a2*c2.at(i) ) ) 204 | end 205 | 206 | rgba << a1 207 | 208 | return rgba 209 | end 210 | 211 | def clamp(v, min=0.0, max=1.0) 212 | v = min if v < min 213 | v = max if v > max 214 | return v 215 | end 216 | end 217 | 218 | end 219 | end 220 | -------------------------------------------------------------------------------- /lib/rubygame/color/models/hsl.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | module Rubygame 23 | module Color 24 | 25 | # Represents color in the HSL (Hue, Saturation, Luminosity) color space. 26 | class ColorHSL 27 | include ColorBase 28 | 29 | attr_reader :h, :s, :l, :a 30 | 31 | # call-seq: 32 | # new( [h,s,l,a] ) -> ColorHSL 33 | # new( [h,s,l] ) -> ColorHSL 34 | # new( color ) -> ColorHSL 35 | # 36 | # Create a new instance from an Array or an existing color 37 | # (of any type). If the alpha (opacity) component is omitted 38 | # from the array, full opacity will be used. 39 | # 40 | # All color components range from 0.0 to 1.0. 41 | # 42 | def initialize( color ) 43 | if color.kind_of?(Array) and color.size >= 3 44 | @h, @s, @l, @a = color.collect { |i| i.to_f } 45 | @a = 1.0 unless @a 46 | elsif color.instance_of?(self.class) 47 | @h, @s, @l, @a = color.to_ary 48 | elsif color.respond_to?(:to_rgba_ary) 49 | @h, @s, @l, @a = self.class.rgba_to_hsla( *color.to_rgba_ary ) 50 | else 51 | raise ArgumentError, "Invalid color: #{color.inspect}" 52 | end 53 | 54 | hash 55 | end 56 | 57 | # Returns the color as an RGBA array of floats ranging from 0.0 to 1.0. 58 | def to_rgba_ary 59 | return self.class.hsla_to_rgba( @h, @s, @l, @a ) 60 | end 61 | 62 | # Returns the color as an HSLA array of floats ranging from 0.0 63 | # to 1.0. 64 | def to_ary 65 | [@h, @s, @l, @a] 66 | end 67 | 68 | 69 | class << self 70 | 71 | # Creates a new instance from an RGBA array of floats ranging 72 | # from 0.0 to 1.0. 73 | def new_from_rgba( rgba ) 74 | new( rgba_to_hsla(*rgba) ) 75 | end 76 | 77 | # Convert the red, green, blue, and alpha to the 78 | # equivalent hue, saturation, luminosity, and alpha. 79 | def rgba_to_hsla( r, g, b, a ) # :nodoc: 80 | rgb_arr = [r, g, b] 81 | max = rgb_arr.max 82 | min = rgb_arr.min 83 | 84 | # Calculate lightness. 85 | l = (max + min) / 2.0 86 | 87 | # Calculate saturation. 88 | if l == 0.0 or max == min 89 | s = 0 90 | elsif 0 < l and l <= 0.5 91 | s = (max - min) / (max + min) 92 | else # l > 0.5 93 | s = (max - min) / (2 - (max + min)) 94 | end 95 | 96 | # Calculate hue. 97 | if min == max 98 | h = 0 99 | # Undefined in this case, but set it to zero 100 | elsif max == r and g >= b 101 | h = (1.quo(6) * (g - b) / (max - min)) + 0 102 | elsif max == r and g < b 103 | h = (1.quo(6) * (g - b) / (max - min)) + 1.0 104 | elsif max == g 105 | h = (1.quo(6) * (b - r) / (max - min)) + 1.quo(3) 106 | elsif max == b 107 | h = (1.quo(6) * (r - g) / (max - min)) + 2.quo(3) 108 | else 109 | raise "Should never happen" 110 | end 111 | 112 | return [h,s,l,a] 113 | end 114 | 115 | # Convert the hue, saturation, luminosity, and alpha 116 | # to the equivalent red, green, blue, and alpha. 117 | def hsla_to_rgba( h, s, l, a ) # :nodoc: 118 | # If the color is achromatic, return already with the 119 | # lightness value for all components 120 | if s == 0.0 121 | return [l, l, l, a] 122 | end 123 | 124 | # Otherwise, we have to do the long, hard calculation 125 | 126 | # q helper value 127 | q = (l < 0.5) ? (l * (1.0 + s)) : (l + s - l * s) 128 | 129 | # p helper value 130 | p = (2.0 * l) - q 131 | 132 | r = calculate_component( p, q, h + 1.quo(3) ) 133 | g = calculate_component( p, q, h ) 134 | b = calculate_component( p, q, h - 1.quo(3) ) 135 | 136 | return [r,g,b,a] 137 | end 138 | 139 | private 140 | 141 | # Perform some arcane math to calculate a color component. 142 | def calculate_component(p, q, tc) # :nodoc: 143 | tc %= 1.0 144 | if tc < 1.quo(6) 145 | p + (q - p) * tc * 6.0 146 | elsif tc < 0.5 147 | q 148 | elsif tc < 2.quo(3) 149 | p + (q - p) * (2.quo(3) - tc) * 6.0 150 | else 151 | p 152 | end 153 | end 154 | 155 | end 156 | 157 | end 158 | end 159 | end 160 | -------------------------------------------------------------------------------- /lib/rubygame/color/models/hsv.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | module Rubygame 23 | module Color 24 | 25 | # Represents color in the HSV (Hue, Saturation, Value) color space. 26 | class ColorHSV 27 | include ColorBase 28 | 29 | attr_reader :h, :s, :v, :a 30 | 31 | # call-seq: 32 | # new( [h,s,v,a] ) -> ColorHSV 33 | # new( [h,s,v] ) -> ColorHSV 34 | # new( color ) -> ColorHSV 35 | # 36 | # Create a new instance from an Array or an existing color 37 | # (of any type). If the alpha (opacity) component is omitted 38 | # from the array, full opacity will be used. 39 | # 40 | # All color components range from 0.0 to 1.0. 41 | # 42 | def initialize( color ) 43 | if color.kind_of?(Array) and color.size >= 3 44 | @h, @s, @v, @a = color.collect { |i| i.to_f } 45 | @a = 1.0 unless @a 46 | elsif color.instance_of?(self.class) 47 | @h, @s, @v, @a = color.to_ary 48 | elsif color.respond_to?(:to_rgba_ary) 49 | @h, @s, @v, @a = self.class.rgba_to_hsva( *color.to_rgba_ary ) 50 | else 51 | raise ArgumentError, "Invalid color: #{color.inspect}" 52 | end 53 | 54 | hash 55 | end 56 | 57 | # Returns the color as an RGBA array of floats ranging from 0.0 58 | # to 1.0. 59 | def to_rgba_ary 60 | return self.class.hsva_to_rgba( @h, @s, @v, @a ) 61 | end 62 | 63 | # Returns the color as an HSVA array of floats ranging from 0.0 64 | # to 1.0. 65 | def to_ary 66 | [@h, @s, @v, @a] 67 | end 68 | 69 | 70 | class << self 71 | 72 | # Creates a new instance from an RGBA array of floats ranging 73 | # from 0.0 to 1.0. 74 | def new_from_rgba( rgba ) 75 | new( rgba_to_hsva(*rgba) ) 76 | end 77 | 78 | # Convert the red, green, blue, and alpha to the 79 | # equivalent hue, saturation, value, and alpha. 80 | def rgba_to_hsva( r, g, b, a ) # :nodoc: 81 | rgb_arr = [r, g, b] 82 | max = rgb_arr.max 83 | min = rgb_arr.min 84 | 85 | # Calculate hue. 86 | if min == max 87 | h = 0 88 | # Undefined in this case, but set it to zero 89 | elsif max == r and g >= b 90 | h = (1.quo(6) * (g - b) / (max - min)) + 0 91 | elsif max == r and g < b 92 | h = (1.quo(6) * (g - b) / (max - min)) + 1.0 93 | elsif max == g 94 | h = (1.quo(6) * (b - r) / (max - min)) + 1.quo(3) 95 | elsif max == b 96 | h = (1.quo(6) * (r - g) / (max - min)) + 2.quo(3) 97 | else 98 | raise "Should never happen" 99 | end 100 | 101 | # Calulate value. 102 | v = max 103 | 104 | # Calculate saturation. 105 | if max == 0.0 106 | s = 0.0 107 | else 108 | s = 1.0 - (min / max) 109 | end 110 | 111 | return [h,s,v,a] 112 | end 113 | 114 | # Convert the hue, saturation, value, and alpha 115 | # to the equivalent red, green, blue, and alpha. 116 | def hsva_to_rgba( h, s, v, a ) # :nodoc: 117 | # Determine what part of the "color hexagon" the hue is in. 118 | hi = (h * 6).floor % 6 119 | 120 | # Fractional part 121 | f = (h * 6) - hi 122 | 123 | # Helper values 124 | p = v * (1.0 - s) 125 | q = v * (1.0 - (f * s)) 126 | t = v * (1.0 - ((1.0 - f) * s)) 127 | 128 | # Finally calculate the rgb values 129 | r, g, b = calculate_rgb(hi, v, p, t, q) 130 | 131 | return [r, g, b, a] 132 | end 133 | 134 | private 135 | 136 | def calculate_rgb(hi, v, p, t, q) # :nodoc: 137 | case hi 138 | when 0 139 | return v, t, p 140 | when 1 141 | return q, v, p 142 | when 2 143 | return p, v, t 144 | when 3 145 | return p, q, v 146 | when 4 147 | return t, p, v 148 | when 5 149 | return v, p, q 150 | end 151 | end 152 | end 153 | end 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /lib/rubygame/color/models/rgb.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | module Rubygame 23 | module Color 24 | 25 | # Represents color in the RGB (Red, Green, Blue) color space, 26 | # with each color component as a float from 0.0 to 1.0. 27 | # 28 | # See also ColorRGB255, where components are integers from 0 to 255. 29 | # 30 | class ColorRGB 31 | include ColorBase 32 | 33 | attr_reader :r, :g, :b, :a 34 | 35 | # call-seq: 36 | # new( [r,g,b,a] ) -> ColorRGB 37 | # new( [r,g,b] ) -> ColorRGB 38 | # new( color ) -> ColorRGB 39 | # 40 | # Create a new instance from an Array or an existing color 41 | # (of any type). If the alpha (opacity) component is omitted 42 | # from the array, full opacity will be used. 43 | # 44 | # All color components range from 0.0 to 1.0. 45 | # 46 | def initialize( color ) 47 | if color.kind_of?(Array) and color.size >= 3 48 | @r, @g, @b, @a = color.collect { |i| i.to_f } 49 | @a = 1.0 unless @a 50 | elsif color.instance_of?(self.class) 51 | @r, @g, @b, @a = color.to_ary 52 | elsif color.respond_to?(:to_rgba_ary) 53 | @r, @g, @b, @a = color.to_rgba_ary 54 | else 55 | raise ArgumentError, "Invalid color: #{color.inspect}" 56 | end 57 | 58 | hash 59 | end 60 | 61 | # Creates a new instance from an RGBA array of floats ranging 62 | # from 0.0 to 1.0. 63 | def self.new_from_rgba( rgba ) 64 | new( rgba ) 65 | end 66 | 67 | # Returns the color as an RGBA array of floats ranging from 0.0 68 | # to 1.0. 69 | def to_rgba_ary 70 | return [@r, @g, @b, @a] 71 | end 72 | 73 | # Same as #to_rgba_ary 74 | def to_ary 75 | return [@r, @g, @b, @a] 76 | end 77 | 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/rubygame/color/models/rgb255.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2010 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | module Rubygame 23 | module Color 24 | 25 | # Represents color in the RGB (Red, Green, Blue) color space, 26 | # with each color component as an integer from 0 to 255. 27 | # 28 | # See also ColorRGB, where components are floats from 0.0 to 1.0. 29 | # 30 | class ColorRGB255 31 | include ColorBase 32 | 33 | attr_reader :r, :g, :b, :a 34 | 35 | # call-seq: 36 | # new( [r,g,b,a] ) -> ColorRGB 37 | # new( [r,g,b] ) -> ColorRGB 38 | # new( color ) -> ColorRGB 39 | # 40 | # Create a new instance from an Array or an existing color 41 | # (of any type). If the alpha (opacity) component is omitted 42 | # from the array, full opacity will be used. 43 | # 44 | # All color components range from 0 to 255. 45 | # 46 | def initialize( color ) 47 | if color.kind_of?(Array) and color.size >= 3 48 | @r, @g, @b, @a = color.collect { |i| i.round } 49 | @a = 255 unless @a 50 | elsif color.instance_of?(self.class) 51 | @r, @g, @b, @a = color.to_ary 52 | elsif color.respond_to?(:to_rgba_ary) 53 | @r, @g, @b, @a = color.to_sdl_rgba_ary 54 | else 55 | raise ArgumentError, "Invalid color: #{color.inspect}" 56 | end 57 | 58 | hash 59 | end 60 | 61 | # Creates a new instance from an RGBA array of floats ranging 62 | # from 0.0 to 1.0. 63 | def self.new_from_rgba( rgba ) 64 | new( rgba.collect { |i| (i * 255).round } ) 65 | end 66 | 67 | # Creates a new instance from an RGBA array of integers 68 | # ranging from 0 to 255. 69 | def self.new_from_sdl_rgba( rgba ) 70 | new( rgba ) 71 | end 72 | 73 | # Creates a new instance from a string containing an HTML/CSS 74 | # color string, i.e. "#RGB", "#RGBA", "#RRGGBB", or "#RRGGBBAA". 75 | # The leading "#" is optional. 76 | # 77 | # Example: 78 | # 79 | # include Rubygame::Color 80 | # 81 | # # 4 ways of specifying the same color: 82 | # 83 | # # "#RGB" 84 | # ColorRGB255.hex("#248") 85 | # # => # 86 | # 87 | # # "#RGBA" 88 | # ColorRGB255.hex("#248f") 89 | # # => # 90 | # 91 | # # "#RRGGBB" 92 | # ColorRGB255.hex("#224488") 93 | # # => # 94 | # 95 | # # "#RRGGBBAA" 96 | # ColorRGB255.hex("#224488ff") 97 | # # => # 98 | # 99 | def self.hex( color_str ) 100 | case color_str 101 | when /^#?([0-9a-f]{8}$)/i 102 | r = $1[0,2].hex 103 | g = $1[2,2].hex 104 | b = $1[4,2].hex 105 | a = $1[6,2].hex 106 | new( [r, g, b, a] ) 107 | when /^#?([0-9a-f]{6}$)/i 108 | r = $1[0,2].hex 109 | g = $1[2,2].hex 110 | b = $1[4,2].hex 111 | a = 255 112 | new( [r, g, b, a] ) 113 | when /^#?([0-9a-f]{4})$/i 114 | # As with HTML/CSS, each hexdigit is repeated. 115 | # So, "#1234" means "#11223344" (i.e. [17, 34, 51, 68]). 116 | r = ($1[0,1]*2).hex 117 | g = ($1[1,1]*2).hex 118 | b = ($1[2,1]*2).hex 119 | a = ($1[3,1]*2).hex 120 | new( [r, g, b, a] ) 121 | when /^#?([0-9a-f]{3})$/i 122 | # As with HTML/CSS, each hexdigit is repeated. 123 | # So, "#123" means "#112233" (i.e. [17, 34, 51]). 124 | r = ($1[0,1]*2).hex 125 | g = ($1[1,1]*2).hex 126 | b = ($1[2,1]*2).hex 127 | a = 255 128 | new( [r, g, b, a] ) 129 | else 130 | raise "Invalid hex color string #{color_str.inspect}." 131 | end 132 | end 133 | 134 | # Returns the color as an RGBA array of integers ranging from 0 135 | # to 255, as SDL wants. 136 | def to_sdl_rgba_ary 137 | [@r, @g, @b, @a] 138 | end 139 | 140 | # Returns the color as an RGBA array of floats ranging from 0.0 141 | # to 1.0. 142 | def to_rgba_ary 143 | [@r/255.0, @g/255.0, @b/255.0, @a/255.0] 144 | end 145 | 146 | # Same as #to_sdl_rgba_ary 147 | def to_ary 148 | [@r, @g, @b, @a] 149 | end 150 | 151 | # Returns the color as an 8-digit hex color string, in the form 152 | # "#RRGGBBAA". See also ColorRGB255.hex. 153 | # 154 | # Example: 155 | # 156 | # include Rubygame::Color 157 | # 158 | # color = ColorRGB255.hex("#124589ff") 159 | # # => # 160 | # color.to_hex 161 | # # => "#124589ff" 162 | # 163 | # ColorRGB255.hex("1234").to_hex 164 | # # => "#11223344" 165 | # 166 | def to_hex 167 | "#" + "%0.2x"%@r + "%0.2x"%@g + "%0.2x"%@b + "%0.2x"%@a 168 | end 169 | 170 | end 171 | end 172 | end 173 | -------------------------------------------------------------------------------- /lib/rubygame/color/palettes/css.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | module Rubygame 23 | module Color 24 | 25 | # :enddoc: 26 | 27 | # The CSS module contains all the colors in the CSS/HTML palette 28 | # by symbol name, e.g. :alice_blue, :dark_olive_green, etc. 29 | # 30 | # NOTE: The CSS palette is identical to the X11 palette except for 31 | # four colors: gray, green, maroon, and purple. 32 | # 33 | # Differences between CSS and X11 derived from 34 | # http://en.wikipedia.org/wiki/X11_color_names 35 | # as accessed on 2007-12-17 36 | # 37 | CSS = Palette.new({ 38 | :gray => ColorRGB.new( [0.50196, 0.50196, 0.50196] ), 39 | :green => ColorRGB.new( [0.00000, 0.50196, 0.00000] ), 40 | :maroon => ColorRGB.new( [0.50196, 0.00000, 0.00000] ), 41 | :purple => ColorRGB.new( [0.50196, 0.00000, 0.50196] ) 42 | }) 43 | 44 | CSS.include X11 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/rubygame/color/palettes/palette.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | class Rubygame::Color::Palette 21 | 22 | # Create a new Palette with the given name => color pairs. 23 | def initialize( colors = {} ) 24 | @includes = [] 25 | 26 | @colors = {} 27 | colors.each_pair do |name, color| 28 | @colors[sanitize_name(name)] = color 29 | end 30 | end 31 | 32 | # Retrieve a color by name from this palette. 33 | # 34 | # The name can be a Symbol or String. See #sanitize_name. 35 | # 36 | # If the color cannot be found in this palette, search 37 | # each of the #included palettes (recursively, depth-first, 38 | # to a maximum depth of 5 levels). 39 | # 40 | # If the color is not found in this palette or any included 41 | # palettes, raise IndexError. 42 | # 43 | def []( name ) 44 | c = lookup( sanitize_name( name ) ) 45 | raise IndexError, "unknown color #{name}" unless c 46 | return c 47 | end 48 | 49 | # Store a color by name in this palette. See #sanitize_name 50 | def []=( name, color ) 51 | # Uncache colors with this name, to avoid using obsolete value. 52 | Rubygame::Color.remove_from_cache( name ) 53 | 54 | name = sanitize_name( name ) 55 | @colors[name] = color 56 | end 57 | 58 | # Include another palette in this one. If a color cannot be 59 | # found in this palette, the included palette(s) will be searched. 60 | # See also #uninclude. 61 | # 62 | # Has no effect if the palette is already included. 63 | def include( palette ) 64 | @includes += [palette] unless @includes.include? palette 65 | end 66 | 67 | # Remove the other palette from this one, so that it won't be 68 | # searched for missing colors anymore. Has no effect if the 69 | # other palette hasn't been #included. 70 | def uninclude( palette ) 71 | @includes -= [palette] 72 | end 73 | 74 | protected 75 | 76 | # Recursive color lookup 77 | def lookup( name, max_depth=5 ) # :nodoc: 78 | return nil if max_depth < 0 79 | 80 | color = @colors[name] 81 | 82 | unless color 83 | @includes.each { |palette| 84 | c = palette.lookup(name, max_depth-1) 85 | color = c if c 86 | } 87 | end 88 | 89 | return color 90 | end 91 | 92 | private 93 | 94 | # Takes either a Symbol or a String, and converts it to a 95 | # lowercase Symbol with spaces converted to underscores. 96 | # 97 | # E.g. "Alice Blue" and :ALICE_BLUE both become :alice_blue. 98 | # 99 | def sanitize_name( name ) 100 | name.to_s.gsub(' ','_').downcase.intern 101 | end 102 | 103 | end 104 | -------------------------------------------------------------------------------- /lib/rubygame/event_actions.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2008 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | # This module contains all the event action classes that 22 | # come with Rubygame. 23 | # 24 | # An event action class is simply a class which can be 25 | # used as an action an EventHook. The action is used to 26 | # cause some effect when the EventHook matches an event. 27 | # 28 | # The only requirement for an event action is this: 29 | # 30 | # * It must have a #perform method which takes exactly two 31 | # arguments (the hook owner and an event). Return values 32 | # are ignored. 33 | # 34 | # You can make your own custom event action classes and 35 | # use them in an EventHook if they meet that requirement. 36 | # 37 | # Here is an overview of the event action classes that 38 | # come with Rubygame as of version 2.4: 39 | # 40 | # BlockAction:: Calls a custom code block, passing it the 41 | # hook owner and the event. 42 | # 43 | # MethodAction:: Calls one of the owner's methods, passing 44 | # it the event. 45 | # 46 | # MultiAction:: Holds multiple other actions and performs 47 | # each of them, in order. 48 | # 49 | module Rubygame::EventActions 50 | 51 | 52 | # BlockAction is an event action used with EventHook. BlockAction 53 | # takes a code block at initialization. When the action is performed, 54 | # it executes the block, passing in the EventHook#owner and the event 55 | # that is being handled as the two parameters to the block. 56 | # 57 | # Example: 58 | # 59 | # hit_by_missile = KindOfTrigger.new( MissileCollisionEvent ) 60 | # 61 | # take_damage = BlockAction.new { |owner, event| 62 | # owner.health -= event.damage_amount 63 | # } 64 | # 65 | # hook = EventHook.new( :owner => player_ship, 66 | # :trigger => hit_by_missile, 67 | # :action => take_damage ) 68 | # 69 | # 70 | # NOTE: It is also possible to pass a Proc or detached Method 71 | # as the block, using the standard Ruby syntax for that: 72 | # 73 | # # Using a pre-built Proc. 74 | # 75 | # my_proc = Proc.new { |owner, event| do_something() } 76 | # 77 | # BlockAction.new( &my_proc ) 78 | # 79 | # 80 | # # Using a detached method. 81 | # 82 | # def a_method( owner, event ) 83 | # do_something 84 | # end 85 | # 86 | # detached_method = method(:a_method) 87 | # 88 | # BlockAction.new( &detached_method ) 89 | # 90 | # 91 | class BlockAction 92 | 93 | # Create a new BlockAction using the given code block. 94 | # 95 | # &block:: the code block to execute. Should take two parameters, 96 | # owner and event. (Proc, required) 97 | # 98 | # May raise:: ArgumentError, if no block is provided. 99 | # 100 | def initialize( &block ) 101 | raise ArgumentError, "BlockAction needs a block" unless block_given? 102 | @block = block 103 | end 104 | 105 | # Execute the code block, passing in owner and event as the two 106 | # parameters to the block. This is automatically called by EventHook 107 | # when an event matches the trigger. You should usually not call it 108 | # in your own code. 109 | # 110 | # owner:: the owner of the EventHook, or nil if there is none. 111 | # (Object, required) 112 | # event:: the event that matched the trigger. (Object, required) 113 | # 114 | def perform( owner, event ) 115 | @block.call( owner, event ) 116 | end 117 | end 118 | 119 | 120 | # MethodAction is an event action used with EventHook. 121 | # MethodAction takes a symbol giving the name of a method. When 122 | # it is performed, it calls that method on the owner, passing 123 | # it the event that triggered the hook. 124 | # 125 | # Example: 126 | # 127 | # class Player 128 | # def aim_at( event ) 129 | # self.crosshair_pos = event.pos 130 | # end 131 | # end 132 | # 133 | # player1 = Player.new 134 | # 135 | # EventHook.new( :owner => player1, 136 | # :trigger => MouseMoveTrigger.new(), 137 | # :action => MethodAction.new( :aim_at ) ) 138 | # 139 | class MethodAction 140 | 141 | # Create a new MethodAction using the given method name. 142 | # 143 | # method_name:: the method to call when performing. 144 | # (Symbol, required) 145 | # 146 | def initialize( method_name ) 147 | @method_name = method_name 148 | end 149 | 150 | 151 | # Call the method of the owner represented by @method_name, 152 | # passing in the event as the only argument. 153 | # 154 | # If that causes ArgumentError (e.g. because the method doesn't 155 | # take an argument), calls again without any arguments. 156 | # 157 | # If that also fails, this method re-raises the original error. 158 | # 159 | def perform( owner, event ) 160 | owner.method(@method_name).call( event ) 161 | rescue ArgumentError => s 162 | begin 163 | # Oops! Try again, without any argument. 164 | owner.method(@method_name).call() 165 | rescue ArgumentError 166 | # That didn't work either. Raise the original error. 167 | raise s 168 | end 169 | end 170 | end 171 | 172 | 173 | 174 | # MultiAction is an event action used with EventHook. 175 | # It takes zero or more actions (e.g. BlockAction or MethodAction 176 | # instances) at initialization. 177 | # 178 | # When MultiAction is performed, it performs all the given 179 | # actions, in the order they were given, passing in the owner 180 | # and event. 181 | # 182 | # As the name suggests, you can use MultiAction to cause 183 | # multiple actions to occur when an EventHook is triggered. 184 | # 185 | class MultiAction 186 | 187 | # Create a new MultiAction instance with the given sub-actions. 188 | # 189 | # *actions:: the actions to perform. (Action instances) 190 | # 191 | def initialize( *actions ) 192 | @actions = actions 193 | end 194 | 195 | # Performs all the sub-actions, in the order they were given, 196 | # passing in the owner and event to each one. 197 | # 198 | def perform( owner, event ) 199 | @actions.each { |action| action.perform( owner, event ) } 200 | end 201 | end 202 | 203 | end 204 | -------------------------------------------------------------------------------- /lib/rubygame/event_hook.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2008 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | require "rubygame/event_triggers" 22 | require "rubygame/event_actions" 23 | 24 | 25 | module Rubygame 26 | 27 | # The EventHook class provides the bare framework for event hooks 28 | # used by EventHandler. Each hook has a trigger, which controls what 29 | # types of events cause the hook to engage, and an action, which 30 | # controls what should happen when the hook engages. 31 | # 32 | # An instance of EventHook has these attributes: 33 | # 34 | # owner:: the object that this hook applies to. This value will 35 | # be provided to the action when the hook engages. 36 | # 37 | # trigger:: an instance of a trigger class, used to test every 38 | # event to check whether the hook should engage. 39 | # A valid trigger must have a #match? method which 40 | # accepts an event and returns either true or false. 41 | # 42 | # action:: an instance of an action class, which is performed 43 | # when the trigger matches an event. A valid action 44 | # must have a #perform method which accepts two values: 45 | # the hook's owner and the matching event. 46 | # 47 | # consumes:: if true, the event hook "eats" every event that it 48 | # matches, so that hooks that come after it will not 49 | # see the event. Has no effect on non-matching events. 50 | # 51 | # active:: if false, the event hook is disabled, and will not 52 | # match any event until it is set to true again. You can 53 | # use this to temporarily disable the hook. 54 | # 55 | class EventHook 56 | attr_accessor :owner, :trigger, :action, :consumes, :active 57 | 58 | # Create a new instance of EventHook. Description is a Hash with 59 | # the following keys. See the class documentation for EventHook for 60 | # more information about what these mean. 61 | # 62 | # :owner :: the hook's owner. (any object, required) 63 | # :trigger :: an event trigger which matches certain events. 64 | # (Object with +#match?(event)+, required) 65 | # :action :: an event action to do when an event matches. 66 | # (Object with +#perform(owner,event)+, required) 67 | # :consumes :: if true, the hook will "eat" matching so 68 | # later hooks won't see them. Default: false. 69 | # (true or false, optional) 70 | # :active :: if false, the hook will ignore all events. 71 | # Default: true. (true or false, optional) 72 | # 73 | # NOTE: None of the attributes are truly required to create a hook. 74 | # But, the hook will do nothing unless both @trigger and @action are 75 | # set. Setting @owner is also highly recommended, because some types 76 | # of actions use the owner, and may raise an error if it is nil. 77 | # 78 | # TIP: It's possible to set these attributes at any time using the 79 | # accessors. For example, You could change keyboard controls on the 80 | # fly, or temporarily deactivate a hook to stop it from engaging. 81 | # 82 | # Example: 83 | # 84 | # # Call player1.jump() when the space bar is pressed. 85 | # EventHook.new( :owner => player1, 86 | # :trigger => KeyPressTrigger.new(:space) 87 | # :action => MethodAction.new(:jump) ) 88 | # 89 | def initialize( description={} ) 90 | @owner = description[:owner] 91 | @trigger = description[:trigger] 92 | @action = description[:action] 93 | @consumes = (description[:consumes] or false) 94 | @active = (description[:active].nil? ? true : description[:active]) 95 | end 96 | 97 | # Passes the event to @trigger's #match? method, and returns the 98 | # result. If there is no @trigger or if @active is false, returns 99 | # nil immediately. 100 | def match?( event ) 101 | if (@trigger and @active) 102 | @trigger.match?( event ) 103 | else 104 | false 105 | end 106 | end 107 | 108 | # Passes @owner and event to the @action's #perform method. 109 | def perform( event ) 110 | @action.perform( owner, event ) if @action 111 | end 112 | end 113 | 114 | end 115 | -------------------------------------------------------------------------------- /lib/rubygame/events/clock_events.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # This file is one part of: 3 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 4 | # 5 | # Copyright (C) 2009 John Croisant 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | # ++ 21 | 22 | 23 | module Rubygame 24 | 25 | module Events 26 | 27 | # ClockTicked is an event returned by Clock#tick, if the Clock 28 | # has been configured with Clock#enable_tick_events. 29 | # 30 | # ClockTicked stores the time that has passed since the previous 31 | # tick. You can access that information with #seconds or 32 | # #milliseconds. This is useful to calculate how far a character 33 | # should move during the current frame, for example. 34 | # 35 | class ClockTicked 36 | 37 | # Create a new ClockTicked event. 38 | # 39 | # milliseconds:: The time since the last tick, 40 | # in milliseconds. (Numeric, required) 41 | # 42 | def initialize( milliseconds ) 43 | @milliseconds = milliseconds 44 | end 45 | 46 | # Return the time since the last tick, in milliseconds. 47 | def milliseconds 48 | @milliseconds 49 | end 50 | 51 | # Return the time since the last tick, in seconds. 52 | def seconds 53 | @seconds or (@seconds = @milliseconds * 0.001) 54 | end 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/rubygame/events/keyboard_events.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # This file is one part of: 3 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 4 | # 5 | # Copyright (C) 2008 John Croisant 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | # ++ 21 | 22 | 23 | module Rubygame 24 | 25 | module Events 26 | 27 | # 28 | # KeyboardEvent is a mixin module included in the KeyPressed 29 | # and KeyReleased classes. It defines the #key and #modifiers 30 | # accessors. 31 | # 32 | module KeyboardEvent 33 | attr_reader :key, :modifiers 34 | 35 | # 36 | # Initialize the KeyboardEvent. 37 | # 38 | # key:: a key symbol for the key that was pressed or 39 | # released. (Symbol, required) 40 | # 41 | # modifiers:: an Array of key symbols for the modifier keys 42 | # that were active when the event occured. 43 | # (Array, optional) 44 | # 45 | def initialize( key, modifiers=[] ) 46 | 47 | unless key.kind_of? Symbol 48 | raise ArgumentError, "key must be a :symbol" 49 | end 50 | 51 | @key = key 52 | 53 | @modifiers = modifiers.to_ary.dup 54 | @modifiers.freeze 55 | 56 | end 57 | end 58 | 59 | 60 | # 61 | # KeyPressed is an event class which occurs when a key 62 | # on the keyboard is pressed down. 63 | # 64 | # This class gains #key and #modifiers readers from 65 | # the KeyboardEvent mixin module. 66 | # 67 | # It also has #string, which is a UTF8 string containing 68 | # the text character that was generated by the keystroke; 69 | # if nothing was generated, #string will be the empty string, 70 | # "". 71 | # 72 | # The #string attribute is useful for for taking text input 73 | # (e.g. in a GUI). It supports UTF8 Unicode characters, and 74 | # works correctly on many different types of keyboards. 75 | # 76 | class KeyPressed 77 | include KeyboardEvent 78 | 79 | attr_reader :string 80 | 81 | # 82 | # Create a new KeyPressed instance. 83 | # 84 | # key:: a key symbol for the key that was pressed or 85 | # released. (Symbol, required) 86 | # 87 | # modifiers:: an Array of key symbols for the modifier keys 88 | # that were active when the event occured. 89 | # (Array, optional) 90 | # 91 | # string:: a String containing the text character that 92 | # was generated by the keystroke, or "" if 93 | # nothing was generated. (String, optional) 94 | # 95 | def initialize( key, modifiers=[], string="" ) 96 | super( key, modifiers ) 97 | 98 | @string = string.to_str.dup 99 | @string.freeze 100 | 101 | end 102 | end 103 | 104 | 105 | # 106 | # KeyReleased is an event class which occurs when a key 107 | # on the keyboard is released (no longer being pressed). 108 | # 109 | # This class gains #key and #modifiers readers from 110 | # the KeyboardEvent mixin module. 111 | # 112 | class KeyReleased 113 | include KeyboardEvent 114 | 115 | # 116 | # Create a new KeyReleased instance. 117 | # 118 | # key:: a key symbol for the key that was pressed or 119 | # released. (Symbol, required) 120 | # 121 | # modifiers:: an Array of key symbols for the modifier keys 122 | # that were active when the event occured. 123 | # (Array, optional) 124 | # 125 | def initialize( key, modifiers=[] ) 126 | super 127 | end 128 | 129 | end 130 | 131 | end 132 | end 133 | -------------------------------------------------------------------------------- /lib/rubygame/events/misc_events.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # This file is one part of: 3 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 4 | # 5 | # Copyright (C) 2008-2010 John Croisant 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | # ++ 21 | 22 | 23 | module Rubygame 24 | 25 | module Events 26 | 27 | # FocusEvent is a mixin module that is included in the following 28 | # event classes: 29 | # 30 | # * InputFocusGained 31 | # * InputFocusLost 32 | # * MouseFocusGained 33 | # * MouseFocusLost 34 | # * WindowMinimized 35 | # * WindowUnminimized 36 | # 37 | # This module provides no functionality. It exists only to make it 38 | # easier to detect the above classes in a case statement, etc. 39 | # 40 | # Examples: 41 | # 42 | # include Rubygame::Events 43 | # 44 | # case event 45 | # when FocusEvent 46 | # # ... 47 | # end 48 | # 49 | # if event.is_a? FocusEvent 50 | # # ... 51 | # end 52 | # 53 | module FocusEvent; end 54 | 55 | 56 | 57 | # InputFocusEvent is a mixin module that is included in the 58 | # following event classes: 59 | # 60 | # * InputFocusGained 61 | # * InputFocusLost 62 | # 63 | # This module provides no functionality. It exists only to make it 64 | # easier to detect the above classes in a case statement, etc. 65 | # 66 | # Examples: 67 | # 68 | # include Rubygame::Events 69 | # 70 | # case event 71 | # when InputFocusEvent 72 | # # ... 73 | # end 74 | # 75 | # if event.is_a? InputFocusEvent 76 | # # ... 77 | # end 78 | # 79 | module InputFocusEvent 80 | include FocusEvent 81 | end 82 | 83 | # InputFocusGained is an event that occurs when 84 | # the Rubygame application gains input focus. 85 | # 86 | # Input focus means that the app will receive events from 87 | # input devices, such as the keyboard and joysticks. 88 | # 89 | # Usually, an application has input focus when it is the "active" 90 | # application (the one the user has clicked on or switched to most 91 | # recently). 92 | # 93 | class InputFocusGained 94 | include InputFocusEvent 95 | end 96 | 97 | # InputFocusLost is an event that occurs when 98 | # the Rubygame application loses input focus. 99 | # 100 | # See InputFocusGained for a description of "input focus". 101 | # 102 | class InputFocusLost 103 | include InputFocusEvent 104 | end 105 | 106 | 107 | 108 | # MouseFocusEvent is a mixin module that is included in the 109 | # following event classes: 110 | # 111 | # * MouseFocusGained 112 | # * MouseFocusLost 113 | # 114 | # This module provides no functionality. It exists only to make it 115 | # easier to detect the above classes in a case statement, etc. 116 | # 117 | # Examples: 118 | # 119 | # include Rubygame::Events 120 | # 121 | # case event 122 | # when MouseFocusEvent 123 | # # ... 124 | # end 125 | # 126 | # if event.is_a? MouseFocusEvent 127 | # # ... 128 | # end 129 | # 130 | module MouseFocusEvent 131 | include FocusEvent 132 | end 133 | 134 | # MouseFocusGained is an event that occurs when 135 | # the Rubygame application gains mouse focus. 136 | # 137 | # Mouse focus means that the mouse cursor is inside the 138 | # app window. When the app has mouse focus, it will receive 139 | # mouse events, particularly MouseMoved. 140 | # 141 | class MouseFocusGained 142 | include MouseFocusEvent 143 | end 144 | 145 | # MouseFocusLost is an event that occurs when 146 | # the Rubygame application loses mouse focus. 147 | # 148 | # See MouseFocusGained for a description of "mouse focus". 149 | # 150 | class MouseFocusLost 151 | include MouseFocusEvent 152 | end 153 | 154 | 155 | 156 | # MinimizeEvent is a mixin module that is included in the 157 | # following event classes: 158 | # 159 | # * WindowMinimized 160 | # * WindowUnminimized 161 | # 162 | # This module provides no functionality. It exists only to make it 163 | # easier to detect the above classes in a case statement, etc. 164 | # 165 | # Examples: 166 | # 167 | # include Rubygame::Events 168 | # 169 | # case event 170 | # when MinimizeEvent 171 | # # ... 172 | # end 173 | # 174 | # if event.is_a? MinimizeEvent 175 | # # ... 176 | # end 177 | # 178 | module MinimizeEvent 179 | include FocusEvent 180 | end 181 | 182 | # WindowMinimized is an event that occurs when 183 | # the Rubygame application window becomes minimized (also 184 | # called 'iconified'). 185 | # 186 | class WindowMinimized 187 | include MinimizeEvent 188 | end 189 | 190 | # WindowUnminimized is an event that occurs when the 191 | # Rubygame application window is restored after it had been 192 | # minimized. 193 | # 194 | class WindowUnminimized 195 | include MinimizeEvent 196 | end 197 | 198 | 199 | 200 | # WindowExposed is an event that occurs in 201 | # certain situations when the Rubygame application 202 | # window is exposed after being covered by another 203 | # application. 204 | # 205 | # This event may not occur on all platforms, but 206 | # when it does occur, your app should refresh the 207 | # entire window via Screen#flip (or 208 | # Rubygame::GL.swap_buffers, if using OpenGL). 209 | # 210 | class WindowExposed; end 211 | 212 | 213 | 214 | # QuitRequested is an event that occurs when the 215 | # application receives a quit request, usually due to the 216 | # user clicking the "Close" button on the app window. 217 | # 218 | # Almost always, your application should respond to this 219 | # event by quitting or by displaying a "Quit/Cancel" 220 | # dialog. If you ignore this event, the user may become 221 | # frustrated that your app won't close properly! 222 | # 223 | class QuitRequested; end 224 | 225 | 226 | 227 | # WindowResized is an event that occurs when the 228 | # Rubygame application window is resized by the user. 229 | # This can only happen if the Screen mode was set with 230 | # the "resizable" flag. 231 | # 232 | # Your application should respond to this event by 233 | # setting the Screen mode again with the new #size and 234 | # redrawing. 235 | # 236 | # If you ignore this event, the "active" area of the 237 | # Screen will stay the same size, and the rest (if the 238 | # window was enlarged) will be black and won't receive 239 | # any changes (blits, drawing, etc.). 240 | # 241 | class WindowResized 242 | attr_reader :size 243 | 244 | def initialize( size ) 245 | 246 | @size = size.to_ary.dup 247 | @size.freeze 248 | 249 | unless @size.length == 2 250 | raise ArgumentError, "size must have exactly 2 parts (got %s)"%@size.length 251 | end 252 | 253 | @size.each do |part| 254 | if part <= 0 255 | raise ArgumentError, "size must be positive (got %s)"%part 256 | end 257 | end 258 | 259 | end 260 | end 261 | 262 | 263 | end 264 | end 265 | -------------------------------------------------------------------------------- /lib/rubygame/events/mouse_events.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # This file is one part of: 3 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 4 | # 5 | # Copyright (C) 2008 John Croisant 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | # ++ 21 | 22 | 23 | module Rubygame 24 | 25 | module Events 26 | 27 | # 28 | # MouseButtonEvent is a mixin module included in the MousePressed 29 | # and MouseReleased classes. It defines the #button and #pos 30 | # attribute readers. 31 | # 32 | module MouseButtonEvent 33 | attr_reader :button, :pos 34 | 35 | # 36 | # Initialize the MouseButtonEvent. 37 | # 38 | # button:: a symbol for the button that was pressed or 39 | # released. (Symbol, required) 40 | # 41 | # pos:: an Array for the position of the mouse cursor 42 | # when the event occured. [0,0] is the top-left 43 | # corner of the window (or the screen if running 44 | # full-screen). (Array, required) 45 | # 46 | def initialize( pos, button ) 47 | 48 | unless button.kind_of? Symbol 49 | raise ArgumentError, "button must be a :symbol" 50 | end 51 | 52 | @button = button 53 | 54 | @pos = pos.to_ary.dup 55 | @pos.freeze 56 | 57 | end 58 | end 59 | 60 | 61 | # 62 | # MousePressed is an event class which occurs when a button 63 | # on the mouse is pressed down. 64 | # 65 | # This class gains #button and #pos attribute readers from 66 | # the MouseButtonEvent mixin module. 67 | # 68 | class MousePressed 69 | include MouseButtonEvent 70 | 71 | # 72 | # Create a new MousePressed instance. 73 | # 74 | # button:: a symbol for the button that was pressed or 75 | # released. (Symbol, required) 76 | # 77 | # pos:: an Array for the position of the mouse cursor 78 | # when the event occured. [0,0] is the top-left 79 | # corner of the window (or the screen if running 80 | # full-screen). (Array, required) 81 | # 82 | def initialize( pos, button ) 83 | super 84 | end 85 | end 86 | 87 | 88 | # 89 | # MouseReleased is an event class which occurs when a button 90 | # on the mouse is released (no longer being pressed). 91 | # 92 | # This class gains #button and #pos attribute readers from 93 | # the MouseButtonEvent mixin module. 94 | # 95 | class MouseReleased 96 | include MouseButtonEvent 97 | 98 | # 99 | # Create a new MouseReleased instance. 100 | # 101 | # button:: a symbol for the button that was pressed or 102 | # released. (Symbol, required) 103 | # 104 | # pos:: an Array for the position of the mouse cursor 105 | # when the event occured. [0,0] is the top-left 106 | # corner of the window (or the screen if running 107 | # full-screen). (Array, required) 108 | # 109 | def initialize( pos, button ) 110 | super 111 | end 112 | end 113 | 114 | 115 | # 116 | # MouseMoved is an event class which occurs when the mouse 117 | # cursor moves. It has attribute readers for #pos (new position), 118 | # #rel (change since the last MouseMoved event), and #buttons 119 | # (an Array of the mouse buttons that were held down while moving). 120 | # 121 | class MouseMoved 122 | 123 | attr_reader :pos, :rel, :buttons 124 | 125 | # 126 | # Create a new MouseReleased instance. 127 | # 128 | # pos:: an Array for the new position of the mouse cursor. 129 | # The point [0,0] is the top-left corner of the window 130 | # (or the screen if running full-screen). 131 | # (Array, required) 132 | # 133 | # rel:: an Array for the position change since the last 134 | # MouseMoved event, in pixels. (Array, required) 135 | # 136 | # buttons:: an Array of symbols for the mouse buttons that were 137 | # being held down while the mouse was moving. [] if 138 | # no buttons were being held. (Array, optional) 139 | # 140 | def initialize( pos, rel, buttons=[] ) 141 | 142 | @pos = pos.to_ary.dup 143 | @pos.freeze 144 | 145 | @rel = rel.to_ary.dup 146 | @rel.freeze 147 | 148 | @buttons = buttons.to_ary.dup 149 | @buttons.freeze 150 | 151 | end 152 | end 153 | 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /lib/rubygame/gl.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2009 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | # The GL module provides an interface to SDL's OpenGL-related functions, 23 | # allowing a Rubygame application to create hardware-accelerated 3D graphics 24 | # with OpenGL. 25 | # 26 | # Please note that Rubygame itself does not provide an interface to OpenGL 27 | # functions -- only functions which allow Rubygame to work together with 28 | # OpenGL. You will need to use another library, for example 29 | # ruby-opengl[http://ruby-opengl.rubyforge.org/], 30 | # to actually create graphics with OpenGL. 31 | # 32 | # Users who wish to use Rubygame Surfaces as textures in OpenGL should 33 | # see also the Surface#pixels method. 34 | # 35 | module Rubygame::GL 36 | 37 | 38 | # Return the value of the the SDL/OpenGL attribute identified by +attrib+, 39 | # which should be one of the constants defined in the Rubygame::GL module. 40 | # See #set_attrib for a list of attribute constants. 41 | # 42 | # This method is useful after using #set_attrib and calling Screen#set_mode, 43 | # to make sure the attribute is the expected value. 44 | # 45 | def self.get_attrib( attrib ) 46 | result = SDL.GL_GetAttribute(attrib) 47 | if( result.nil? ) 48 | raise Rubygame::SDLError, "GL get attribute failed: #{SDL.GetError()}" 49 | end 50 | end 51 | 52 | 53 | 54 | # Set the SDL/OpenGL attribute +attrib+ to +value+. This should be called 55 | # *before* you call Screen#set_mode with the OPENGL flag. You may wish to 56 | # use #get_attrib after calling Screen#set_mode to confirm that the attribute 57 | # is set to the desired value. 58 | # 59 | # The full list of SDL/OpenGL attribute identifier constants (located under 60 | # the Rubygame::GL module) is as follows: 61 | # 62 | # RED_SIZE:: Size of framebuffer red component, in bits. 63 | # GREEN_SIZE:: Size of framebuffer green component, in bits. 64 | # BLUE_SIZE:: Size of framebuffer blue component, in bits. 65 | # ALPHA_SIZE:: Size of framebuffer alpha (opacity) component, in bits. 66 | # BUFFER_SIZE:: Size of framebuffer, in bits. 67 | # DOUBLEBUFFER:: Enable or disable double-buffering. 68 | # DEPTH_SIZE:: Size of depth buffer, in bits. 69 | # STENCIL_SIZE:: Size of stencil buffer, in bits. 70 | # ACCUM_RED_SIZE:: Size of accumulation buffer red component, in bits. 71 | # ACCUM_GREEN_SIZE:: Size of accumulation buffer green component, in bits. 72 | # ACCUM_BLUE_SIZE:: Size of accumulation buffer blue component, in bits. 73 | # ACCUM_ALPHA_SIZE:: Size of accumulation buffer alpha component, in bits. 74 | # 75 | def self.set_attrib( attrib, value ) 76 | result = SDL.GL_SetAttribute( attrib, value ) 77 | if( result == -1 ) 78 | raise Rubygame::SDLError, "GL set attribute failed: #{SDL.GetError()}" 79 | end 80 | end 81 | 82 | 83 | 84 | # Swap the back and front buffers, for double-buffered OpenGL displays. 85 | # Should be safe to use (albeit with no effect) on single-buffered OpenGL 86 | # displays. 87 | # 88 | def self.swap_buffers 89 | SDL.GL_SwapBuffers() 90 | end 91 | 92 | 93 | RED_SIZE = SDL::GL_RED_SIZE 94 | GREEN_SIZE = SDL::GL_GREEN_SIZE 95 | BLUE_SIZE = SDL::GL_BLUE_SIZE 96 | ALPHA_SIZE = SDL::GL_ALPHA_SIZE 97 | BUFFER_SIZE = SDL::GL_BUFFER_SIZE 98 | DOUBLEBUFFER = SDL::GL_DOUBLEBUFFER 99 | DEPTH_SIZE = SDL::GL_DEPTH_SIZE 100 | STENCIL_SIZE = SDL::GL_STENCIL_SIZE 101 | ACCUM_RED_SIZE = SDL::GL_ACCUM_RED_SIZE 102 | ACCUM_GREEN_SIZE = SDL::GL_ACCUM_GREEN_SIZE 103 | ACCUM_BLUE_SIZE = SDL::GL_ACCUM_BLUE_SIZE 104 | ACCUM_ALPHA_SIZE = SDL::GL_ACCUM_ALPHA_SIZE 105 | 106 | 107 | end 108 | -------------------------------------------------------------------------------- /lib/rubygame/image.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2009 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | require "ruby-sdl-ffi/image" 23 | 24 | Rubygame::VERSIONS[:sdl_image] = SDL::Image.Linked_Version().to_ary 25 | 26 | 27 | class Rubygame::Surface 28 | 29 | class << self 30 | 31 | # Searches each directory in Surface.autoload_dirs for a file with 32 | # the given filename. If it finds that file, loads it and returns 33 | # a Surface instance. If it doesn't find the file, returns nil. 34 | # 35 | # See Rubygame::NamedResource for more information about this 36 | # functionality. 37 | # 38 | def autoload( name ) 39 | path = find_file( name ) 40 | 41 | if( path ) 42 | return load( path ) 43 | else 44 | return nil 45 | end 46 | end 47 | 48 | 49 | # *IMPORTANT*: this method only exists if SDL_image is available! 50 | # Your code should check "defined?(Rubygame::Surface.load) != nil" 51 | # to see if you can use this method, or be prepared to rescue from 52 | # NoMethodError. 53 | # 54 | # Load an image file from the disk to a Surface. If the image has an alpha 55 | # channel (e.g. PNG with transparency), the Surface will as well. If the 56 | # image cannot be loaded (for example if the image format is unsupported), 57 | # will raise SDLError. 58 | # 59 | # This method takes this argument: 60 | # filename:: a string containing the relative or absolute path to the 61 | # image file. The file must have the proper file extension, 62 | # as it is used to determine image format. 63 | # 64 | # These formats may be supported, but some may not be available on a 65 | # particular system. 66 | # BMP:: "Windows Bitmap" format. 67 | # GIF:: "Graphics Interchange Format." 68 | # JPG:: "Independent JPEG Group" format. 69 | # LBM:: "Linear Bitmap" format (?) 70 | # PCX:: "PC Paintbrush" format 71 | # PNG:: "Portable Network Graphics" format. 72 | # PNM:: "Portable Any Map" format. (i.e., PPM, PGM, or PBM) 73 | # TGA:: "Truevision TARGA" format. 74 | # TIF:: "Tagged Image File Format" 75 | # XCF:: "eXperimental Computing Facility" (GIMP native format). 76 | # XPM:: "XPixMap" format. 77 | # 78 | def load( filename ) 79 | surf = SDL::Image.Load( filename.to_s ) 80 | 81 | if( surf.pointer.null? ) 82 | raise( Rubygame::SDLError, "Couldn't load image \"%s\": %s"%\ 83 | [filename, SDL.GetError()] ) 84 | end 85 | 86 | return self.new(surf) 87 | end 88 | 89 | 90 | # Deprecated. Use Surface.load instead! 91 | def load_image( filename ) 92 | Rubygame.deprecated( "Rubygame::Surface.load_image", "3.0" ) 93 | load( filename ) 94 | end 95 | 96 | 97 | # *IMPORTANT*: this method only exists if SDL_image is available! 98 | # Your code should check 99 | # "defined?(Rubygame::Surface.load_from_string) != nil" to see if 100 | # you can use this method, or be prepared to rescue from 101 | # NoMethodError. 102 | # 103 | # Load an image file from memory (in the form of the given data) 104 | # to a Surface. If the image has an alpha channel (e.g. PNG with 105 | # transparency), the Surface will as well. If the image cannot be 106 | # loaded (for example if the image format is unsupported), will 107 | # raise SDLError. 108 | # 109 | # This method takes these arguments: 110 | # data:: a string containing the data for the image, such as 111 | # IO::read would return. 112 | # type:: The type of file that the image is (i.e. 'TGA'). Case is 113 | # not important. If absent, the library will try to 114 | # automatically detect the type. 115 | # 116 | # See Surface.load for a list of possible supported file types. 117 | # 118 | def load_from_string( data, type=nil ) 119 | size = data.respond_to?(:bytesize) ? data.bytesize : data.size 120 | bytes = FFI::MemoryPointer.new(:char, size) 121 | bytes.put_bytes(0, data, 0, size) 122 | rw = SDL.RWFromMem(bytes, size) 123 | 124 | surf = if type 125 | SDL::Image.LoadTyped_RW(rw, 1, type) 126 | else 127 | SDL::Image.Load_RW(rw, 1) 128 | end 129 | 130 | if surf.pointer.null? 131 | raise( Rubygame::SDLError, 132 | "Couldn't load image from string: #{SDL.GetError()}" ) 133 | end 134 | 135 | new(surf) 136 | end 137 | 138 | end 139 | 140 | end 141 | -------------------------------------------------------------------------------- /lib/rubygame/joystick.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2009 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | # The Joystick class interfaces with joysticks, gamepads, and other 23 | # similar hardware devices used to play games. Each joystick may 24 | # have zero or more #axes, #balls, #hats, and/or #buttons. 25 | # 26 | # After a Joystick object is successfully created, events for that 27 | # Joystick will begin appearing on the EventQueue when a button is 28 | # pressed or released, a control stick is moved, etc. 29 | # 30 | # You can use Joystick.activate_all to start receiving events for 31 | # all joysticks (equivalent to creating them all individually with 32 | # Joystick.new). You can use Joystick.deactivate_all to stop 33 | # receiving events for all joysticks. 34 | # 35 | # As of Rubygame 2.4, these are the current, "new-style" Joystick 36 | # event classes: 37 | # 38 | # * Events::JoystickAxisMoved 39 | # * Events::JoystickButtonPressed 40 | # * Events::JoystickButtonReleased 41 | # * Events::JoystickBallMoved 42 | # * Events::JoystickHatMoved 43 | # 44 | # These old Joystick-related events are deprecated and will be 45 | # removed in Rubygame 3.0: 46 | # 47 | # * JoyAxisEvent 48 | # * JoyBallEvent 49 | # * JoyHatEvent 50 | # * JoyDownEvent 51 | # * JoyUpEvent 52 | # 53 | # For more information about "new-style" events, see 54 | # EventQueue.enable_new_style_events. 55 | # 56 | class Rubygame::Joystick 57 | 58 | 59 | # Returns the total number of joysticks detected on the system. 60 | def self.num_joysticks 61 | SDL.NumJoysticks() 62 | end 63 | 64 | 65 | # Returns the name of Nth joystick on the system. The name is 66 | # implementation-dependent. See also #name. 67 | # 68 | def self.get_name( index ) 69 | SDL.JoystickName( index ) 70 | end 71 | 72 | 73 | # Activate all joysticks on the system, equivalent to calling 74 | # Joystick.new for every joystick available. This will allow 75 | # joystick-related events to be sent to the EventQueue for all 76 | # joysticks. 77 | # 78 | # Returns:: Array of zero or more Joysticks. 79 | # May raise:: SDLError, if the joystick system could not be 80 | # initialized. 81 | # 82 | def self.activate_all 83 | # Initialize if it isn't already. 84 | if( SDL.WasInit(SDL::INIT_JOYSTICK) == 0 ) 85 | if( SDL.Init(SDL::INIT_JOYSTICK) != 0 ) 86 | raise Rubygame::SDLError, "Could not initialize SDL joysticks." 87 | end 88 | end 89 | 90 | # Collect Joystick instances in an Array 91 | joysticks = [] 92 | 93 | num_joysticks.times do |i| 94 | joysticks << new( i ) 95 | end 96 | 97 | return joysticks 98 | end 99 | 100 | 101 | # Deactivate all joysticks on the system. This will stop all 102 | # joystick-related events from being sent to the EventQueue. 103 | # 104 | def self.deactivate_all 105 | # Return right away if it isn't active 106 | return if( SDL.WasInit(SDL::INIT_JOYSTICK) == 0 ) 107 | 108 | num_joysticks.times do |i| 109 | joy = SDL.JoystickOpen(i) 110 | unless( joy.pointer.nil? ) 111 | SDL.JoystickClose( joy ) 112 | end 113 | end 114 | 115 | return nil 116 | end 117 | 118 | 119 | # Create and initialize an interface to the Nth joystick on the 120 | # system. Raises SDLError if the joystick could not be opened. 121 | # 122 | def initialize( index ) 123 | @struct = SDL.JoystickOpen( index ) 124 | if( @struct.pointer.null? ) 125 | raise( Rubygame::SDLError, "Could not open joystick %d: %s"% 126 | [index, SDL.GetError()] ) 127 | end 128 | end 129 | 130 | 131 | # Returns the index number of the Joystick, i.e. the identifier 132 | # number of the joystick that this interface controls. This is the 133 | # same number that was given to Joystick.new. 134 | # 135 | def index 136 | SDL.JoystickIndex( @struct ) 137 | end 138 | 139 | 140 | # Returns a String containing the name of the Joystick. The name is 141 | # implementation-dependent. See also Joystick.get_name. 142 | # 143 | def name 144 | SDL.JoystickName( self.index ) 145 | end 146 | 147 | 148 | # Returns the number of axes (singular: axis) featured on the 149 | # Joystick. Each control stick generally has two axes (X and Y), 150 | # although there are other types of controls which are represented 151 | # as one or more axes. 152 | # 153 | def axes 154 | SDL.JoystickNumAxes( @struct ) 155 | end 156 | 157 | 158 | # Returns the number of trackballs featured on the Joystick. A 159 | # trackball is usually a small sphere which can be rotated in-place 160 | # in any direction, registering relative movement along two axes. 161 | # 162 | def balls 163 | SDL.JoystickNumBalls( @struct ) 164 | end 165 | 166 | 167 | # Returns the number of hats featured on the Joystick. A hat is a 168 | # switch which can be pushed in one of several directions, or 169 | # centered. 170 | # 171 | def hats 172 | SDL.JoystickNumHats( @struct ) 173 | end 174 | 175 | 176 | # Returns the number of buttons featured on the Joystick. A button 177 | # can be in one of two states: neutral, or pushed. 178 | # 179 | def buttons 180 | SDL.JoystickNumButtons( @struct ) 181 | end 182 | 183 | 184 | end 185 | -------------------------------------------------------------------------------- /lib/rubygame/main.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2010 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | require "ruby-sdl-ffi/sdl" 23 | 24 | 25 | module Rubygame 26 | 27 | VERSIONS = { 28 | :rubygame => [2, 6, 4], 29 | :sdl => SDL.Linked_Version().to_ary 30 | } 31 | 32 | 33 | # Initialize Rubygame. This should be called soon after you 34 | # +require+ Rubygame, so that everything will work properly. 35 | # 36 | def self.init 37 | if( SDL.Init(SDL::INIT_EVERYTHING) == 0 ) 38 | SDL.EnableUNICODE(1) 39 | else 40 | raise Rubygame::SDLError, "Could not initialize SDL: #{SDL.GetError()}" 41 | end 42 | end 43 | 44 | 45 | # Quit Rubygame. This should be used before your program terminates, 46 | # especially if you have been using a fullscreen Screen! (Otherwise, 47 | # the desktop resolution might not revert to its previous setting on 48 | # some platforms, and your users will be frustrated and confused!) 49 | # 50 | def self.quit 51 | SDL.Quit 52 | end 53 | 54 | 55 | 56 | # Indicates that an SDL function failed. 57 | class SDLError < RuntimeError 58 | end 59 | 60 | 61 | 62 | SWSURFACE = SDL::SWSURFACE 63 | HWSURFACE = SDL::HWSURFACE 64 | ASYNCBLIT = SDL::ASYNCBLIT 65 | ANYFORMAT = SDL::ANYFORMAT 66 | HWPALETTE = SDL::HWPALETTE 67 | HWACCEL = SDL::HWACCEL 68 | SRCCOLORKEY = SDL::SRCCOLORKEY 69 | RLEACCELOK = SDL::RLEACCELOK 70 | RLEACCEL = SDL::RLEACCEL 71 | SRCALPHA = SDL::SRCALPHA 72 | PREALLOC = SDL::PREALLOC 73 | 74 | DOUBLEBUF = SDL::DOUBLEBUF 75 | FULLSCREEN = SDL::FULLSCREEN 76 | OPENGL = SDL::OPENGL 77 | OPENGLBLIT = SDL::OPENGLBLIT 78 | RESIZABLE = SDL::RESIZABLE 79 | NOFRAME = SDL::NOFRAME 80 | 81 | 82 | end 83 | -------------------------------------------------------------------------------- /lib/rubygame/mediabag.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2007 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | 22 | #-- 23 | # MediaBag is DEPRECATED and will be removed in Rubygame 3.0! 24 | # Warn the user when this file is loaded. 25 | #++ 26 | Rubygame.deprecated("Rubygame::MediaBag", "3.0") 27 | 28 | 29 | module Rubygame 30 | 31 | # *NOTE*: MediaBag is DEPRECATED and will be removed in Rubygame 3.0! 32 | # Use the NamedResource functionality of Music, Sound, and Surface instead. 33 | # 34 | # *NOTE*: you must require 'rubygame/mediabag' manually to gain access to 35 | # Rubygame::MediaBag. It is not imported with Rubygame by default! 36 | # 37 | # A Hash-like class which will load and retain media files (images and 38 | # sounds), so that the file can be loaded once, but used many times. 39 | # 40 | # The first time a file is requested with the #[] method,that file will be 41 | # loaded into memory. All subsequent requests for the same file will return 42 | # a reference to the already-loaded version. Ideally, objects should not 43 | # have to know whether or not the image has been loaded or not. 44 | class MediaBag 45 | @@image_ext = %W{bmp gif jpg lbm pcx png pnm ppm pgm pbm tga tif xcf xpm} 46 | @@sound_ext = %W{wav} 47 | 48 | def initialize() 49 | @media = Hash.new 50 | end 51 | 52 | # Return a reference to the stored value for key. 53 | # If there is no value for key, automatically attempt to load key 54 | # as a filename (guessing the file type based on its extension) 55 | # 56 | def [](key) 57 | @media[key] or load(key) 58 | rescue Rubygame::SDLError 59 | nil 60 | end 61 | 62 | # Load the file, but only if it has not been previously loaded. 63 | def load(filename) 64 | @media[filename] or store( filename, load_file(filename) ) 65 | end 66 | 67 | # Store value as key, but only if there is no previous value. 68 | def store(key,value) 69 | @media[key] ||= value 70 | end 71 | 72 | # Forcibly (re)load the file, replacing the previous version in memory 73 | # (if any). 74 | def force_load(filename) 75 | force_store( filename, load_file(filename) ) 76 | end 77 | 78 | # Forcibly store value as key, replacing the previous value (if any). 79 | def force_store(key,value) 80 | @media[key] = value 81 | end 82 | 83 | def load_file(filename) 84 | case File::extname(filename).downcase[1..-1] 85 | when *(@@image_ext) 86 | return load_image(filename) 87 | when *(@@sound_ext) 88 | return load_sound(filename) 89 | else 90 | raise(ArgumentError,"Unrecognized file extension `%s': %s"% 91 | [File::extname(filename), filename]) 92 | end 93 | end 94 | 95 | def load_image(filename) 96 | return Rubygame::Surface.load_image(filename) 97 | end 98 | 99 | def load_sound(filename) 100 | return Rubygame::Mixer::Sample.load_audio(filename) 101 | end 102 | end 103 | 104 | end 105 | -------------------------------------------------------------------------------- /lib/rubygame/mixer.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2009 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | require "ruby-sdl-ffi/mixer" 22 | 23 | Rubygame::VERSIONS[:sdl_mixer] = SDL::Mixer.Linked_Version.to_ary 24 | 25 | require "rubygame/audio" 26 | require "rubygame/sound" 27 | require "rubygame/music" 28 | require "rubygame/deprecated_mixer" 29 | -------------------------------------------------------------------------------- /lib/rubygame/named_resource.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 3 | # Copyright (C) 2004-2008 John Croisant 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | #++ 19 | 20 | 21 | module Rubygame 22 | 23 | # NamedResource is a mix-in module to implement a globally-available 24 | # resource table, a @name variable and accessors, and a system for 25 | # automatically loading resources when they are first needed. 26 | # 27 | # This module is used for Rubygame::Music, Rubygame::Sound, and 28 | # Rubygame::Surface. You can use it in your own classes this way: 29 | # 30 | # 1. Do 'include Rubygame::NamedResource' in your class definition. 31 | # 32 | # 2. Set MyClass.autoload_dirs to an Array of directories to look 33 | # for files when autoloading. Tip: use File.join to create 34 | # paths that work on any operating system. 35 | # 36 | # 3. Define #autoload to implement the behavior for your class, 37 | # or leave it as the default if you don't need autoloading. 38 | # See the docs for #autoload for more information. 39 | # 40 | # Here's an example of how you could use this for a class which 41 | # loads maps from a file: 42 | # 43 | # class Map 44 | # include Rubygame::NamedResource 45 | # 46 | # Map.autoload_dirs = [ File.join("maps","world_1"), 47 | # File.join("maps","custom") ] 48 | # 49 | # def autoload( name ) 50 | # # Searches autoload_dirs for the file 51 | # path = find_file( name ) 52 | # 53 | # if( path ) 54 | # return load_map( path ) 55 | # else 56 | # return nil 57 | # end 58 | # end 59 | # 60 | # def load_map( path ) 61 | # # Your code to do the real loading, then return 62 | # # the created instance of Map class. 63 | # # ... 64 | # return map_instance 65 | # end 66 | # end 67 | # 68 | # Here's an example of how you could then use the Map class: 69 | # 70 | # map = Map["level_1.map"] 71 | # 72 | # if( map ) 73 | # start_playing( map ) 74 | # else 75 | # raise "Oops! The map file for Level 1 doesn't exist!" 76 | # end 77 | # 78 | module NamedResource 79 | 80 | 81 | # Adds class methods when the NamedResource module is included 82 | # in a class. (Here, we are assuming that the NamedResource 83 | # module was included in a class called MyClass.) 84 | module NamedResourceClassMethods 85 | 86 | # An Array of paths to check for files. See #find_file. 87 | attr_accessor :autoload_dirs 88 | 89 | 90 | # call-seq: 91 | # MyClass[ name ] -> instance or nil 92 | # 93 | # Retrieves an instance of the class from a per-class resource 94 | # table (Hash). 95 | # 96 | # If no object has been saved under the given name, invoke 97 | # #autoload to try to load a new instance, store it in the 98 | # Hash table under this name, and sets the instance's @name 99 | # to this name. 100 | # 101 | def []( name ) 102 | result = @resources[name] 103 | 104 | if result.nil? 105 | result = autoload(name) 106 | if result 107 | self[name] = result 108 | result.name = name 109 | end 110 | end 111 | 112 | return result 113 | end 114 | 115 | 116 | # call-seq: 117 | # MyClass[ name ] = instance 118 | # 119 | # Stores an instance of the class in a per-class resource table 120 | # (Hash) for future access. If another object is already stored 121 | # with this name, the old record is lost. 122 | # 123 | # May raise: TypeError, if you try to store anything 124 | # that is not kind of this class. 125 | # 126 | def []=( name, value ) 127 | if( value.kind_of? self ) 128 | @resources[name] = value 129 | else 130 | raise TypeError, "#{self}#[]= can only store instances of #{self}" 131 | end 132 | end 133 | 134 | # call-seq: 135 | # MyClass.autoload( name ) -> instance or nil 136 | # 137 | # This method is invoked when a non-existing resource is 138 | # accessed with #[]. By default, this method simply returns 139 | # nil, effectively disabling autoloading. 140 | # 141 | # You should override this method in your class to provide 142 | # class-specific loading behavior, or leave it as the default if 143 | # you don't need autoloading. Your method should return either 144 | # an instance of the class, or nil. 145 | # 146 | # NOTE: The #find_file method is useful for getting the full 147 | # path to a file which matches the name. That's what it's there 148 | # for, so you should use it! 149 | # 150 | def autoload( name ) 151 | nil 152 | end 153 | 154 | 155 | # call-seq: 156 | # MyClass.basename( path ) -> filename 157 | # 158 | # Returns the basename for the path (i.e. the 159 | # filename without the directory). Same as 160 | # File.basename 161 | # 162 | def basename( path ) 163 | File.basename( path ) 164 | end 165 | 166 | 167 | # call-seq: 168 | # MyClass.exist?( path ) -> true or false 169 | # 170 | # True if the given path points to a file 171 | # that exists, otherwise false. Same as 172 | # File.exist? 173 | # 174 | def exist?( path ) 175 | File.exist?(path) 176 | end 177 | 178 | 179 | # call-seq: 180 | # MyClass.find_file( filename ) -> path or nil 181 | # 182 | # Checks every directory in @autoload_dirs for 183 | # a file with the given name, and returns the 184 | # path (directory and name) for the first match. 185 | # 186 | # If no directories have a file with that name, 187 | # return nil. 188 | # 189 | def find_file( filename ) 190 | dir = @autoload_dirs.find { |dir| 191 | exist?( File.join(dir,filename) ) 192 | } 193 | 194 | if dir 195 | return File.join(dir,filename) 196 | else 197 | return nil 198 | end 199 | end 200 | 201 | end 202 | 203 | 204 | # Sets up the class when this module is included. 205 | # Adds the class methods and defines class instance 206 | # variables. 207 | def self.included( object ) # :nodoc: 208 | 209 | class << object 210 | include NamedResourceClassMethods 211 | end 212 | 213 | object.instance_eval do 214 | @resources = Hash.new 215 | @autoload_dirs = [] 216 | end 217 | 218 | end 219 | 220 | 221 | # Returns the instance's @name. See also #name=. 222 | def name 223 | @name 224 | end 225 | 226 | # 227 | # Sets the instance's @name to the given String, or nil to 228 | # unset the name. See also #name. 229 | # 230 | # NOTE: This does not automatically store the instance in the 231 | # class resource table by name. Use the #[]= class method to do 232 | # that. 233 | # 234 | # The string is dup'ed and frozen before being stored. 235 | # 236 | # May raise: TypeError, if new_name is not a String or nil. 237 | # 238 | def name=( new_name ) 239 | if new_name.nil? 240 | return @name = nil 241 | end 242 | 243 | unless new_name.kind_of? String 244 | raise TypeError, "name must be a String (got #{new_name.class})" 245 | end 246 | 247 | @name = new_name.dup 248 | @name.freeze 249 | end 250 | 251 | 252 | end 253 | 254 | end 255 | -------------------------------------------------------------------------------- /lib/rubygame/shared.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Common / utility methods. 3 | # 4 | #-- 5 | # 6 | # Rubygame -- Ruby code and bindings to SDL to facilitate game creation 7 | # Copyright (C) 2009 John Croisant 8 | # 9 | # This library is free software; you can redistribute it and/or 10 | # modify it under the terms of the GNU Lesser General Public 11 | # License as published by the Free Software Foundation; either 12 | # version 2.1 of the License, or (at your option) any later version. 13 | # 14 | # This library is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | # Lesser General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Lesser General Public 20 | # License along with this library; if not, write to the Free Software 21 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 | # 23 | #++ 24 | 25 | 26 | module Rubygame 27 | 28 | class DeprecationError < RuntimeError # :nodoc: 29 | end 30 | 31 | # Warn about a deprecated Rubygame feature. The behavior of this 32 | # method is influenced by the value of ENV["RUBYGAME_DEPRECATED"]: 33 | # 34 | # (default):: Print a warning with Kernel.warn the first time each 35 | # deprecated feature is used. Note that Kernel.warn is 36 | # silent unless warnings are enabled (e.g. -W flag). 37 | # 38 | # "warn":: Print a warning on STDERR the first time each 39 | # deprecated feature is used. 40 | # 41 | # "warn!":: Print a warning on STDERR every time a deprecated 42 | # feature is used. 43 | # 44 | # "error":: Raise Rubygame::DeprecationError the first time each 45 | # deprecated feature is used. 46 | # 47 | # "error!":: Raise Rubygame::DeprecationError every time a 48 | # deprecated feature is used. 49 | # 50 | # "quiet":: Never warn when any deprecated feature is used. 51 | # 52 | def self.deprecated( feature, version=nil, info=nil) # :nodoc: 53 | @deprec_warned ||= {} 54 | 55 | config = ENV["RUBYGAME_DEPRECATED"] 56 | return if /^quiet$/i =~ config 57 | 58 | if( /^(error!|warn!)$/i =~ config || !@deprec_warned[feature] ) 59 | message = 60 | "#{feature} is DEPRECATED and will be removed in " + 61 | (version ? "Rubygame #{version}" : "a future version of Rubygame") + 62 | "! " + (info || "Please see the docs for more information.") 63 | 64 | case config 65 | when /^error!?$/i 66 | raise Rubygame::DeprecationError.new(message) 67 | when /^warn!?$/i 68 | STDERR.puts 'warning: ' + message 69 | else 70 | Kernel.warn 'warning: ' + message 71 | end 72 | 73 | @deprec_warned[feature] = true 74 | end 75 | 76 | nil 77 | end 78 | 79 | 80 | # Initialize the SDL video system if necessary. 81 | def self.init_video_system # :nodoc: 82 | if( SDL::WasInit(SDL::INIT_VIDEO) == 0 ) 83 | return SDL::Init(SDL::INIT_VIDEO) 84 | else 85 | return 0 86 | end 87 | end 88 | 89 | 90 | # Take nil, an integer, or an Array of integers. Returns an integer. 91 | def self.collapse_flags( flags ) # :nodoc: 92 | case flags 93 | when Array 94 | flags.inject(0){ |mem, flag| mem|flag } 95 | when Numeric 96 | flags 97 | when nil 98 | 0 99 | else 100 | raise( ArgumentError, "Wrong type for flags " + 101 | "(wanted integer, Array, or nil; got #{flags.class})." ) 102 | end 103 | end 104 | 105 | end 106 | -------------------------------------------------------------------------------- /samples/FreeSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/FreeSans.ttf -------------------------------------------------------------------------------- /samples/README: -------------------------------------------------------------------------------- 1 | Certain items in this directory are copyrighted works, but the nature 2 | of their media makes inclusion of copyright and licensing information 3 | impossible or inconvenient. 4 | 5 | Copyright and licensing information for those items is included herein 6 | for your convenience. 7 | 8 | 9 | "panda.png" copyright (C) 2004 John Croisant 10 | "ruby.png" copyright (C) 2006 John Croisant 11 | "rubygame.png" copyright (C) 2009 John Croisant 12 | "song.ogg" copyright (C) 1999 Bjorn De Meyer 13 | "term16.png" copyright (C) 2005 John Croisant 14 | 15 | 16 | "panda.png", "ruby.png", "rubygame.png", "song.ogg", and "term16.png" 17 | are licensed under the Creative Commons Attribution-ShareAlike 2.5 License. 18 | 19 | To view a copy of this license, visit 20 | 21 | http://creativecommons.org/licenses/by-sa/2.5/ 22 | 23 | or send a letter to: 24 | 25 | Creative Commons, 26 | 543 Howard Street, 5th Floor, 27 | San Francisco, California, 94105, USA. 28 | 29 | 30 | 31 | "FreeSans.ttf" Copyleft 2002, 2003, 2005, 2008, 2009 32 | Free Software Foundation. 33 | 34 | "FreeSans.ttf" is licensed under the GNU General Public License. 35 | 36 | You should have received a copy of the GNU General Public License along with 37 | this program; if not, write to: 38 | 39 | Free Software Foundation, Inc., 40 | 59 Temple Place, Suite 330, 41 | Boston, MA 02111-1307 USA 42 | -------------------------------------------------------------------------------- /samples/chimp.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/chimp.bmp -------------------------------------------------------------------------------- /samples/demo_draw.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This program is released to the PUBLIC DOMAIN. 4 | # It is distributed in the hope that it will be useful, 5 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 6 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | require "rubygame" 9 | include Rubygame 10 | 11 | $stdout.sync = true 12 | 13 | Rubygame.init() 14 | 15 | 16 | # SDL_gfx is required for drawing shapes and rotating/zooming Surfaces. 17 | $gfx_ok = (VERSIONS[:sdl_gfx] != nil) 18 | 19 | unless ( $gfx_ok ) 20 | raise "You must have SDL_gfx support to run this demo!" 21 | end 22 | 23 | 24 | 25 | # Set up autoloading for Surfaces. Surfaces will be loaded automatically 26 | # the first time you use Surface["filename"]. Check out the docs for 27 | # Rubygame::NamedResource for more info about that. 28 | # 29 | Surface.autoload_dirs = [ File.dirname(__FILE__) ] 30 | 31 | 32 | 33 | screen = Screen.open([320,240]) 34 | screen.title = "Drawing test" 35 | 36 | 37 | 38 | # Make the background surface. We'll draw on this, then blit (copy) it 39 | # onto the screen. We'll also use it as the background for "erasing" 40 | # the pandas from their old positions each frame. 41 | background = Surface.new( screen.size ) 42 | 43 | 44 | # Filling with colors in a variety of ways. 45 | # See the Rubygame::Color module for more info. 46 | def fill_background( background ) 47 | background.fill( Color::ColorRGB.new([0.1, 0.2, 0.35]) ) 48 | background.fill( :black, [70,120,80,80] ) 49 | background.fill( "dark red", [80,110,80,80] ) 50 | end 51 | 52 | 53 | # Draw a bunch of shapes to try out the drawing methods 54 | def draw_some_shapes( background ) 55 | # Create a new surface 56 | a = Surface.new([100,100]) 57 | 58 | # Fill it with blue 59 | a.fill([70,70,255]) 60 | 61 | # Fill a specific part of it with a different blue 62 | rect1 = Rect.new([3,3,94,94]) 63 | a.fill([40,40,150],rect1) 64 | 65 | # Draw a black box with white almost at the edges 66 | a.draw_box_s( [30,30], [70,70], [0,0,0] ) 67 | a.draw_box( [31,31], [69,69], [255,255,255] ) 68 | 69 | # Draw a circle in the box 70 | a.draw_circle_s( [50,50], 10, [100,150,200] ) 71 | 72 | # Two diagonal white lines, the right anti-aliased, the left not 73 | a.draw_line([31,69],[49,31],[255,255,255]) 74 | a.draw_line_a([49,31],[69,69],[255,255,255]) 75 | 76 | # Finally, blit (copy) this interesting surface onto 77 | # the background image 78 | a.blit(background,[50,50],[0,0,90,80]) 79 | end 80 | 81 | 82 | # Draw a filled pentagon with a lighter border 83 | def draw_pentagon( background ) 84 | points = [ [50,150], [100,140], [150,160], [120,180], [60,170] ] 85 | background.draw_polygon_s( points, [100,100,100] ) 86 | background.draw_polygon_a( points, [200,200,200] ) 87 | end 88 | 89 | 90 | # Draw a pepperoni pizza! (Use your imagination...) 91 | def draw_pizza( background ) 92 | # Crust 93 | background.draw_arc_s( [250,200], 34, [210,150], [180,130,50]) 94 | 95 | # Cheese -- similar to the crust, but different radius and color 96 | background.draw_arc_s( [250,200], 30, [210,150], [230,180,80]) 97 | 98 | # Pepperonis 99 | background.draw_circle_s( [240,180], 4, :dark_red ) 100 | background.draw_circle_s( [265,185], 4, :dark_red ) 101 | background.draw_circle_s( [258,200], 4, :dark_red ) 102 | background.draw_circle_s( [240,215], 4, :dark_red ) 103 | background.draw_circle_s( [260,220], 4, :dark_red ) 104 | end 105 | 106 | 107 | # _Try_ to draw an anti-aliased, solid ellipse, but it doesn't work 108 | # well. If you look closely at the white ellipse, you can see that it 109 | # isn't anti-aliased on the left and right side, and there are some 110 | # black specks on the top and bottom where the two ellipses don't 111 | # quite match. 112 | # 113 | # It sure would be nice if SDL_gfx had anti-aliased solid shapes... 114 | # 115 | def draw_antialiased_filled_ellipse( background ) 116 | background.draw_ellipse_s([200,150],[30,25], :beige ) 117 | background.draw_ellipse_a([200,150],[30,25], :beige ) 118 | end 119 | 120 | 121 | # Render some text with SFont (image-based font) 122 | def render_sfont_text( background ) 123 | require "rubygame/sfont" 124 | sfont = SFont.new( Surface["term16.png"] ) 125 | result = sfont.render( "This is some SFont text!" ) 126 | result.blit( background, [10,10] ) 127 | end 128 | 129 | 130 | # Render some text with TTF (vector-based font) 131 | def render_ttf_text( background ) 132 | TTF.setup() 133 | ttfont_path = File.join(File.dirname(__FILE__),"FreeSans.ttf") 134 | ttfont = TTF.new( ttfont_path, 20 ) 135 | 136 | result = ttfont.render( "This is some TTF text!", true, [250,250,250] ) 137 | result.blit( background, [20,200] ) 138 | end 139 | 140 | 141 | # Create another surface to test transparency blitting 142 | def do_transparent_blit( background ) 143 | b = Surface.new([200,50]) 144 | b.fill([150,20,40]) 145 | b.set_alpha(123)# approx. half transparent 146 | b.blit(background,[20,40]) 147 | end 148 | 149 | 150 | # Call all those functions to draw on the background. 151 | # Try commenting some of these out or reordering them to 152 | # see what happens! 153 | 154 | fill_background( background ) 155 | draw_some_shapes( background ) 156 | draw_pentagon( background ) 157 | draw_pizza( background ) 158 | draw_antialiased_filled_ellipse( background ) 159 | render_sfont_text( background ) 160 | render_ttf_text( background ) 161 | do_transparent_blit( background ) 162 | 163 | 164 | # Now blit the background onto the screen and update the screen once. 165 | # During the loop, we'll use 'dirty rect' updating to refresh only the 166 | # parts of the screen that have changed. 167 | background.blit(screen,[0,0]) 168 | 169 | screen.update() 170 | 171 | 172 | 173 | queue = EventQueue.new() 174 | queue.ignore = [ActiveEvent,MouseMotionEvent,MouseUpEvent,MouseDownEvent] 175 | queue.wait() 176 | -------------------------------------------------------------------------------- /samples/demo_imagefont.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | 5 | require "rubygame" 6 | require "rubygame/imagefont" 7 | 8 | include Rubygame 9 | 10 | 11 | class App 12 | def initialize( filename, options={} ) 13 | @filename = filename 14 | @basename = File.basename(filename) 15 | 16 | @font = ImageFont.new( Surface.load(@filename) ) 17 | 18 | options = { 19 | :padding => 10, 20 | :message => default_message, 21 | :background => [50,50,50], 22 | }.merge(options) 23 | 24 | @message = options[:message] 25 | @padding = options[:padding] 26 | @background = options[:background] 27 | 28 | @screen = _make_screen() 29 | @queue = _make_queue() 30 | @clock = _make_clock() 31 | 32 | redraw() 33 | end 34 | 35 | 36 | def go 37 | catch(:app_quit) do 38 | loop do 39 | step 40 | end 41 | end 42 | end 43 | 44 | 45 | def step 46 | @screen.update() 47 | 48 | @queue.each do |event| 49 | case event 50 | when Events::QuitRequested 51 | quit() 52 | when Events::KeyPressed 53 | case event.key 54 | when :escape 55 | quit() 56 | else 57 | handle_keystroke(event) 58 | end 59 | end 60 | end 61 | 62 | @clock.tick() 63 | end 64 | 65 | 66 | def handle_keystroke(event) 67 | case event.key 68 | when :backspace 69 | self.message = @message[0..-2] 70 | when :return, :keypad_enter 71 | self.message += "\n" 72 | else 73 | unless event.string.empty? 74 | self.message += event.string 75 | end 76 | end 77 | end 78 | 79 | 80 | def redraw 81 | @screen.fill(@background) 82 | @font.render_to(@message, @screen, :offset => [@padding, @padding]) 83 | @screen.update 84 | end 85 | 86 | 87 | def message 88 | @message.dup.freeze 89 | end 90 | 91 | def message=( new_message ) 92 | # Increase the screen size to fit the new message, if necessary. 93 | new_size = _calculate_screen_size( new_message ) 94 | new_size[0] = [new_size[0], @screen.width].max 95 | new_size[1] = [new_size[1], @screen.height].max 96 | 97 | unless new_size == @screen.size 98 | @screen = _make_screen( new_size ) 99 | end 100 | 101 | @message = new_message 102 | 103 | redraw() 104 | end 105 | 106 | 107 | def default_message 108 | lines = 109 | ["Font: %s"%@basename, 110 | "", 111 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 112 | "The quick brown fox jumped over the lazy dog.", 113 | ""] 114 | 115 | # Necessary to use Array#each_slice with Ruby 1.8.6. 116 | require 'enumerator' 117 | 118 | # Add all the glyphs in the font, in rows of 32 glyphs. 119 | @font.glyphs.each_slice(32) { |slice| 120 | lines << slice.join('') 121 | } 122 | 123 | lines += ["", "Type on the keyboard to edit this text."] 124 | 125 | return lines.join("\n") 126 | end 127 | 128 | 129 | def quit 130 | throw :app_quit 131 | end 132 | 133 | 134 | private 135 | 136 | 137 | # Calculate how big the screen needs to be to fit the message. 138 | def _calculate_screen_size( message ) 139 | w, h = @font.render_size(message) 140 | 141 | w += @padding * 2 142 | h += @padding * 2 143 | 144 | # Make sure it's not a super huge window. 145 | w = 1600 if w > 1600 146 | h = 1000 if h > 1000 147 | 148 | [w,h] 149 | end 150 | 151 | 152 | def _make_screen( size=:auto ) 153 | size = _calculate_screen_size(@message) if size == :auto 154 | screen = Screen.new(size) 155 | screen.title = "ImageFont Test (%s)"%@basename 156 | return screen 157 | end 158 | 159 | 160 | def _make_queue 161 | Rubygame.enable_key_repeat 162 | return EventQueue.new { |q| 163 | q.enable_new_style_events 164 | } 165 | end 166 | 167 | 168 | def _make_clock 169 | return Clock.new { |c| 170 | c.calibrate 171 | c.enable_tick_events 172 | c.target_framerate = 30 173 | } 174 | end 175 | 176 | end 177 | 178 | 179 | if ARGV.length < 1 180 | font_name = "term16.png" 181 | puts < 3, :repeats => -1 ); 37 | puts('ERROR: Music not fading in') unless mus.fading?(:in) 38 | sleep 3 39 | 40 | puts "Playing for 2 seconds." 41 | sleep 2 42 | 43 | puts "Lowering volume to half for 3 seconds." 44 | mus.volume = 0.5; 45 | puts "ERROR: Volume wasn't adjusted" unless mus.volume == 0.5 46 | sleep 3 47 | 48 | puts "Restoring volume to full." 49 | mus.volume = 1.0; 50 | sleep 2 51 | 52 | puts "Pausing for 1 seconds." 53 | mus.pause 54 | puts "ERROR: Music not paused." unless mus.paused? 55 | sleep 1 56 | 57 | puts "Resuming." 58 | mus.unpause 59 | puts "ERROR: Music not resumed" unless mus.playing? 60 | 61 | puts "Playing for 2 seconds." 62 | sleep 2 63 | 64 | puts "Fading out over 2 seconds." 65 | mus.fade_out(2); 66 | puts "ERROR: Music not fading out " unless mus.fading?(:out) 67 | 68 | while mus.playing? or mus.fading? == :out do Thread.pass end 69 | # Test playing of music to the end 70 | 71 | puts "ERROR: Music not ended" if mus.playing? 72 | end 73 | 74 | music_thread = Thread.new do test_music() end 75 | music_thread.join 76 | Rubygame.quit() 77 | 78 | -------------------------------------------------------------------------------- /samples/demo_ttf.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This program is PUBLIC DOMAIN. 4 | # It is distributed in the hope that it will be useful, 5 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 6 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | require "rubygame" 9 | include Rubygame 10 | Rubygame.init() 11 | 12 | def test_noaa_nobg(font,screen,y) 13 | puts "No antialiasing, no background..." 14 | text = font.render("No AA, no BG",false,[200,200,200]) 15 | text.blit(screen,[0,y]) 16 | end 17 | 18 | def test_noaa_bg(font,screen,y) 19 | puts "No antialiasing, background..." 20 | text = font.render("No AA, BG",false,[200,200,200],[0,0,255]) 21 | text.blit(screen,[0,y]) 22 | end 23 | 24 | def test_aa_nobg(font,screen,y) 25 | puts "Antialiasing, no background..." 26 | text = font.render("AA, no BG",true,[200,200,200]) 27 | text.blit(screen,[0,y]) 28 | end 29 | 30 | def test_aa_bg(font,screen,y) 31 | puts "Antialiasing, background..." 32 | text = font.render("AA, BG",true,[200,200,200],[0,0,255]) 33 | text.blit(screen,[0,y]) 34 | end 35 | 36 | def test_bold_noaa(font,screen,y) 37 | puts "Bold, no antialiasing..." 38 | font.bold = true 39 | text = font.render("Bold, no AA",false,[200,200,200]) 40 | text.blit(screen,[0,y]) 41 | font.bold = false 42 | end 43 | 44 | def test_bold_aa(font,screen,y) 45 | puts "Bold, antialiasing..." 46 | font.bold = true 47 | text = font.render("Bold, AA",true,[200,200,200]) 48 | text.blit(screen,[0,y]) 49 | font.bold = false 50 | end 51 | 52 | def test_italic_noaa(font,screen,y) 53 | puts "Italic, no antialiasing..." 54 | font.italic = true 55 | text = font.render("Italic, no AA",false,[200,200,200]) 56 | text.blit(screen,[0,y]) 57 | font.italic = false 58 | end 59 | 60 | def test_italic_aa(font,screen,y) 61 | puts "Italic, antialiasing..." 62 | font.italic = true 63 | text = font.render("Italic, AA",true,[200,200,200]) 64 | text.blit(screen,[0,y]) 65 | font.italic = false 66 | end 67 | 68 | def test_underline_noaa(font,screen,y) 69 | puts "Underline, no antialiasing..." 70 | font.underline = true 71 | text = font.render("Underline, no AA",false,[200,200,200]) 72 | text.blit(screen,[0,y]) 73 | font.underline = false 74 | end 75 | 76 | def test_underline_aa(font,screen,y) 77 | puts "Underline, antialiasing..." 78 | font.underline = true 79 | text = font.render("Underline, AA",true,[200,200,200]) 80 | text.blit(screen,[0,y]) 81 | font.underline = false 82 | end 83 | 84 | def test_bi_noaa(font,screen,y) 85 | puts "Bold, Italic, no antialiasing..." 86 | font.bold = true 87 | font.italic = true 88 | text = font.render("B, I, no AA",false,[200,200,200]) 89 | text.blit(screen,[0,y]) 90 | font.bold = false 91 | font.italic = false 92 | end 93 | 94 | def test_bu_noaa(font,screen,y) 95 | puts "Bold, Underline, no antialiasing..." 96 | font.bold = true 97 | font.underline = true 98 | text = font.render("B, U, no AA",false,[200,200,200]) 99 | text.blit(screen,[0,y]) 100 | font.bold = false 101 | font.underline = false 102 | end 103 | 104 | def test_iu_noaa(font,screen,y) 105 | puts "Italic, Underline, no antialiasing..." 106 | font.italic = true 107 | font.underline = true 108 | text = font.render("I, U, no AA",false,[200,200,200]) 109 | text.blit(screen,[0,y]) 110 | font.italic = false 111 | font.underline = false 112 | end 113 | 114 | def test_bi_aa(font,screen,y) 115 | puts "Bold, Italic, antialiasing..." 116 | font.bold = true 117 | font.italic = true 118 | text = font.render("B, I, AA",true,[200,200,200]) 119 | text.blit(screen,[0,y]) 120 | font.bold = false 121 | font.italic = false 122 | end 123 | 124 | def test_bu_aa(font,screen,y) 125 | puts "Bold, Underline, antialiasing..." 126 | font.bold = true 127 | font.underline = true 128 | text = font.render("B, U, AA",true,[200,200,200]) 129 | text.blit(screen,[0,y]) 130 | font.bold = false 131 | font.underline = false 132 | end 133 | 134 | def test_iu_aa(font,screen,y) 135 | puts "Italic, Underline, antialiasing..." 136 | font.italic = true 137 | font.underline = true 138 | text = font.render("I, U, AA",true,[200,200,200]) 139 | text.blit(screen,[0,y]) 140 | font.italic = false 141 | font.underline = false 142 | end 143 | 144 | def main 145 | screen = Screen.open([300,300]) 146 | queue = EventQueue.new() 147 | queue.ignore = SDL_EVENTS - [QuitEvent, KeyDownEvent] 148 | 149 | 150 | unless VERSIONS[:sdl_ttf] 151 | raise "TTF is not usable. Bailing out." 152 | end 153 | TTF.setup() 154 | font = TTF.new("FreeSans.ttf",30) 155 | 156 | skip = font.line_skip() 157 | 158 | screen.fill([30,70,30]) 159 | y = -skip 160 | test_noaa_nobg(font,screen,y+=skip) 161 | test_noaa_bg(font,screen,y+=skip) 162 | test_aa_nobg(font,screen,y+=skip) 163 | test_aa_bg(font,screen,y+=skip) 164 | screen.update() 165 | 166 | queue.wait() 167 | 168 | screen.fill([30,70,30]) 169 | y = -skip 170 | test_bold_noaa(font,screen,y+=skip) 171 | test_bold_aa(font,screen,y+=skip) 172 | test_italic_noaa(font,screen,y+=skip) 173 | test_italic_aa(font,screen,y+=skip) 174 | test_underline_noaa(font,screen,y+=skip) 175 | test_underline_aa(font,screen,y+=skip) 176 | screen.update() 177 | 178 | queue.wait() 179 | 180 | screen.fill([30,70,30]) 181 | y = -skip 182 | test_bi_noaa(font,screen,y+=skip) 183 | test_bi_aa(font,screen,y+=skip) 184 | test_bu_noaa(font,screen,y+=skip) 185 | test_bu_aa(font,screen,y+=skip) 186 | test_iu_noaa(font,screen,y+=skip) 187 | test_iu_aa(font,screen,y+=skip) 188 | screen.update() 189 | 190 | queue.wait() 191 | end 192 | 193 | main() 194 | 195 | Rubygame.quit 196 | -------------------------------------------------------------------------------- /samples/demo_utf8.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Original script was contributed by ageldama (Yun, Jonghyouk) 4 | 5 | require 'encoding/character/utf-8' 6 | require 'rubygame' 7 | 8 | # Initialize Rubygame 9 | Rubygame.init 10 | screen = Rubygame::Screen.open([320,200]) 11 | queue = Rubygame::EventQueue.new 12 | 13 | # Initialize fonts 14 | 15 | fontname = 'FreeSans.ttf' 16 | str = u'abc123하이~' 17 | if ARGV[0] 18 | if File.exist?(File.expand_path(ARGV[0])) 19 | fontname = File.expand_path(ARGV[0]) 20 | str = ARGV[1..-1].join(" ") 21 | else 22 | str = ARGV[0..-1].join(" ") 23 | end 24 | else 25 | puts <= stop_time ) 107 | 108 | # Tick the clock. Returns a ClockTicked instance 109 | # telling us how long this frame was. 110 | tick_event = clock.tick 111 | 112 | # Update the Mover. 113 | mover.update( tick_event ) 114 | 115 | end 116 | 117 | end 118 | 119 | 120 | main_loop() 121 | -------------------------------------------------------------------------------- /samples/image_viewer.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # A basic image viewer demo for Rubygame. 4 | # 5 | # Usage: Pass an image file to this script to display it. To quit, 6 | # press Q or Escape, right click, or close the window. 7 | # 8 | # Author: John Croisant 9 | # Date: 2009-10-18 10 | # License: You may use this code however you want. No warranty. 11 | # 12 | 13 | 14 | # Load Rubygame and include Rubygame and Rubygame::Events 15 | # in the current namespace, for convenience. 16 | # 17 | require "rubygame" 18 | include Rubygame 19 | include Rubygame::Events 20 | 21 | 22 | # Initialize Rubygame, and clean up when script exits. 23 | Rubygame.init 24 | at_exit { Rubygame.quit } 25 | 26 | 27 | # Decide which image file to load. 28 | this_dir = File.dirname( __FILE__ ) 29 | file = File.expand_path( "rubygame.png", this_dir ) # default file 30 | if ARGV[0] 31 | file = File.expand_path( ARGV[0], this_dir ) 32 | else 33 | puts "You can pass an script to this file to load it." 34 | end 35 | 36 | 37 | # Wrap this code in a "begin/rescue" block to recover from errors if 38 | # the image cannot be loaded. 39 | # 40 | begin 41 | 42 | # Load the image 43 | image = Surface.load( file ) 44 | 45 | # Open a new Rubygame window to fit the image. 46 | screen = Screen.open( image.size ) 47 | screen.title = File.basename(file) 48 | 49 | # Blit ("paste") the image onto the screen. 50 | image.blit( screen, [0,0] ) 51 | 52 | rescue SDLError, NoMethodError 53 | 54 | # Oops, load failed. 55 | puts "#{File.basename($0)}: Could not load image: #{file}" 56 | 57 | # Open a new Rubygame window 58 | screen = Screen.open( [200,200] ) 59 | screen.title = "Load Error" 60 | 61 | # Fill with white 62 | screen.fill( :white ) 63 | 64 | # Thick red line from top left to bottom right. 65 | # Point [0,0] is the top left corner of the screen, 66 | # [200,200] is bottom right. 67 | # 68 | screen.draw_polygon_s( [[ -5, 5], [ 5, -5], 69 | [205, 195], [195, 205]], :red ) 70 | 71 | # Now from top right to bottom left. 72 | screen.draw_polygon_s( [[195, -5], [205, 5], 73 | [ 5, 205], [ -5, 195]], :red ) 74 | 75 | end 76 | 77 | 78 | # Set up an event handler to listen for events: 79 | # 80 | # - Q or Escape keys press 81 | # - Right mouse click inside the window 82 | # - Window close button press 83 | # 84 | # When any of these events occurs, the "exit_script" method is called. 85 | # 86 | class MyHandler 87 | include Rubygame::EventHandler::HasEventHandler 88 | end 89 | 90 | handler = MyHandler.new 91 | handler.make_magic_hooks( :q => :exit_script, 92 | :escape => :exit_script, 93 | :mouse_right => :exit_script, 94 | QuitRequested => :exit_script ) 95 | 96 | def exit_script 97 | exit 98 | end 99 | 100 | 101 | # Repeat this code until the user quits. 102 | loop do 103 | 104 | # Fetch the user input events from SDL. 105 | events = Rubygame::Events.fetch_sdl_events 106 | 107 | # Pass each input to the event handler to check. 108 | events.each do |event| 109 | handler.handle( event ) 110 | end 111 | 112 | # Refresh the screen 113 | screen.update 114 | 115 | # Pause for a while before checking for more events. 116 | sleep 0.1 117 | 118 | end 119 | -------------------------------------------------------------------------------- /samples/load_and_blit.rb: -------------------------------------------------------------------------------- 1 | #/usr/bin/env ruby 2 | 3 | # A very basic sample application. 4 | 5 | require "rubygame" 6 | include Rubygame 7 | 8 | Rubygame.init 9 | 10 | screen = Screen.open([320,240]) 11 | 12 | queue = EventQueue.new() { 13 | |q| q.ignore = [MouseMotionEvent, ActiveEvent] 14 | } 15 | 16 | image = Surface.load_image("panda.png") 17 | puts "Size is: [%s,%s]"%image.size 18 | image.blit(screen,[0,0]) 19 | 20 | queue.wait() { screen.update() } 21 | 22 | Rubygame.quit 23 | -------------------------------------------------------------------------------- /samples/palettized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/palettized.png -------------------------------------------------------------------------------- /samples/panda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/panda.png -------------------------------------------------------------------------------- /samples/punch.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/punch.wav -------------------------------------------------------------------------------- /samples/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/ruby.png -------------------------------------------------------------------------------- /samples/rubygame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/rubygame.png -------------------------------------------------------------------------------- /samples/song.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/song.ogg -------------------------------------------------------------------------------- /samples/term16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/term16.png -------------------------------------------------------------------------------- /samples/whiff.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/samples/whiff.wav -------------------------------------------------------------------------------- /spec/audio_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | 5 | 6 | 7 | ######################### 8 | ## ## 9 | ## GENERAL ## 10 | ## ## 11 | ######################### 12 | 13 | 14 | describe "Opening audio for the first time" do 15 | after :each do 16 | Rubygame.close_audio 17 | end 18 | 19 | it "should return true" do 20 | Rubygame.open_audio.should be_true 21 | end 22 | end 23 | 24 | 25 | describe "Opening audio when it's already open" do 26 | before :each do 27 | Rubygame.open_audio 28 | end 29 | 30 | after :each do 31 | Rubygame.close_audio 32 | end 33 | 34 | it "should return false" do 35 | Rubygame.open_audio.should be_false 36 | end 37 | end 38 | 39 | 40 | describe "Opening audio that was opened then closed again" do 41 | before :each do 42 | Rubygame.open_audio 43 | Rubygame.close_audio 44 | end 45 | 46 | after :each do 47 | Rubygame.close_audio 48 | end 49 | 50 | it "should return true" do 51 | Rubygame.open_audio.should be_true 52 | end 53 | end 54 | 55 | 56 | describe "Opening audio with invalid argument" do 57 | after :each do 58 | Rubygame.close_audio 59 | end 60 | 61 | it "should raise TypeError" do 62 | lambda{ Rubygame.open_audio(:foo) }.should raise_error(TypeError) 63 | end 64 | end 65 | 66 | 67 | describe "Closing audio that was open" do 68 | before :each do 69 | Rubygame.open_audio 70 | end 71 | 72 | after :each do 73 | Rubygame.close_audio 74 | end 75 | 76 | it "should return true" do 77 | Rubygame.close_audio.should be_true 78 | end 79 | end 80 | 81 | 82 | describe "Closing audio that was not open" do 83 | before :each do 84 | end 85 | 86 | after :each do 87 | Rubygame.close_audio 88 | end 89 | 90 | it "should return false" do 91 | Rubygame.close_audio.should be_false 92 | end 93 | end 94 | 95 | 96 | 97 | ######################### 98 | ## ## 99 | ## FREQUENCY ## 100 | ## ## 101 | ######################### 102 | 103 | 104 | describe "Opening audio with a valid frequency" do 105 | after :each do 106 | Rubygame.close_audio 107 | end 108 | 109 | it "should not raise an error" do 110 | lambda{ Rubygame.open_audio(:frequency => 44100) }.should_not raise_error 111 | end 112 | end 113 | 114 | 115 | describe "Opening audio with a negative frequency" do 116 | after :each do 117 | Rubygame.close_audio 118 | end 119 | 120 | it "should raise ArgumentError" do 121 | lambda{ Rubygame.open_audio(:frequency => -1) }.should raise_error(ArgumentError) 122 | end 123 | end 124 | 125 | 126 | describe "Opening audio with a frequency of zero" do 127 | after :each do 128 | Rubygame.close_audio 129 | end 130 | 131 | it "should raise ArgumentError" do 132 | lambda{ Rubygame.open_audio(:frequency => 0) }.should raise_error(ArgumentError) 133 | end 134 | end 135 | 136 | 137 | 138 | ######################### 139 | ## ## 140 | ## CHANNELS ## 141 | ## ## 142 | ######################### 143 | 144 | 145 | describe "Opening audio with 1 channel (mono)" do 146 | after :each do 147 | Rubygame.close_audio 148 | end 149 | 150 | it "should not raise an error" do 151 | lambda{ Rubygame.open_audio(:channels => 1) }.should_not raise_error 152 | end 153 | end 154 | 155 | 156 | describe "Opening audio with 2 channels (stereo)" do 157 | after :each do 158 | Rubygame.close_audio 159 | end 160 | 161 | it "should not raise an error" do 162 | lambda{ Rubygame.open_audio(:channels => 2) }.should_not raise_error 163 | end 164 | end 165 | 166 | 167 | describe "Opening audio with a too-small number of channels" do 168 | after :each do 169 | Rubygame.close_audio 170 | end 171 | 172 | it "should raise ArgumentError" do 173 | lambda{ Rubygame.open_audio(:channels => 0) }.should raise_error(ArgumentError) 174 | end 175 | end 176 | 177 | 178 | describe "Opening audio with a too-large number of channels" do 179 | after :each do 180 | Rubygame.close_audio 181 | end 182 | 183 | it "should raise ArgumentError" do 184 | lambda{ Rubygame.open_audio(:channels => 3) }.should raise_error(ArgumentError) 185 | end 186 | end 187 | 188 | 189 | 190 | ######################### 191 | ## ## 192 | ## FREQUENCY ## 193 | ## ## 194 | ######################### 195 | 196 | 197 | describe "Opening audio with a valid buffer size" do 198 | after :each do 199 | Rubygame.close_audio 200 | end 201 | 202 | it "should not raise an error" do 203 | lambda{ Rubygame.open_audio(:buffer => 512) }.should_not raise_error 204 | end 205 | end 206 | 207 | 208 | describe "Opening audio with a non-power-of-two buffer size" do 209 | after :each do 210 | Rubygame.close_audio 211 | end 212 | 213 | it "should raise ArgumentError" do 214 | lambda{ Rubygame.open_audio(:buffer => 511) }.should raise_error(ArgumentError) 215 | end 216 | end 217 | 218 | 219 | describe "Opening audio with a negative buffer size" do 220 | after :each do 221 | Rubygame.close_audio 222 | end 223 | 224 | it "should raise ArgumentError" do 225 | lambda{ Rubygame.open_audio(:buffer => -1) }.should raise_error(ArgumentError) 226 | end 227 | end 228 | 229 | 230 | describe "Opening audio with a buffer of zero" do 231 | after :each do 232 | Rubygame.close_audio 233 | end 234 | 235 | it "should raise ArgumentError" do 236 | lambda{ Rubygame.open_audio(:buffer => 0) }.should raise_error(ArgumentError) 237 | end 238 | end 239 | -------------------------------------------------------------------------------- /spec/clock_spec.rb: -------------------------------------------------------------------------------- 1 | # This is mostly for regression testing and bugfix confirmation at the moment. 2 | 3 | 4 | require 'rubygame' 5 | include Rubygame 6 | include Rubygame::Events 7 | 8 | 9 | 10 | describe Clock do 11 | 12 | before :each do 13 | @clock = Clock.new 14 | end 15 | 16 | after :each do 17 | Rubygame.quit 18 | end 19 | 20 | 21 | it "should have a granularity accessor" do 22 | lambda{@clock.granularity = 5}.should_not raise_error 23 | end 24 | 25 | it "should have a nice accessor" do 26 | lambda{@clock.nice = true}.should_not raise_error 27 | end 28 | 29 | 30 | describe "with target" do 31 | 32 | it "should pass frametime, granularity, and nice to Clock.delay" do 33 | @clock.target_frametime = 1 34 | @clock.granularity = 2 35 | @clock.nice = true 36 | 37 | Clock.should_receive(:delay).with(1,2,true).and_return(1) 38 | @clock.tick 39 | end 40 | 41 | end 42 | 43 | 44 | describe "with tick events" do 45 | 46 | before :each do 47 | @clock.enable_tick_events 48 | end 49 | 50 | 51 | it "should return ClockTicked events" do 52 | @clock.tick.should be_instance_of(ClockTicked) 53 | end 54 | 55 | 56 | it "should cache ClockTicked events" do 57 | Clock.stub!(:delay).and_return(10) 58 | # Make sure they are the same exact object 59 | @clock.tick.should equal(@clock.tick) 60 | end 61 | 62 | end 63 | 64 | 65 | describe "without tick events" do 66 | 67 | it "should return integer ticks" do 68 | @clock.tick.should be_instance_of(Fixnum) 69 | end 70 | 71 | end 72 | 73 | end 74 | -------------------------------------------------------------------------------- /spec/event_actions_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | include Rubygame 5 | include Rubygame::EventActions 6 | 7 | 8 | 9 | shared_examples_for "an event action" do 10 | 11 | it "should have a #perform method" do 12 | @action.should respond_to(:perform) 13 | end 14 | 15 | it "should take 2 arguments to #perform" do 16 | @action.method(:perform).arity.should == 2 17 | end 18 | 19 | it "should raise error if #perform gets too few arguments" do 20 | lambda { @action.perform(1) }.should raise_error(ArgumentError) 21 | end 22 | 23 | it "should raise error if #perform gets too many arguments" do 24 | lambda { @action.perform(1,2,3) }.should raise_error(ArgumentError) 25 | end 26 | 27 | end 28 | 29 | 30 | 31 | 32 | describe BlockAction do 33 | 34 | before :each do 35 | @owner = mock("owner") 36 | @action = BlockAction.new { |owner,event| owner.foo(event) } 37 | end 38 | 39 | it_should_behave_like "an event action" 40 | 41 | it "should fail on creation if no block is given" do 42 | lambda { BlockAction.new }.should raise_error( ArgumentError ) 43 | end 44 | 45 | it "#perform should execute the block" do 46 | @owner.should_receive( :foo ).with( :event ) 47 | @action.perform( @owner, :event ) 48 | end 49 | 50 | it "should yield 2 parameters (owner and event) to the block" do 51 | @action = BlockAction.new { |owner, event| 52 | owner.should == :owner 53 | event.should == :event 54 | } 55 | @action.perform( :owner, :event ) 56 | end 57 | 58 | end 59 | 60 | 61 | 62 | 63 | describe MethodAction do 64 | 65 | before :each do 66 | @owner = mock("owner") 67 | @action = MethodAction.new( :foo ) 68 | end 69 | 70 | it_should_behave_like "an event action" 71 | 72 | it "should call the owner's method with the event" do 73 | @owner.should_receive( :foo ).with( :event ) 74 | @action.perform( @owner, :event ) 75 | end 76 | 77 | describe "with a method that takes no arg" do 78 | 79 | it "should retry with no args" do 80 | @owner.should_receive( :foo ).with( :event ).ordered.and_raise( ArgumentError ) 81 | @owner.should_receive( :foo ).with( no_args ).ordered 82 | @action.perform( @owner, :event ) 83 | end 84 | 85 | end 86 | 87 | end 88 | 89 | 90 | 91 | 92 | describe MultiAction do 93 | 94 | before :each do 95 | @owner = mock("owner") 96 | 97 | action1 = MethodAction.new( :foo ) 98 | action2 = BlockAction.new { |owner,event| owner.bar(event) } 99 | @action = MultiAction.new( action1, action2 ) 100 | end 101 | 102 | it_should_behave_like "an event action" 103 | 104 | it "#perform should perform all included actions in order" do 105 | @owner.should_receive( :foo ).with( :event ).ordered 106 | @owner.should_receive( :bar ).with( :event ).ordered 107 | @action.perform( @owner, :event ) 108 | end 109 | 110 | end 111 | -------------------------------------------------------------------------------- /spec/event_hook_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | include Rubygame 5 | 6 | 7 | 8 | describe "EventHook" do 9 | 10 | ############## 11 | # ATTRIBUTES # 12 | ############## 13 | 14 | [:owner, :trigger, :action, :consumes, :active].each do |a| 15 | 16 | it "should have an accessor for @#{a}" do 17 | EventHook.new.should respond_to(a) 18 | EventHook.new.should respond_to("#{a}=".intern) 19 | end 20 | 21 | it "should accept :#{a} at creation" do 22 | e = EventHook.new(a => :value) 23 | e.send(a).should == :value 24 | end 25 | 26 | # Default values for attributes. 27 | # owner, trigger, and action default to nil. 28 | defaults = {:consumes => false, :active => true} 29 | 30 | it "@#{a} should be #{defaults[a].inspect} by default" do 31 | EventHook.new.send(a).should == defaults[a] 32 | end 33 | 34 | it "should allow setting @#{a} post-creation" do 35 | e = EventHook.new 36 | e.send("#{a}=".intern, :value) 37 | e.send(a).should == :value 38 | end 39 | 40 | end 41 | 42 | 43 | ############ 44 | # MATCHING # 45 | ############ 46 | 47 | it "should have a #match? method" do 48 | EventHook.new.should respond_to(:match?) 49 | end 50 | 51 | it "#match? should take one event" do 52 | lambda { EventHook.new.match?( ) }.should raise_error 53 | lambda { EventHook.new.match?( 1 ) }.should_not raise_error 54 | lambda { EventHook.new.match?( 1, 2 ) }.should raise_error 55 | end 56 | 57 | it "should ask the trigger to see if an event matches" do 58 | trigger = mock("trigger") 59 | trigger.should_receive(:match?).with(:event) 60 | EventHook.new(:trigger => trigger).match?(:event) 61 | end 62 | 63 | it "should match if the event matches the trigger" do 64 | trigger = mock("trigger", :match? => true) 65 | EventHook.new(:trigger => trigger).match?(:event).should be_true 66 | end 67 | 68 | it "should not match if the event does not match the trigger" do 69 | trigger = mock("trigger", :match? => false) 70 | EventHook.new(:trigger => trigger).match?(:event).should be_false 71 | end 72 | 73 | it "should not match if there is no trigger" do 74 | EventHook.new.match?(:event).should be_false 75 | end 76 | 77 | it "should not match if the hook is not active" do 78 | trigger = mock("trigger", :match? => true) 79 | e = EventHook.new(:trigger => trigger, :active => false) 80 | e.match?(:event).should be_false 81 | end 82 | 83 | 84 | ############## 85 | # PERFORMING # 86 | ############## 87 | 88 | it "should have a #perform method" do 89 | EventHook.new.should respond_to(:perform) 90 | end 91 | 92 | it "#perform should take one event" do 93 | lambda { EventHook.new.perform( ) }.should raise_error 94 | lambda { EventHook.new.perform( 1 ) }.should_not raise_error 95 | lambda { EventHook.new.perform( 1, 2 ) }.should raise_error 96 | end 97 | 98 | it "should call the action's #perform with the owner and event" do 99 | action = mock("action") 100 | action.should_receive(:perform).with(:owner, :event) 101 | EventHook.new(:action => action, :owner => :owner).perform(:event) 102 | end 103 | 104 | end 105 | -------------------------------------------------------------------------------- /spec/event_queue_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | include Rubygame 5 | 6 | 7 | 8 | describe EventQueue do 9 | 10 | before :each do 11 | @queue = EventQueue.new 12 | end 13 | 14 | 15 | 16 | ############# 17 | # AUTOFETCH # 18 | ############# 19 | 20 | it "autofetch should be on by default" do 21 | @queue.autofetch.should be_true 22 | end 23 | 24 | it "should have autofetch read-write accessors" do 25 | @queue.should respond_to(:autofetch) 26 | @queue.should respond_to(:autofetch=) 27 | end 28 | 29 | 30 | 31 | ######## 32 | # EACH # 33 | ######## 34 | 35 | it "#each should yield each event in the queue in order" do 36 | collect = [] 37 | @queue.push( [1,2,3] ) 38 | @queue.each { |e| collect << e} 39 | collect.should == [1,2,3] 40 | end 41 | 42 | it "#each should flush the buffer afterwards" do 43 | @queue.push( [1,2,3] ) 44 | @queue.each {} 45 | @queue.should be_empty 46 | end 47 | 48 | it "#each should fetch SDL events if autofetch is on" do 49 | @queue.autofetch = true 50 | 51 | @queue.should_receive(:fetch_sdl_events).and_return([:foo]) 52 | @queue.each {} 53 | end 54 | 55 | it "#each should not fetch SDL events if autofetch is off" do 56 | @queue.autofetch = false 57 | 58 | @queue.should_not_receive(:fetch_sdl_events) 59 | @queue.each {} 60 | end 61 | 62 | 63 | 64 | ############# 65 | # PEEK EACH # 66 | ############# 67 | 68 | it "#peek_each should yield each event in the queue in order" do 69 | collect = [] 70 | @queue.push( [1,2,3] ) 71 | @queue.peek_each { |e| collect << e} 72 | collect.should == [1,2,3] 73 | end 74 | 75 | it "#peek_each should not flush the buffer afterwards" do 76 | @queue.push( [1,2,3] ) 77 | @queue.peek_each {} 78 | @queue.should_not be_empty 79 | end 80 | 81 | it "#peek_each should fetch SDL events if autofetch is on" do 82 | @queue.autofetch = true 83 | 84 | @queue.should_receive(:fetch_sdl_events).and_return([:foo]) 85 | @queue.peek_each {} 86 | end 87 | 88 | it "#peek_each should not fetch SDL events if autofetch is off" do 89 | @queue.autofetch = false 90 | 91 | @queue.should_not_receive(:fetch_sdl_events) 92 | @queue.peek_each {} 93 | end 94 | 95 | 96 | 97 | #################### 98 | # FETCH SDL EVENTS # 99 | #################### 100 | 101 | it "should fetch SDL events from the old place by default" do 102 | Rubygame.should_receive(:fetch_sdl_events).and_return([]) 103 | @queue.fetch_sdl_events 104 | end 105 | 106 | it "should fetch SDL events from the new place if enabled" do 107 | Rubygame::Events.should_receive(:fetch_sdl_events).and_return([]) 108 | @queue.enable_new_style_events 109 | @queue.fetch_sdl_events 110 | end 111 | 112 | 113 | 114 | 115 | ########## 116 | # IGNORE # 117 | ########## 118 | 119 | it "should provide @ignore read-write accessors" do 120 | @queue.should respond_to(:ignore) 121 | @queue.should respond_to(:ignore=) 122 | end 123 | 124 | it "should silently reject pushed objects whose class is ignored" do 125 | @queue.ignore << Fixnum 126 | @queue.push( :foo, 3, :baz ) 127 | @queue.to_ary.should == [:foo, :baz] 128 | end 129 | 130 | 131 | ############### 132 | # PUSH / POST # 133 | ############### 134 | 135 | it "should accept pushes" do 136 | @queue.push( :foo ) 137 | @queue.to_ary.should == [:foo] 138 | end 139 | 140 | it "should accept multiple pushes at once" do 141 | @queue.push( :foo, :bar, :baz ) 142 | @queue.to_ary.should == [:foo, :bar, :baz] 143 | end 144 | 145 | it "should accept a pushed array" do 146 | @queue.push( [:foo, :bar] ) 147 | @queue.to_ary.should == [:foo, :bar] 148 | end 149 | 150 | it "should accept posts" do 151 | @queue.post( :foo ) 152 | @queue.to_ary.should == [:foo] 153 | end 154 | 155 | it "should accept multiple posts at once" do 156 | @queue.post( :foo, :bar, :baz ) 157 | @queue.to_ary.should == [:foo, :bar, :baz] 158 | end 159 | 160 | it "should accept a posted array" do 161 | @queue.post( [:foo, :bar] ) 162 | @queue.to_ary.should == [:foo, :bar] 163 | end 164 | 165 | it "should accept <<" do 166 | @queue << :foo 167 | @queue.to_ary.should == [:foo] 168 | end 169 | 170 | end 171 | -------------------------------------------------------------------------------- /spec/ftor_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame/ftor' 4 | include Rubygame 5 | 6 | 7 | 8 | describe Ftor do 9 | 10 | describe ".new_from_to" do 11 | it "should accept two Ftors" do 12 | lambda { 13 | Ftor.new_from_to( Ftor.new(0,0), Ftor.new(2,0) ) 14 | }.should_not raise_error 15 | end 16 | 17 | it "should accept two Arrays" do 18 | lambda { 19 | Ftor.new_from_to( [0,0], [2,0] ) 20 | }.should_not raise_error 21 | end 22 | 23 | it "should create an Ftor connecting the points" do 24 | f = Ftor.new_from_to([2,-4],[10,3]) 25 | f.x.should == 8 26 | f.y.should == 7 27 | end 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /spec/has_event_handler_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | include Rubygame 5 | include Rubygame::EventTriggers 6 | include Rubygame::EventActions 7 | 8 | 9 | 10 | HasEventHandler = Rubygame::EventHandler::HasEventHandler 11 | 12 | class HandledObject 13 | include HasEventHandler 14 | end 15 | 16 | 17 | describe HasEventHandler do 18 | 19 | before :each do 20 | @object = HandledObject.new 21 | @results = [] 22 | end 23 | 24 | ############### 25 | # MAGIC HOOKS # 26 | ############### 27 | 28 | it "should have a #make_magic_hooks method" do 29 | @object.should respond_to(:make_magic_hooks) 30 | end 31 | 32 | describe "#make_magic_hooks" do 33 | 34 | it "should accept a hash" do 35 | lambda { @object.make_magic_hooks({}) }.should_not raise_error 36 | end 37 | 38 | it "should reject non-hashes" do 39 | lambda { @object.make_magic_hooks(EventHook.new) }.should raise_error 40 | lambda { @object.make_magic_hooks("string") }.should raise_error 41 | end 42 | 43 | it "should accept a valid hook hash" do 44 | lambda { 45 | @object.make_magic_hooks( { :up => :foo } ) 46 | }.should_not raise_error 47 | end 48 | 49 | it "should accept a hook hash with multiple pairs" do 50 | lambda { 51 | @object.make_magic_hooks( { :up => :foo, 52 | :down => :bar, 53 | :mouse_left => :shoot } ) 54 | }.should_not raise_error 55 | end 56 | 57 | it "should return an Array of EventHook instances" do 58 | hooks = @object.make_magic_hooks( { :up => :foo, 59 | :down => :bar, 60 | :mouse_left => :shoot } ) 61 | 62 | hooks.should be_instance_of(Array) 63 | hooks.each { |hook| hook.should be_instance_of(EventHook) } 64 | end 65 | 66 | ############ 67 | # TRIGGERS # 68 | ############ 69 | 70 | it "should accept :mouse_* symbol triggers" do 71 | lambda { 72 | @object.make_magic_hooks( { :mouse_left => :foo } ) 73 | @object.make_magic_hooks( { :mouse_right => :foo } ) 74 | @object.make_magic_hooks( { :mouse_middle => :foo } ) 75 | @object.make_magic_hooks( { :mouse_wheel_up => :foo } ) 76 | @object.make_magic_hooks( { :mouse_wheel_down => :foo } ) 77 | }.should_not raise_error 78 | end 79 | 80 | it "should turn :mouse_* symbols into MousePressTriggers" do 81 | hooks = @object.make_magic_hooks( {:mouse_left => :foo }) 82 | hooks[0].trigger.should be_instance_of(MousePressTrigger) 83 | end 84 | 85 | it "should accept keyboard symbol triggers" do 86 | lambda { 87 | @object.make_magic_hooks( { :a => :foo } ) 88 | @object.make_magic_hooks( { :up => :foo } ) 89 | @object.make_magic_hooks( { :space => :foo } ) 90 | @object.make_magic_hooks( { :number1 => :foo } ) 91 | }.should_not raise_error 92 | end 93 | 94 | it "should turn keyboard symbols into KeyPressTriggers" do 95 | hooks = @object.make_magic_hooks( {:a => :foo }) 96 | hooks[0].trigger.should be_instance_of(KeyPressTrigger) 97 | end 98 | 99 | it "should accept classes as triggers" do 100 | lambda { 101 | @object.make_magic_hooks( { Object => :foo } ) 102 | @object.make_magic_hooks( { Events::MousePressed => :foo } ) 103 | @object.make_magic_hooks( { Events::WindowResized => :foo } ) 104 | }.should_not raise_error 105 | end 106 | 107 | it "should turn classes into InstanceOfTriggers" do 108 | hooks = @object.make_magic_hooks( {Object => :foo }) 109 | hooks[0].trigger.should be_instance_of(InstanceOfTrigger) 110 | end 111 | 112 | it "should turn :tick into TickTrigger" do 113 | hooks = @object.make_magic_hooks( {:tick => :foo }) 114 | hooks[0].trigger.should be_instance_of(TickTrigger) 115 | end 116 | 117 | it "should accept objects with #match? as triggers" do 118 | fake_trigger = Object.new 119 | class << fake_trigger; def match?; end; end 120 | 121 | lambda { 122 | @object.make_magic_hooks( { fake_trigger => :foo } ) 123 | }.should_not raise_error 124 | end 125 | 126 | it "should use a dup of #match? triggers" do 127 | class FakeTrigger; def match?; end; end 128 | fake_trigger = FakeTrigger.new 129 | 130 | hooks = @object.make_magic_hooks( { fake_trigger => :foo } ) 131 | 132 | hooks[0].trigger.should be_instance_of(FakeTrigger) 133 | hooks[0].trigger.should_not eql(fake_trigger) 134 | end 135 | 136 | it "should not accept invalid triggers" do 137 | lambda { 138 | @object.make_magic_hooks( { Object.new => :foo } ) 139 | }.should raise_error(ArgumentError) 140 | 141 | lambda { 142 | @object.make_magic_hooks( { "string" => :foo } ) 143 | }.should raise_error(ArgumentError) 144 | 145 | lambda { 146 | @object.make_magic_hooks( { 1 => :foo } ) 147 | }.should raise_error(ArgumentError) 148 | end 149 | 150 | 151 | ########### 152 | # ACTIONS # 153 | ########### 154 | 155 | it "should accept method name symbol actions" do 156 | lambda { 157 | @object.make_magic_hooks( { :up => :foo } ) 158 | }.should_not raise_error 159 | end 160 | 161 | it "should turn method names into MethodActions" do 162 | hooks = @object.make_magic_hooks( { :up => :foo } ) 163 | hooks[0].action.should be_instance_of(MethodAction) 164 | end 165 | 166 | it "should accept Proc actions" do 167 | lambda { 168 | @object.make_magic_hooks( { :up => Proc.new { |o,e| :foo } } ) 169 | }.should_not raise_error 170 | end 171 | 172 | it "should turn Procs into BlockActions" do 173 | hooks = @object.make_magic_hooks( { :up => Proc.new { |o,e| :foo } } ) 174 | hooks[0].action.should be_instance_of(BlockAction) 175 | end 176 | 177 | it "should accept detached method actions" do 178 | lambda { 179 | @object.make_magic_hooks( { :up => Object.new.method(:to_s) } ) 180 | }.should_not raise_error 181 | end 182 | 183 | it "should turn detached ethods into BlockActions" do 184 | hooks = @object.make_magic_hooks( { :up => Object.new.method(:to_s) } ) 185 | hooks[0].action.should be_instance_of(BlockAction) 186 | end 187 | 188 | it "should accept objects with #perform as actions" do 189 | fake_action = Object.new 190 | class << fake_action; def perform; end; end 191 | 192 | lambda { 193 | @object.make_magic_hooks( { :up => fake_action } ) 194 | }.should_not raise_error 195 | end 196 | 197 | it "should use a dup of #perform actions" do 198 | class FakeAction; def perform; end; end 199 | fake_action = FakeAction.new 200 | 201 | hooks = @object.make_magic_hooks( { :up => fake_action } ) 202 | 203 | hooks[0].action.should be_instance_of(FakeAction) 204 | hooks[0].action.should_not eql(fake_action) 205 | end 206 | 207 | it "should not accept invalid actions" do 208 | lambda { 209 | @object.make_magic_hooks( { :up => Object.new } ) 210 | }.should raise_error(ArgumentError) 211 | 212 | lambda { 213 | @object.make_magic_hooks( { :up => "string" } ) 214 | }.should raise_error(ArgumentError) 215 | 216 | lambda { 217 | @object.make_magic_hooks( { :up => 1 } ) 218 | }.should raise_error(ArgumentError) 219 | end 220 | 221 | end 222 | 223 | 224 | describe "#make_magic_hooks_for" do 225 | 226 | it "should use the given owner" do 227 | hooks = @object.make_magic_hooks_for( :owner, {:a => :foo } ) 228 | hooks[0].owner.should == :owner 229 | end 230 | 231 | end 232 | 233 | 234 | # Regression test 235 | it "#handle should not eat NoMethodErrors" do 236 | @object.make_magic_hooks( :a => proc{ bad_method_call() } ) 237 | lambda { 238 | @object.handle( Events::KeyPressed.new(:a) ) 239 | }.should raise_error(NoMethodError) 240 | end 241 | 242 | end 243 | -------------------------------------------------------------------------------- /spec/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/spec/image.png -------------------------------------------------------------------------------- /spec/image_8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/spec/image_8bit.png -------------------------------------------------------------------------------- /spec/keyboard_events_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | include Rubygame::Events 5 | 6 | 7 | 8 | shared_examples_for "a keyboard event" do 9 | 10 | it "should have a key symbol" do 11 | @event.key.should == :a 12 | end 13 | 14 | it "should complain if key symbol is not a symbol" do 15 | lambda { @event.class.new( 4 ) }.should raise_error(ArgumentError) 16 | end 17 | 18 | it "should have an array of modifiers" do 19 | @event.modifiers.should == [:shift] 20 | end 21 | 22 | it "should complain if modifiers is not Array-like" do 23 | lambda { @event.class.new( :a, 4 ) }.should raise_error 24 | end 25 | 26 | it "modifiers should be frozen" do 27 | @event.modifiers.should be_frozen 28 | end 29 | 30 | it "should not freeze the original modifiers Array" do 31 | mods = [:shift] 32 | @event.class.new( :a, mods ) 33 | mods.should_not be_frozen 34 | end 35 | 36 | end 37 | 38 | 39 | 40 | describe KeyPressed do 41 | 42 | before :each do 43 | @event = KeyPressed.new( :a, [:shift], "A" ) 44 | end 45 | 46 | it_should_behave_like "a keyboard event" 47 | 48 | it "should have a string" do 49 | @event.string.should == "A" 50 | end 51 | 52 | it "should complain if string is not String-like" do 53 | lambda {@event.class.new( :a, [:shift], 4 )}.should raise_error 54 | end 55 | 56 | it "string should be frozen" do 57 | @event.string.should be_frozen 58 | end 59 | 60 | end 61 | 62 | 63 | describe KeyReleased do 64 | 65 | before :each do 66 | @event = KeyReleased.new( :a, [:shift] ) 67 | end 68 | 69 | it_should_behave_like "a keyboard event" 70 | 71 | end 72 | 73 | 74 | 75 | describe "Rubygame.pressed_keys" do 76 | 77 | # Helper method to make an Array like would be returned from 78 | # SDL.GetKeyState. If you pass SDL::K_* constants to this method, 79 | # they will be marked as "pressed" in the array. 80 | # 81 | def mock_keys( *pressed ) 82 | keys = Array.new(SDL::K_LAST, 0) 83 | pressed.each{ |key| keys[key] = 1 } 84 | keys 85 | end 86 | 87 | it "should query SDL.GetKeyState" do 88 | SDL.should_receive(:GetKeyState).and_return( mock_keys() ) 89 | Rubygame.pressed_keys 90 | end 91 | 92 | it "should return an empty hash when no keys are pressed" do 93 | SDL.stub(:GetKeyState){ mock_keys() } 94 | Rubygame.pressed_keys.should == {} 95 | end 96 | 97 | it "should have a pair for every pressed key" do 98 | SDL.stub(:GetKeyState){ mock_keys( SDL::K_a, SDL::K_LCTRL ) } 99 | keys = Rubygame.pressed_keys 100 | keys.should have_key( :a ) 101 | keys.should have_key( :left_ctrl ) 102 | end 103 | 104 | it "should have a value of true for every pressed key" do 105 | SDL.stub(:GetKeyState){ mock_keys( SDL::K_a, SDL::K_LCTRL ) } 106 | keys = Rubygame.pressed_keys 107 | keys[:a].should be_true 108 | keys[:left_ctrl].should be_true 109 | end 110 | 111 | it "should have a falsey value for non-pressed keys" do 112 | SDL.stub(:GetKeyState){ mock_keys( SDL::K_a, SDL::K_LCTRL ) } 113 | keys = Rubygame.pressed_keys 114 | keys[:b].should satisfy{ |v| not v } 115 | keys[:left_shift].should satisfy{ |v| not v } 116 | end 117 | 118 | end 119 | -------------------------------------------------------------------------------- /spec/misc_events_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | include Rubygame::Events 5 | 6 | 7 | 8 | shared_examples_for "a simple event" do 9 | 10 | it "should take zero arguments at creation" do 11 | lambda { @class.new }.should_not raise_error 12 | lambda { @class.new(:foo) }.should raise_error 13 | end 14 | 15 | end 16 | 17 | 18 | 19 | describe InputFocusGained do 20 | before :each do 21 | @class = InputFocusGained 22 | end 23 | 24 | it_should_behave_like "a simple event" 25 | end 26 | 27 | describe InputFocusLost do 28 | before :each do 29 | @class = InputFocusLost 30 | end 31 | 32 | it_should_behave_like "a simple event" 33 | end 34 | 35 | 36 | 37 | describe MouseFocusGained do 38 | before :each do 39 | @class = MouseFocusGained 40 | end 41 | 42 | it_should_behave_like "a simple event" 43 | end 44 | 45 | describe MouseFocusLost do 46 | before :each do 47 | @class = MouseFocusLost 48 | end 49 | 50 | it_should_behave_like "a simple event" 51 | end 52 | 53 | 54 | 55 | describe WindowMinimized do 56 | before :each do 57 | @class = WindowMinimized 58 | end 59 | 60 | it_should_behave_like "a simple event" 61 | end 62 | 63 | describe WindowUnminimized do 64 | before :each do 65 | @class = WindowUnminimized 66 | end 67 | 68 | it_should_behave_like "a simple event" 69 | end 70 | 71 | 72 | 73 | describe WindowExposed do 74 | before :each do 75 | @class = WindowExposed 76 | end 77 | 78 | it_should_behave_like "a simple event" 79 | end 80 | 81 | 82 | 83 | describe QuitRequested do 84 | before :each do 85 | @class = QuitRequested 86 | end 87 | 88 | it_should_behave_like "a simple event" 89 | end 90 | 91 | 92 | 93 | 94 | describe WindowResized do 95 | 96 | it "should have a size" do 97 | WindowResized.new([20,20]).should respond_to(:size) 98 | end 99 | 100 | it "should accept an [x,y] Array as size" do 101 | lambda { WindowResized.new([20,20]) }.should_not raise_error(ArgumentError) 102 | end 103 | 104 | it "should reject negative sizes" do 105 | lambda { WindowResized.new([-20, 20]) }.should raise_error(ArgumentError) 106 | lambda { WindowResized.new([ 20,-20]) }.should raise_error(ArgumentError) 107 | lambda { WindowResized.new([-20,-20]) }.should raise_error(ArgumentError) 108 | end 109 | 110 | it "should reject size zero" do 111 | lambda { WindowResized.new([ 0, 20]) }.should raise_error(ArgumentError) 112 | lambda { WindowResized.new([ 20, 0]) }.should raise_error(ArgumentError) 113 | lambda { WindowResized.new([ 0, 0]) }.should raise_error(ArgumentError) 114 | end 115 | 116 | it "should reject non-Array-like objects as size" do 117 | lambda { WindowResized.new( 20 ) }.should raise_error(NoMethodError) 118 | lambda { WindowResized.new( :foo ) }.should raise_error(NoMethodError) 119 | lambda { WindowResized.new( "blue" ) }.should raise_error(NoMethodError) 120 | end 121 | 122 | it "should reject sizes with wrong number of elements" do 123 | lambda { WindowResized.new([ ]) }.should raise_error(ArgumentError) 124 | lambda { WindowResized.new([ 20 ]) }.should raise_error(ArgumentError) 125 | lambda { WindowResized.new([ 20,20,20 ]) }.should raise_error(ArgumentError) 126 | end 127 | 128 | it "size should be read-only" do 129 | WindowResized.new([20,20]).should_not respond_to(:size=) 130 | end 131 | 132 | it "size should be frozen" do 133 | WindowResized.new([20,20]).size.should be_frozen 134 | end 135 | 136 | it "should not freeze the original Array passed as size" do 137 | a = [20,20] 138 | WindowResized.new(a) 139 | a.should_not be_frozen 140 | end 141 | 142 | end 143 | -------------------------------------------------------------------------------- /spec/mouse_events_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame' 4 | include Rubygame::Events 5 | 6 | 7 | 8 | shared_examples_for "a mouse button event" do 9 | 10 | it "should have a button symbol" do 11 | @event.button.should == :mouse_left 12 | end 13 | 14 | it "should complain if button symbol is not a symbol" do 15 | lambda { @event.class.new(4) }.should raise_error(ArgumentError) 16 | end 17 | 18 | it "should complain if button symbol is omitted" do 19 | lambda { @event.class.new }.should raise_error(ArgumentError) 20 | end 21 | 22 | it "button symbol should be read-only" do 23 | @event.should_not respond_to(:button=) 24 | end 25 | 26 | 27 | 28 | it "should have a position" do 29 | @event.pos.should == [1,2] 30 | end 31 | 32 | it "should complain if position isn't Array-like" do 33 | lambda { @event.class.new(4,:mouse_left) }.should raise_error 34 | end 35 | 36 | it "should complain if position is omitted" do 37 | lambda { @event.class.new(:mouse_left) }.should raise_error(ArgumentError) 38 | end 39 | 40 | it "position should be read-only" do 41 | @event.should_not respond_to(:pos=) 42 | end 43 | 44 | it "position should be frozen" do 45 | @event.pos.should be_frozen 46 | end 47 | 48 | it "should not freeze the original position Array" do 49 | a = [0,0] 50 | @event.class.new(a, :mouse_left) 51 | a.should_not be_frozen 52 | end 53 | 54 | end 55 | 56 | 57 | 58 | describe MousePressed do 59 | 60 | before :each do 61 | @event = MousePressed.new( [1,2], :mouse_left ) 62 | end 63 | 64 | it_should_behave_like "a mouse button event" 65 | 66 | end 67 | 68 | 69 | 70 | describe MouseReleased do 71 | 72 | before :each do 73 | @event = MouseReleased.new( [1,2], :mouse_left ) 74 | end 75 | 76 | it_should_behave_like "a mouse button event" 77 | 78 | end 79 | 80 | 81 | 82 | describe MouseMoved do 83 | 84 | before :each do 85 | @event = MouseMoved.new( [1,2], [3,4], [:mouse_left] ) 86 | end 87 | 88 | it "should have a position" do 89 | @event.pos.should == [1,2] 90 | end 91 | 92 | it "should complain if position is not Array-like" do 93 | lambda { @event.class.new( 4, [3,4] ) }.should raise_error 94 | end 95 | 96 | it "should complain if position is omitted" do 97 | lambda { @event.class.new() }.should raise_error(ArgumentError) 98 | end 99 | 100 | it "position should be frozen" do 101 | @event.pos.should be_frozen 102 | end 103 | 104 | it "should not freeze the original position Array" do 105 | a = [0,0] 106 | @event.class.new(a, [3,4]) 107 | a.should_not be_frozen 108 | end 109 | 110 | 111 | 112 | it "should have a relative movement" do 113 | @event.rel.should == [3,4] 114 | end 115 | 116 | it "should complain if relative movement is not Array-like" do 117 | lambda { @event.class.new( [1,2], 4 ) }.should raise_error 118 | end 119 | 120 | it "should complain if relative movement is omitted" do 121 | lambda { @event.class.new( [1,2] ) }.should raise_error(ArgumentError) 122 | end 123 | 124 | it "relative movement should be frozen" do 125 | @event.rel.should be_frozen 126 | end 127 | 128 | it "should not freeze the original relative movement Array" do 129 | a = [0,0] 130 | @event.class.new([1,2], a) 131 | a.should_not be_frozen 132 | end 133 | 134 | 135 | 136 | it "should have an array of held buttons" do 137 | @event.buttons.should == [:mouse_left] 138 | end 139 | 140 | it "should complain if held buttons is not Array-like" do 141 | lambda { @event.class.new( [1,2], [3,4], 4 ) }.should raise_error 142 | end 143 | 144 | it "should have no held buttons if omitted" do 145 | @event = MouseMoved.new( [1,2], [3,4] ) 146 | @event.buttons.should == [] 147 | end 148 | 149 | it "buttons should be frozen" do 150 | @event.buttons.should be_frozen 151 | end 152 | 153 | it "should not freeze the original buttons Array" do 154 | a = [:mouse_left] 155 | @event.class.new([1,2], [3,4], a) 156 | a.should_not be_frozen 157 | end 158 | 159 | end 160 | -------------------------------------------------------------------------------- /spec/named_resource_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'rubygame/named_resource' 4 | include Rubygame 5 | 6 | TEST_DIR = File.dirname(__FILE__) 7 | 8 | 9 | 10 | describe NamedResource do 11 | 12 | 13 | describe "instance names" do 14 | before :each do 15 | @class = Class.new { 16 | include NamedResource 17 | 18 | def initialize( name=nil ) 19 | self.name = name 20 | end 21 | 22 | def to_s 23 | "#" 24 | end 25 | alias :inspect :to_s 26 | } 27 | 28 | @resource = @class.new( "my_name" ) 29 | end 30 | 31 | it "should have a name" do 32 | @resource.name.should == "my_name" 33 | end 34 | 35 | it "name string should be frozen" do 36 | @resource.name.should be_frozen 37 | end 38 | 39 | it "should be able to set name" do 40 | @resource.name = "new_name" 41 | @resource.name.should == "new_name" 42 | end 43 | 44 | it "should reject non-string names" do 45 | lambda { @resource.name = ["foo"] }.should raise_error(TypeError) 46 | end 47 | end 48 | 49 | 50 | describe "resource table" do 51 | 52 | describe "general" do 53 | before :each do 54 | @class = Class.new { 55 | include NamedResource 56 | 57 | class << self 58 | attr_accessor :resources 59 | end 60 | 61 | def to_s 62 | "#" 63 | end 64 | alias :inspect :to_s 65 | } 66 | 67 | @instance = @class.new 68 | @class.resources["foo"] = @instance 69 | end 70 | 71 | it "should be able to get basename a name from a filename" do 72 | @class.basename("/foo/bar/baz.ext").should == "baz.ext" 73 | end 74 | 75 | it "should return object with registered name" do 76 | @class["foo"].should == @instance 77 | end 78 | 79 | it "should be able to register an object by name" do 80 | @class["bar"] = @instance 81 | @class["bar"].should == @instance 82 | end 83 | 84 | it "should only allow registering instances of that class" do 85 | lambda { @class["bar"] = 3 }.should raise_error(TypeError) 86 | end 87 | end 88 | 89 | 90 | describe "without autoload" do 91 | before :each do 92 | @class = Class.new { 93 | include NamedResource 94 | 95 | class << self 96 | attr_accessor :resources 97 | end 98 | 99 | def to_s 100 | "#" 101 | end 102 | alias :inspect :to_s 103 | } 104 | end 105 | 106 | it "should return nil for unregistered names" do 107 | @class["unassigned"].should be_nil 108 | end 109 | end 110 | 111 | 112 | describe "with autoload" do 113 | before :each do 114 | @class = Class.new { 115 | include NamedResource 116 | 117 | class << self 118 | attr_accessor :resources 119 | 120 | def autoload( name ) 121 | instance = self.new 122 | return instance 123 | end 124 | end 125 | 126 | attr_accessor :name 127 | 128 | def to_s 129 | "#" 130 | end 131 | alias :inspect :to_s 132 | } 133 | end 134 | 135 | it "should autoload unregistered names" do 136 | instance = @class["bar"] 137 | instance.name.should == "bar" 138 | instance.should be_instance_of(@class) 139 | end 140 | 141 | it "should save autoloaded instances" do 142 | @class.resources["bar"].should be_nil 143 | instance = @class["bar"] 144 | @class.resources["bar"].should == instance 145 | end 146 | 147 | it "should set the name of autoloaded instances" do 148 | @class["bar"].name.should == "bar" 149 | end 150 | end 151 | 152 | 153 | describe "autoload paths" do 154 | before :each do 155 | @class = Class.new { 156 | include NamedResource 157 | 158 | class << self 159 | attr_accessor :resources 160 | 161 | def autoload( name ) 162 | path = find_file( name ) 163 | if path 164 | instance = self.new 165 | instance.path = path 166 | return instance 167 | end 168 | end 169 | 170 | # Fake check to see if file exists 171 | def exist?( path ) 172 | if( path == File.join("foo","bar") or 173 | path == File.join("moo","bar") ) 174 | true 175 | else 176 | false 177 | end 178 | end 179 | end 180 | 181 | attr_accessor :name, :path 182 | 183 | def to_s 184 | "#" 185 | end 186 | alias :inspect :to_s 187 | } 188 | 189 | @some_dirs = [ "hoo", "woo", "foo", "moo" ] 190 | end 191 | 192 | it "should have an accessor for @autoload_paths" do 193 | @class.autoload_dirs.should == [] 194 | @class.autoload_dirs.push("foo") 195 | @class.autoload_dirs.should == ["foo"] 196 | end 197 | 198 | it "should use the first path which succeeds" do 199 | @class.autoload_dirs = @some_dirs 200 | @class["bar"].path.should == File.join("foo","bar") 201 | end 202 | 203 | it "should return nil if no path succeeds" do 204 | @class.autoload_dirs = @some_dirs 205 | @class["nothing"].should be_nil 206 | end 207 | 208 | end 209 | 210 | end 211 | end 212 | -------------------------------------------------------------------------------- /spec/short.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacius/rubygame/9d0d5dca83bb4427386cb1d70da3f8bdf26e9e19/spec/short.ogg -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | # Prefer to load from ../lib before the system paths. 3 | $:.unshift File.expand_path(File.join(File.dirname(__FILE__),"..","lib")) 4 | 5 | ENV["RUBYGAME_DEPRECATED"] = "quiet" 6 | --------------------------------------------------------------------------------