├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.rdoc ├── Rakefile ├── benchmarks ├── README.txt ├── array_vs_hash.rb ├── arrays_bench.rb ├── benchmark.rb ├── benchmark3.rb ├── benchmark4.rb ├── benchmark5.rb ├── benchmark6.rb ├── benchmark_ping_localhost.rb ├── game_object_list_benchmark.rb ├── game_objects_benchmark.rb ├── lookup_benchmark.rb ├── meta_benchmark.rb ├── meta_benchmark2.rb ├── trait_benchmark.rb └── window_benchmark.rb ├── chingu.gemspec ├── examples ├── example10_traits_retrofy.rb ├── example11_animation.rb ├── example12_trait_timer.rb ├── example13_high_scores.rb ├── example14_bounding_box_circle.rb ├── example15_trait_timer2.rb ├── example16_online_high_scores.rb ├── example17_gosu_tutorial.rb ├── example18_animation_trait.rb ├── example19.yml ├── example19_edit_viewport.rb ├── example1_basics.rb ├── example20_trait_inheritence_test.rb ├── example21.yml ├── example21_sidescroller_with_edit.rb ├── example22_text.rb ├── example23_chipmunk.rb ├── example24_enter_name.rb ├── example25.yml ├── example25_fibers_state_machine.rb ├── example26_splash_screen.rb ├── example27_console.rb ├── example28_networking.rb ├── example29_asynchronous.rb ├── example2_gamestate_basics.rb ├── example3_parallax.rb ├── example4_gamestates.rb ├── example5_gamestates_in_pure_gosu.rb ├── example6_transitional_game_state.rb ├── example7_gfx_helpers.rb ├── example8_traits.rb ├── example9_collision_detection.rb ├── game1.rb ├── game_of_life.rb ├── high_score_list.yml ├── media │ ├── Parallax-scroll-example-layer-0.png │ ├── Parallax-scroll-example-layer-1.png │ ├── Parallax-scroll-example-layer-2.png │ ├── Parallax-scroll-example-layer-3.png │ ├── Star.png │ ├── Starfighter.bmp │ ├── background1.png │ ├── battery.png │ ├── big_star.png │ ├── big_stone_wall.bmp │ ├── black_block.png │ ├── bullet.png │ ├── bullet_hit.wav │ ├── circle.png │ ├── city1.png │ ├── city2.png │ ├── city3.png │ ├── cog_wheel.png │ ├── droid.bmp │ ├── droid_11x15.bmp │ ├── droid_11x15.gal │ ├── enemy_bullet.png │ ├── enemy_plane.png │ ├── explosion.wav │ ├── fire_bullet.png │ ├── fireball.png │ ├── heli.bmp │ ├── heli.gal │ ├── laser.wav │ ├── particle.png │ ├── plane.png │ ├── rect.png │ ├── ruby.png │ ├── saucer.gal │ ├── saucer.png │ ├── saw.png │ ├── spaceship.png │ ├── star_25x25_default.png │ ├── star_25x25_explode.gal │ ├── star_25x25_explode.png │ ├── stone_wall.bmp │ ├── talk_bubble.png │ ├── tube.png │ ├── video_games.png │ └── wood.png └── tests │ ├── holding_a_test.rb │ └── tool1_input_codes.rb ├── lib ├── chingu.rb └── chingu │ ├── animation.rb │ ├── assets.rb │ ├── async │ ├── basic_task.rb │ ├── task_builder.rb │ └── task_list.rb │ ├── async_tasks │ ├── call.rb │ ├── exec.rb │ ├── move.rb │ ├── parallel.rb │ ├── tween.rb │ └── wait.rb │ ├── basic_game_object.rb │ ├── classic_game_object.rb │ ├── console.rb │ ├── core_ext │ ├── array.rb │ └── range.rb │ ├── fpscounter.rb │ ├── game_object.rb │ ├── game_object_list.rb │ ├── game_object_map.rb │ ├── game_state.rb │ ├── game_state_manager.rb │ ├── game_states │ ├── debug.rb │ ├── edit.rb │ ├── enter_name.rb │ ├── fade_to.rb │ ├── network_client.rb │ ├── network_server.rb │ ├── network_state.rb │ ├── pause.rb │ └── popup.rb │ ├── gosu_ext │ ├── image.rb │ ├── sample.rb │ └── song.rb │ ├── helpers │ ├── class_inheritable_accessor.rb │ ├── fps_counter.rb │ ├── game_object.rb │ ├── game_state.rb │ ├── gfx.rb │ ├── input_client.rb │ ├── input_dispatcher.rb │ ├── options_setter.rb │ └── rotation_center.rb │ ├── high_score_list.rb │ ├── inflector.rb │ ├── input.rb │ ├── named_resource.rb │ ├── online_high_score_list.rb │ ├── parallax.rb │ ├── particle.rb │ ├── rect.rb │ ├── require_all.rb │ ├── simple_menu.rb │ ├── text.rb │ ├── traits │ ├── animation.rb │ ├── asynchronous.rb │ ├── bounding_box.rb │ ├── bounding_circle.rb │ ├── collision_detection.rb │ ├── effect.rb │ ├── retrofy.rb │ ├── simple_sprite.rb │ ├── sprite.rb │ ├── timer.rb │ ├── velocity.rb │ └── viewport.rb │ ├── version.rb │ ├── viewport.rb │ └── window.rb ├── spec ├── chingu │ ├── animation_spec.rb │ ├── assets_spec.rb │ ├── basic_game_object_spec.rb │ ├── console_spec.rb │ ├── fpscounter_spec.rb │ ├── game_object_list_spec.rb │ ├── game_object_map_spec.rb │ ├── game_object_spec.rb │ ├── game_state_manager_spec.rb │ ├── helpers │ │ ├── input_client_spec.rb │ │ ├── input_dispatcher_spec.rb │ │ └── options_setter_spec.rb │ ├── images │ │ ├── droid_11x15.bmp │ │ └── rect_20x20.png │ ├── inflector_spec.rb │ ├── input_spec.rb │ ├── network_spec.rb │ ├── parallax_spec.rb │ ├── text_spec.rb │ └── window_spec.rb └── spec_helper.rb └── specs.watchr /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | pkg 3 | .gem 4 | doc 5 | .yardoc 6 | .git 7 | .idea 8 | coverage 9 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1.0 4 | - 2.0.0 5 | - 1.9.3 6 | 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | gem "gosu" 3 | 4 | group :test do 5 | gem "rspec", ">= 2.1.0" 6 | gem "watchr" 7 | gem "rcov" 8 | end 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require File.dirname(__FILE__) + '/lib/chingu' 3 | include Chingu 4 | 5 | gem 'rspec', '>= 2.1.0' 6 | require 'rspec/core/rake_task' 7 | 8 | desc "Run the specs under spec" 9 | RSpec::Core::RakeTask.new { |t| } 10 | 11 | desc "Run the specs with rcov" 12 | RSpec::Core::RakeTask.new(:rcov) do |t| 13 | t.rcov = true 14 | t.rcov_opts = ['-T', '--no-html', '--exclude spec,gem'] 15 | end 16 | task :default => :spec 17 | 18 | desc "Build gem" 19 | task :build do 20 | system "gem build chingu.gemspec" 21 | puts "Moving into directory pkg" 22 | system "mv chingu-#{Chingu::VERSION}.gem pkg/" 23 | end 24 | 25 | desc "Release new gem" 26 | task :release => :build do 27 | system "gem push pkg/chingu-#{Chingu::VERSION}.gem" 28 | end -------------------------------------------------------------------------------- /benchmarks/README.txt: -------------------------------------------------------------------------------- 1 | Various chingu / gamedev related benchmark tests.. pretty sloppy but answers some questions. -------------------------------------------------------------------------------- /benchmarks/array_vs_hash.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require 'benchmark' 4 | 5 | 6 | @array = [] 7 | @hash = {} 8 | Benchmark.bm(22) do |x| 9 | x.report('add/remove from array') { 100000.times { |nr| @array.push(nr); @array.delete(nr-1) if nr > 100 } } 10 | x.report('add/remove from hash') { 100000.times { |nr| @hash[nr] = true; @hash.delete(nr-1) if nr > 100 } } 11 | 12 | @array.clear 13 | @hash.clear 14 | x.report('add to array') { 100000.times { |nr| @array.push(nr) } } 15 | x.report('add to hash') { 100000.times { |nr| @hash[nr] = true } } 16 | 17 | x.report('array.each') { 100.times { |nr| @array.each { |x| x } } } 18 | x.report('hash.each') { 100.times { |nr| @hash.each { |k,v| k } } } 19 | x.report('hash.each_key') { 100.times { |nr| @hash.each_key { |x| x } } } 20 | end 21 | -------------------------------------------------------------------------------- /benchmarks/arrays_bench.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require 'benchmark' 4 | a = [] 5 | aa = [] 6 | b = [] 7 | bb = [] 8 | 1000.times do |index| 9 | a.push(index) 10 | b.push(index) if index%2 == 0 11 | aa.push(index) 12 | bb.push(index) if index%2 == 0 13 | end 14 | p a.size 15 | p aa.size 16 | 17 | Benchmark.bm(22) do |x| 18 | x.report('remove b from a #1') { b.each { |x| a.delete(x) }; } 19 | p a.size 20 | x.report('remove b from a #2') { aa -= bb; } 21 | p aa.size 22 | end -------------------------------------------------------------------------------- /benchmarks/benchmark.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | Benchmark.bm do |b| 3 | range = 1..10000 4 | b.report("Object") {range.each {Object.new}} 5 | b.report("BasicObject") {range.each {Array.new}} 6 | end -------------------------------------------------------------------------------- /benchmarks/benchmark3.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | 3 | class A 4 | attr_reader :variable 5 | def initialize 6 | @variable = "hello" 7 | end 8 | end 9 | 10 | a = A.new 11 | # number of iterations 12 | n = 1000000 13 | 14 | Benchmark.bm(22) do |x| 15 | x.report('getter') do 16 | for i in 1..n; a.variable; end 17 | end 18 | 19 | x.report('direct access') do 20 | @variable = "hello" 21 | for i in 1..n; @variable; end 22 | end 23 | end -------------------------------------------------------------------------------- /benchmarks/benchmark4.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | require 'rubygems' 3 | require 'randomr' 4 | 5 | a = Array.new 6 | n = 1000000 7 | Benchmark.bm(22) do |x| 8 | x.report('is_a?') do 9 | n.times do 10 | a.is_a?(Array) 11 | end 12 | end 13 | 14 | x.report('kind_of?') do 15 | n.times do 16 | a.kind_of?(Array) 17 | end 18 | end 19 | 20 | x.report('respond_to?') do 21 | n.times do 22 | a.respond_to?(:size) 23 | end 24 | end 25 | end 26 | 27 | 28 | 29 | arr = [:a, :b, :c] 30 | n = 1000000 31 | Benchmark.bm(22) do |x| 32 | x.report('arr.each') do 33 | n.times do 34 | arr.each { |item| item; } 35 | end 36 | end 37 | 38 | x.report('for item in arr') do 39 | n.times do 40 | for item in arr; item; end; 41 | end 42 | end 43 | end 44 | 45 | 46 | n = 1000000 47 | Benchmark.bm(22) do |x| 48 | x.report('randomr(100)') do 49 | for i in 1..n; Randomr.randomr; end 50 | end 51 | 52 | x.report('rand(100)') do 53 | for i in 1..n; rand(100); end 54 | end 55 | end 56 | 57 | 58 | n = 1000000 59 | Benchmark.bm(12) do |test| 60 | test.report("normal:") do 61 | n.times do |x| 62 | y = x + 1 63 | end 64 | end 65 | test.report("predefine:") do 66 | x = y = 0 67 | n.times do |x| 68 | y = x + 1 69 | end 70 | end 71 | end -------------------------------------------------------------------------------- /benchmarks/benchmark5.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | require 'rubygems' 3 | require 'set' 4 | 5 | class Foo 6 | @list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 7 | @@list2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 8 | 9 | def self.list 10 | @list 11 | end 12 | 13 | def self.list2 14 | @@list2 15 | end 16 | 17 | attr_accessor :list 18 | def initialize 19 | @list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 20 | end 21 | 22 | def bar 23 | end 24 | end 25 | 26 | foo = Foo.new 27 | 28 | 29 | s = Set.new 30 | a = Array.new 31 | h = Hash.new 32 | h[:a] = Array.new 33 | 34 | n = 1000000 35 | Benchmark.bm(22) do |x| 36 | x.report('Array << ') do 37 | n.times do 38 | a << n 39 | end 40 | end 41 | 42 | x.report('Set << ') do 43 | n.times do 44 | s << n 45 | end 46 | end 47 | 48 | x.report('Hash[:a] << ') do 49 | n.times do 50 | h[:a] << n 51 | end 52 | end 53 | end 54 | 55 | 56 | 57 | n = 1000000 58 | Benchmark.bm(22) do |x| 59 | x.report('respond_to?') do 60 | n.times do 61 | foo.respond_to?(:bar) 62 | end 63 | end 64 | 65 | x.report('foo.bar method call') do 66 | n.times do 67 | foo.bar 68 | end 69 | end 70 | end 71 | 72 | n = 100000 73 | Benchmark.bm(22) do |x| 74 | x.report('ivar axx') do 75 | n.times do 76 | foo.list.each { |num| } 77 | end 78 | end 79 | 80 | x.report('class attribute axx') do 81 | n.times do 82 | Foo.list.each { |num| } 83 | end 84 | end 85 | 86 | x.report('class var axx') do 87 | n.times do 88 | Foo.list2.each { |num| } 89 | end 90 | end 91 | end -------------------------------------------------------------------------------- /benchmarks/benchmark6.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | require 'rubygems' 3 | require 'set' 4 | 5 | a = [1,2,3,4,5] 6 | h = {1=>"b",2=>"b",3=>"b",4=>"b",5=>"b"} 7 | 8 | n = 1000000 9 | Benchmark.bm(22) do |x| 10 | 11 | x.report('Array.each ') do 12 | n.times do 13 | a.each {} 14 | end 15 | end 16 | 17 | x.report('Hash.each') do 18 | n.times do 19 | h.each {} 20 | end 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /benchmarks/benchmark_ping_localhost.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require File.join(File.dirname($0), "..", "lib", "chingu") 4 | include Gosu 5 | include Chingu 6 | 7 | # 8 | # Simple 9 | # 10 | class Game < Chingu::Window 11 | 12 | def setup 13 | on_input(:esc, :exit) 14 | 15 | @client = Client.new 16 | @server = Server.new 17 | 18 | @server.start("0.0.0.0", 1234) 19 | @client.connect("127.0.0.1", 1234) 20 | end 21 | 22 | def update 23 | @client.update 24 | @client.update_trait 25 | @server.update 26 | @server.update_trait 27 | end 28 | 29 | end 30 | 31 | # 32 | # Our Client. We inherit from Chingu::GameStates::NetworkClient 33 | # 34 | class Client < GameStates::NetworkClient 35 | trait :timer 36 | 37 | def on_connect 38 | $window.caption = "[CONNECTED]" 39 | send_msg(:timestamp => Gosu::milliseconds) 40 | every(1000) { send_msg(:timestamp => Gosu::milliseconds) } 41 | end 42 | 43 | def on_msg(msg) 44 | latency = (Gosu::milliseconds - msg[:timestamp]) 45 | #puts "PONG: #{latency}ms" 46 | $window.caption = "PONG: #{latency}ms" 47 | end 48 | 49 | end 50 | 51 | # 52 | # Our Server. We inherit from Chingu::GameStates::NetworkServer 53 | # 54 | class Server < GameStates::NetworkServer 55 | 56 | def on_msg(socket, msg) 57 | #puts "PING: #{msg[:timestamp]}" 58 | send_msg(socket, {:timestamp => msg[:timestamp]}) 59 | end 60 | 61 | end 62 | 63 | Game.new.show -------------------------------------------------------------------------------- /benchmarks/game_object_list_benchmark.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require 'benchmark' 4 | require File.join(File.dirname($0), "..", "lib", "chingu") 5 | #gem 'chingu', '0.6' 6 | #require 'chingu' 7 | include Gosu 8 | include Chingu 9 | 10 | class MyGameObject < GameObject 11 | end 12 | 13 | class MyBasicGameObject < GameObject 14 | end 15 | 16 | class ObjectWithTraits < GameObject 17 | traits :velocity, :timer 18 | end 19 | 20 | 21 | class Game < Chingu::Window 22 | def initialize 23 | super(600,200) 24 | self.input = { :esc => :exit } 25 | 26 | Benchmark.bm(22) do |x| 27 | x.report('create 50000 game objects') { 50000.times { MyGameObject.create } } 28 | x.report('force_update') { 60.times { $window.game_objects.force_update } } 29 | x.report('force_draw') { 60.times { $window.game_objects.force_draw } } 30 | x.report('update') { 60.times { $window.game_objects.update } } 31 | x.report('draw') { 60.times { $window.game_objects.draw } } 32 | x.report('update + create') { 60.times { $window.game_objects.update; MyGameObject.create; } } 33 | x.report('draw + create') { 60.times { $window.game_objects.draw; MyGameObject.create; } } 34 | x.report('update + create + destroy') { 60.times { $window.game_objects.update; MyGameObject.create; MyGameObject.all.first.destroy } } 35 | x.report('draw + create + destroy') { 60.times { $window.game_objects.draw; MyGameObject.create; MyGameObject.all.first.destroy } } 36 | end 37 | end 38 | end 39 | 40 | Game.new.show 41 | 42 | # 43 | # BEFORE GameObjectList visible_game_object/unpaused_game_object - refactor 44 | # 45 | #create 50000 game objects 1.919000 0.000000 1.919000 ( 1.906109) 46 | #force_update 0.702000 0.000000 0.702000 ( 0.716041) 47 | #force_draw 0.920000 0.000000 0.920000 ( 0.924053) 48 | #update 1.420000 0.000000 1.420000 ( 1.421081) 49 | #draw 1.497000 0.000000 1.497000 ( 1.520087) 50 | #update + create 1.420000 0.000000 1.420000 ( 1.420081) 51 | #draw + create 1.497000 0.000000 1.497000 ( 1.528088) 52 | #update + create + destroy 2.106000 0.000000 2.106000 ( 2.099120) 53 | #draw + create + destroy 2.262000 0.000000 2.262000 ( 2.265129) 54 | 55 | # 56 | # AFTER 57 | # 58 | #create 50000 game objects 1.934000 0.000000 1.934000 ( 1.927111) 59 | #force_update 0.718000 0.000000 0.718000 ( 0.712040) 60 | #force_draw 0.920000 0.000000 0.920000 ( 0.929053) 61 | #update 0.702000 0.000000 0.702000 ( 0.708041) 62 | #draw 0.904000 0.000000 0.904000 ( 0.932053) 63 | #update + create 0.671000 0.000000 0.671000 ( 0.688040) 64 | #draw + create 0.921000 0.000000 0.921000 ( 0.915052) 65 | #update + create + destroy 1.950000 0.000000 1.950000 ( 1.957112) 66 | #draw + create + destroy 2.184000 0.000000 2.184000 ( 2.230128) 67 | 68 | # 69 | # WITH HASH instead of ARRAY 70 | # 71 | #create 50000 game objects 0.733000 0.016000 0.749000 ( 0.737042) 72 | #force_update 1.045000 0.000000 1.045000 ( 1.048060) 73 | #force_draw 1.154000 0.000000 1.154000 ( 1.150065) 74 | #update 1.014000 0.000000 1.014000 ( 1.028058) 75 | #draw 1.139000 0.000000 1.139000 ( 1.152066) 76 | #update + create 1.045000 0.000000 1.045000 ( 1.041060) 77 | #draw + create 1.155000 0.000000 1.155000 ( 1.155066) 78 | #update + create + destroy 1.529000 0.000000 1.529000 ( 1.525087) 79 | #draw + create + destroy 1.653000 0.000000 1.653000 ( 1.661095) 80 | 81 | 82 | # 83 | # CONCLUSION 84 | # 85 | # Even when we're adding or destroying objects each game tick 86 | # we benefit greatly from keeping separate draw and update lists 87 | # instead of checking visible/paused flags in the draw/update loop itself 88 | # 89 | # Hash would be better then Array if we create/destroy tons of object. 90 | # Arrays are faster to iterate through, which we do alot of gametick. 91 | # 92 | # -------------------------------------------------------------------------------- /benchmarks/game_objects_benchmark.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require 'benchmark' 4 | require File.join(File.dirname($0), "..", "lib", "chingu") 5 | #gem 'chingu', '0.6' 6 | #require 'chingu' 7 | include Gosu 8 | include Chingu 9 | 10 | class ObjectX < GameObject; end; 11 | class ObjectY < GameObject; end; 12 | class ObjectZ < GameObject; end; 13 | class ObjectWithTraits < GameObject 14 | traits :velocity, :timer 15 | end 16 | 17 | class Test 18 | @instances = [] 19 | class << self; attr_accessor :instances end 20 | 21 | def initialize 22 | self.class.instances << self 23 | end 24 | end 25 | 26 | class Game < Chingu::Window 27 | def initialize 28 | super(600,200) 29 | self.input = { :esc => :exit } 30 | end 31 | 32 | def update 33 | Benchmark.bm(22) do |x| 34 | game_object = Test.new 35 | game_object = GameObject.new 36 | x.report('respond_to?') { 100000.times { game_object.respond_to?(:update_trait) } } 37 | x.report('call empty method') { 100000.times { game_object.update_trait } } 38 | 39 | x.report('game_objects') { 10000.times { GameObject.create } } 40 | game_objects.clear 41 | x.report('basic_game_objects') { 10000.times { BasicGameObject.create } } 42 | game_objects.clear 43 | x.report('ObjectX') { 100000.times { ObjectX.create } } 44 | x.report('ObjectY') { 100000.times { ObjectY.create } } 45 | x.report('ObjectZ') { 100000.times { ObjectZ.create } } 46 | x.report('ObjectWithTraits') { 100000.times { ObjectWithTraits.create } } 47 | 48 | x.report('ObjectX full update') { ObjectX.each { |x| x.update_trait; x.update; } } 49 | x.report('ObjectWithTraits full update') { ObjectWithTraits.each { |x| x.update_trait; x.update; } } 50 | 51 | x.report('ObjectX.each') { ObjectX.all.each { |x| x } } 52 | x.report('ObjectY.each') { ObjectY.all.each { |y| y } } 53 | x.report('ObjectZ.each') { ObjectZ.all.each { |z| z } } 54 | 55 | 56 | p game_objects.size 57 | #x.report('ObjectX destroy') { ObjectX.each_with_index { |x, i| x.destroy if i%2==0 }; game_objects.sync; } 58 | p game_objects.size 59 | exit 60 | end 61 | end 62 | end 63 | 64 | Game.new.show 65 | 66 | 67 | # user system total real 68 | #respond_to? 0.015000 0.000000 0.015000 ( 0.016001) 69 | #call empty method 0.016000 0.000000 0.016000 ( 0.013001) 70 | #game_objects 0.327000 0.000000 0.327000 ( 0.329019) 71 | #basic_game_objects 0.047000 0.000000 0.047000 ( 0.038002) 72 | #ObjectX 0.312000 0.000000 0.312000 ( 0.314018) 73 | #ObjectY 0.375000 0.000000 0.375000 ( 0.372021) 74 | #ObjectZ 0.327000 0.000000 0.327000 ( 0.335020) 75 | #ObjectWithTraits 0.484000 0.000000 0.484000 ( 0.484027) 76 | #ObjectX full update 0.015000 0.000000 0.015000 ( 0.012001) 77 | #ObjectWithTraits full update 0.032000 0.000000 0.032000 ( 0.034002) 78 | #ObjectX.each 0.015000 0.000000 0.015000 ( 0.009000) 79 | #ObjectY.each 0.000000 0.000000 0.000000 ( 0.010001) 80 | #ObjectZ.each 0.016000 0.000000 0.016000 ( 0.009001) 81 | -------------------------------------------------------------------------------- /benchmarks/lookup_benchmark.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | 3 | Benchmark.bm(22) do |x| 4 | @map = Array.new 5 | @map[0] = Array.new 6 | @map[0][0] = "item" 7 | x.report('lookup fail exception') { 1000000.times { @map[0][10] rescue nil } } 8 | x.report('lookup fail') { 1000000.times { @map[0] && @map[0][10] } } 9 | end -------------------------------------------------------------------------------- /benchmarks/meta_benchmark.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | 3 | class A; def meth1; true; end; end 4 | 5 | # defining using eval/class/instance 6 | str = "def meth2; true; end" 7 | a_binding = A.send(:binding) 8 | 9 | # proc for class/instance 10 | proc1 = Proc.new { def meth2; true; end } 11 | 12 | # proc for define_method 13 | $proc2 = Proc.new { true } 14 | 15 | # unbound method for bind 16 | um = A.instance_method(:meth1) 17 | 18 | # number of iterations 19 | n = 12000 * 600 20 | n = 12000 21 | 22 | Benchmark.bm(22) do |x| 23 | 24 | x.report('instance_eval/str') do 25 | for i in 1..n; A.instance_eval(str); end 26 | end 27 | 28 | x.report('class_eval/str') do 29 | for i in 1..n; A.class_eval(str); end 30 | end 31 | 32 | x.report('eval/str') do 33 | for i in 1..n; eval(str, a_binding); end 34 | end 35 | 36 | x.report('define_method/class') do 37 | for i in 1..n; class A; define_method(:meth2, &$proc2); end; end 38 | end 39 | 40 | x.report('define_method/send') do 41 | for i in 1..n; A.send(:define_method, :meth2, &$proc2); end 42 | end 43 | 44 | x.report('def/unbind/bind') do 45 | for i in 1..n 46 | class A; def meth2; true; end; end 47 | A.instance_method(:meth2).bind(A.new) 48 | end 49 | end 50 | 51 | x.report('instance_eval/proc') do 52 | for i in 1..n; A.instance_eval(&proc1); end 53 | end 54 | 55 | x.report('class_eval/proc') do 56 | for i in 1..n; A.class_eval(&proc1); end 57 | end 58 | 59 | x.report('def') do 60 | for i in 1..n; class A; def meth2; true; end; end; end 61 | end 62 | 63 | x.report('method/bind') do 64 | for i in 1..n; um.bind(A.new); end 65 | end 66 | 67 | end -------------------------------------------------------------------------------- /benchmarks/meta_benchmark2.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | 3 | class A; def meth1; true; end; end 4 | 5 | # defining using eval/class/instance 6 | str = "@var = 1" 7 | a_binding = A.send(:binding) 8 | 9 | # proc for class/instance 10 | proc1 = Proc.new { def meth2; true; end } 11 | 12 | # proc for define_method 13 | $proc2 = Proc.new { true } 14 | 15 | # unbound method for bind 16 | um = A.instance_method(:meth1) 17 | 18 | # number of iterations 19 | n = 5 * 60 * 10 20 | 21 | Benchmark.bm(22) do |x| 22 | 23 | x.report('straight set') do 24 | for i in 1..n; @var = 1; end 25 | end 26 | 27 | x.report('instance_eval/str') do 28 | for i in 1..n; A.instance_eval(str); end 29 | end 30 | 31 | x.report('class_eval/str') do 32 | for i in 1..n; A.class_eval(str); end 33 | end 34 | 35 | x.report('eval/str') do 36 | for i in 1..n; eval(str, a_binding); end 37 | end 38 | 39 | end -------------------------------------------------------------------------------- /benchmarks/trait_benchmark.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require 'benchmark' 4 | require File.join(File.dirname($0), "..", "lib", "chingu") 5 | include Gosu 6 | include Chingu 7 | 8 | # This includes trait :sprite 9 | class SimpleGameObject < BasicGameObject 10 | trait :simple_sprite 11 | end 12 | 13 | class ObjectX < GameObject 14 | traits :velocity, :timer 15 | end 16 | 17 | class Test 18 | @instances = [] 19 | class << self; attr_accessor :instances end 20 | 21 | def initialize 22 | self.class.instances << self 23 | end 24 | end 25 | 26 | class Game < Chingu::Window 27 | def initialize 28 | super(600,200) 29 | self.input = { :esc => :exit } 30 | 31 | Benchmark.bm(22) do |x| 32 | #x.report('GameObject.destroy_all') { GameObject.destroy_all } # SLOW! 33 | x.report('SimpleameObject.create') { 50000.times { SimpleGameObject.create } } 34 | x.report('ClassicGameObject.create') { 50000.times { ClassicGameObject.create } } 35 | x.report('GameObject.create') { 50000.times { GameObject.create } } 36 | x.report('GameObject.create') { 50000.times { GameObject.create } } 37 | x.report('GameObject.create') { 50000.times { GameObject.create } } 38 | end 39 | 40 | end 41 | 42 | end 43 | 44 | Game.new.show 45 | 46 | 47 | # 50000 x GameObject 48 | 49 | # 50 | # With set_options() 51 | # user system total real 52 | #GameObject.create 1.825000 0.000000 1.825000 ( 1.824104) 53 | #GameObject.create 2.044000 0.016000 2.060000 ( 2.083119) 54 | #GameObject.create 1.934000 0.000000 1.934000 ( 1.996114) 55 | 56 | # 57 | # With oldschool option-parsing 58 | # 59 | # user system total real 60 | #GameObject.create 0.577000 0.000000 0.577000 ( 0.574033) 61 | #GameObject.create 0.577000 0.015000 0.592000 ( 0.599034) 62 | #GameObject.create 0.624000 0.000000 0.624000 ( 0.623036) -------------------------------------------------------------------------------- /benchmarks/window_benchmark.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require 'benchmark' 4 | require File.join(File.dirname($0), "..", "lib", "chingu") 5 | #gem 'chingu', '0.6' 6 | #require 'chingu' 7 | include Gosu 8 | include Chingu 9 | 10 | class MyGameObject < GameObject 11 | end 12 | 13 | class Player < GameObject 14 | end 15 | 16 | 17 | 18 | class Game < Chingu::Window 19 | def initialize 20 | super(600,200) 21 | 22 | player = Player.create 23 | 24 | 25 | Benchmark.bm(22) do |x| 26 | x.report('create 10000 game objects') { 10000.times { MyGameObject.create } } 27 | x.report('update') { 60.times { $window.update } } 28 | 29 | self.input = { :esc => :exit, :d => :exit, :a => :exit } 30 | player.input = { :holding_up => :exit, :holding_down => :exit, :holding_left => :exit, :holding_right => :exit, :space => :exit, :left_ctrl => :exit } 31 | 32 | x.report('update window/player input') { 60.times { $window.update } } 33 | end 34 | end 35 | end 36 | 37 | Game.new.show 38 | -------------------------------------------------------------------------------- /chingu.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.dirname(__FILE__) + '/lib/chingu/version' 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "chingu" 6 | s.version = Chingu::VERSION 7 | s.authors = ["ippa"] 8 | s.email = "ippa@rubylicio.us" 9 | s.homepage = "http://ippa.se/chingu" 10 | s.description = "OpenGL accelerated 2D game framework for Ruby. Builds on Gosu (Ruby/C++) which provides all the core functionality. Chingu adds simple yet powerful game states, prettier input handling, deployment safe asset-handling, a basic re-usable game object and stackable game logic." 11 | s.summary = "OpenGL accelerated 2D game framework for Ruby." 12 | s.required_rubygems_version = ">= 1.3.6" 13 | 14 | s.files = Dir.glob("{lib,examples,benchmarks,spec}/**/*") + %w(LICENSE README.rdoc specs.watchr Rakefile LICENSE) 15 | s.extra_rdoc_files = [ "LICENSE", "README.rdoc" ] 16 | s.require_paths = ["lib"] 17 | s.rubyforge_project = "chingu" 18 | 19 | s.add_dependency("gosu", [">= 0.7.45"]) 20 | s.add_runtime_dependency("gosu", [">= 0.7.45"]) 21 | s.add_development_dependency("rspec", [">= 2.1.0"]) 22 | s.add_development_dependency("watchr", [">= 0"]) 23 | s.add_development_dependency("rcov", [">= 0"]) 24 | end 25 | 26 | -------------------------------------------------------------------------------- /examples/example10_traits_retrofy.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | $stderr.sync = $stdout.sync = true 8 | 9 | # 10 | # Testing out a new module-only-super-chain trait system 11 | # 12 | class Game < Chingu::Window 13 | def initialize 14 | super(600,400) 15 | self.caption = "Testing out new module-based traits (SPACE for more spaceships)" 16 | self.input = { :space => :create_thing, :esc => :exit } 17 | retrofy 18 | create_thing(200,200) 19 | end 20 | 21 | def update 22 | puts "--- UPDATE ---" 23 | super 24 | end 25 | 26 | def draw 27 | puts "--- DRAW ---" 28 | super 29 | end 30 | 31 | def create_thing(x=nil, y=nil) 32 | Thing.create(:x => x||rand($window.width), :y => y||rand($window.height), :debug => true) 33 | end 34 | end 35 | 36 | class Thing < Chingu::GameObject 37 | trait :effect 38 | trait :velocity 39 | 40 | def initialize(options) 41 | super 42 | @image = Image["spaceship.png"] 43 | 44 | self.rotation_center(:center) 45 | 46 | # Julians ninjahack to get that sweet pixely feeling when zooming :) 47 | # glBindTexture(GL_TEXTURE_2D, @image.gl_tex_info.tex_name) 48 | # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) 49 | # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 50 | # 51 | # The above code has been merged into chingu as @image.retrofy 52 | # 53 | # @image.retrofy 54 | # 55 | # Use Gosu::enable_undocumented_retrofication instead! 56 | 57 | 58 | self.scale = 8 59 | puts "scale: " + scale.to_i.to_s 60 | self.rotation_rate = 2 61 | self.velocity_x = 2 62 | end 63 | 64 | def update 65 | puts "Thing#update" 66 | if outside_window? 67 | @velocity_x = -@velocity_x 68 | self.rotation_rate = -self.rotation_rate 69 | end 70 | end 71 | 72 | def draw 73 | puts "Thing#draw" 74 | super 75 | end 76 | 77 | end 78 | 79 | 80 | Game.new.show 81 | -------------------------------------------------------------------------------- /examples/example11_animation.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Animation / retrofy example 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super 14 | self.factor = 6 15 | self.input = { :escape => :exit } 16 | self.caption = "Chingu::Animation / retrofy example. Move with arrows!" 17 | retrofy 18 | Droid.create(:x => $window.width/2, :y => $window.height/2) 19 | end 20 | end 21 | 22 | class Droid < Chingu::GameObject 23 | traits :timer 24 | 25 | def initialize(options = {}) 26 | super 27 | 28 | # 29 | # This shows up the shorten versio of input-maps, where each key calls a method of the very same name. 30 | # Use this by giving an array of symbols to self.input 31 | # 32 | self.input = [:holding_left, :holding_right, :holding_up, :holding_down] 33 | 34 | # Load the full animation from tile-file media/droid.bmp 35 | @animation = Chingu::Animation.new(:file => "droid_11x15.bmp") 36 | @animation.frame_names = { :scan => 0..5, :up => 6..7, :down => 8..9, :left => 10..11, :right => 12..13 } 37 | 38 | # Start out by animation frames 0-5 (contained by @animation[:scan]) 39 | @frame_name = :scan 40 | 41 | @last_x, @last_y = @x, @y 42 | update 43 | end 44 | 45 | def holding_left 46 | @x -= 2 47 | @frame_name = :left 48 | end 49 | 50 | def holding_right 51 | @x += 2 52 | @frame_name = :right 53 | end 54 | 55 | def holding_up 56 | @y -= 2 57 | @frame_name = :up 58 | end 59 | 60 | def holding_down 61 | @y += 2 62 | @frame_name = :down 63 | end 64 | 65 | # We don't need to call super() in update(). 66 | # By default GameObject#update is empty since it doesn't contain any gamelogic to speak of. 67 | def update 68 | 69 | # Move the animation forward by fetching the next frame and putting it into @image 70 | # @image is drawn by default by GameObject#draw 71 | @image = @animation[@frame_name].next 72 | 73 | # 74 | # If droid stands still, use the scanning animation 75 | # 76 | @frame_name = :scan if @x == @last_x && @y == @last_y 77 | 78 | @x, @y = @last_x, @last_y if outside_window? # return to previous coordinates if outside window 79 | @last_x, @last_y = @x, @y # save current coordinates for possible use next time 80 | end 81 | end 82 | 83 | Game.new.show -------------------------------------------------------------------------------- /examples/example12_trait_timer.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Using trait "timer" to achieve various moves 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super(640,400) 14 | self.input = {:esc => :exit} 15 | self.caption = "Example of Chingus :timer trait (arrows,space,return sets timers that affects @x,@y & @color)" 16 | 17 | Cube.create(:x => 200, :y => 200) 18 | end 19 | end 20 | 21 | class Cube < GameObject 22 | trait :timer 23 | 24 | def initialize(options) 25 | super 26 | self.input = { :left => :left, :right => :right, :up => :up, :down => :down, 27 | :space => :shake1, :return => :shake2, :a => :test_after } 28 | end 29 | 30 | def test_after 31 | after(1000) { @color = Color::CYAN } 32 | end 33 | 34 | def left 35 | during(500) { @x -= 1 } 36 | end 37 | 38 | def right 39 | during(500) { @x += 1 } 40 | end 41 | 42 | def up 43 | @color = Color::RED 44 | during(500) { @y -= 1 }.then { @color = Color::WHITE } 45 | end 46 | 47 | def down 48 | @color = Color::BLUE 49 | during(500) { @y += 1 }.then { @color = Color::WHITE } 50 | end 51 | 52 | def shake1 53 | # 54 | # Nesting works too! 55 | # 56 | during(50) {@y -= 4}.then { during(100) {@y += 4}.then { during(50) {@y -= 4} } } 57 | end 58 | 59 | def shake2 60 | # 61 | # Does the exact same as "shake1" but using between() instead of during() 62 | # 63 | between(0,50) {@y -= 4} 64 | between(50,150) {@y += 4} 65 | between(150,200) {@y -= 4} 66 | end 67 | 68 | def draw 69 | $window.fill_rect([@x, @y, 40, 40], @color) 70 | end 71 | end 72 | 73 | Game.new.show 74 | -------------------------------------------------------------------------------- /examples/example13_high_scores.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Demonstrating Chingus HighScoreList-class 10 | # I couldn't keep myself from spicying it up some though :P 11 | # 12 | class Game < Chingu::Window 13 | def initialize 14 | super(640,400) 15 | self.input = {:esc => :exit, :a => :add } 16 | self.caption = "Example of Chingus HighScore class" 17 | 18 | @title = PulsatingText.create("HIGH SCORES", :x => $window.width/2, :y => 50, :size => 70) 19 | 20 | # 21 | # Load a list from disk, defaults to "high_score_list.yml" 22 | # Argument :size forces list to this size 23 | # 24 | @high_score_list = HighScoreList.load(:size => 10) 25 | 26 | # 27 | # Add some new high scores to the list. :name and :score are required but you can put whatever. 28 | # They will mix with the old scores, automatic default sorting on :score 29 | # 30 | 10.times do 31 | data = {:name => "NEW", :score => rand(10000)} 32 | 33 | position = @high_score_list.add(data) 34 | if position 35 | puts "#{data[:name]} - #{data[:score]} got position #{position}" 36 | else 37 | puts "#{data[:name]} - #{data[:score]} didn't make it" 38 | end 39 | end 40 | 41 | create_text 42 | end 43 | 44 | def add 45 | data = {:name => "NEW", :score => @high_score_list.high_scores.first[:score] + 10, :text => "from example13.rb"} 46 | position = @high_score_list.add(data) 47 | puts "Got position: #{position.to_s}" 48 | create_text 49 | end 50 | 51 | def create_text 52 | Text.destroy_if { |text| text.size == 20} 53 | 54 | # 55 | # Iterate through all high scores and create the visual represenation of it 56 | # 57 | @high_score_list.each_with_index do |high_score, index| 58 | y = index * 25 + 100 59 | Text.create(high_score[:name], :x => 200, :y => y, :size => 20) 60 | Text.create(high_score[:score], :x => 400, :y => y, :size => 20) 61 | end 62 | end 63 | end 64 | 65 | # 66 | # colorful pulsating text... 67 | # 68 | class PulsatingText < Text 69 | traits :timer, :effect 70 | 71 | def initialize(text, options = {}) 72 | super(text, options) 73 | 74 | options = text if text.is_a? Hash 75 | @pulse = options[:pulse] || false 76 | self.rotation_center(:center_center) 77 | every(20) { create_pulse } if @pulse == false 78 | end 79 | 80 | def create_pulse 81 | pulse = PulsatingText.create(@text, :x => @x, :y => @y, :height => @height, :pulse => true, :image => @image, :zorder => @zorder+1) 82 | colors = [Color::RED, Color::GREEN, Color::BLUE] 83 | pulse.color = colors[rand(colors.size)].dup 84 | pulse.mode = :additive 85 | pulse.alpha -= 150 86 | pulse.scale_rate = 0.002 87 | pulse.fade_rate = -3 + rand(2) 88 | pulse.rotation_rate = rand(2)==0 ? 0.05 : -0.05 89 | end 90 | 91 | def update 92 | destroy if self.alpha == 0 93 | end 94 | 95 | end 96 | 97 | Game.new.show 98 | -------------------------------------------------------------------------------- /examples/example14_bounding_box_circle.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Demonstrating Chingu-traits bounding_circle, bounding_box and collision_detection. 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super(640,400) 14 | self.input = {:esc => :exit, :q => :decrease_size, :w => :increase_size, :a => :decrease_speed, :s => :increase_speed} 15 | 16 | self.factor = 1 17 | 20.times { Circle.create(:x => width/2, :y => height/2) } 18 | 20.times { Box.create(:x => width/2, :y => height/2) } 19 | @blue = Color.new(0xFF0000FF) 20 | @white = Color.new(0xFFFFFFFF) 21 | end 22 | 23 | def increase_size 24 | game_objects.each { |go| go.factor += 1 } 25 | end 26 | def decrease_size 27 | game_objects.each { |go| go.factor -= 1 if go.factor > 1 } 28 | end 29 | def increase_speed 30 | game_objects.each { |go| go.velocity_x *= 1.2; go.velocity_y *= 1.2; } 31 | end 32 | def decrease_speed 33 | game_objects.each { |go| go.velocity_x *= 0.8; go.velocity_y *= 0.8; } 34 | end 35 | 36 | def update 37 | super 38 | 39 | game_objects.each { |go| go.color = @white } 40 | 41 | # 42 | # Collide Boxes/Circles, Boxes/Boxes and Circles/Circles (basicly all objects on screen) 43 | # 44 | # Before optmization: 25 FPS (20 boxes and 20 circles) 45 | # Cached radius and rects: 46 | # 47 | [Box, Circle].each_collision(Box, Circle) { |o, o2| o.color, o2.color = @blue, @blue } 48 | 49 | # 50 | # Only collide boxes with other boxes 51 | # 52 | ## Box.each_collision(Box) { |o, o2| o.color, o2.color = @blue, @blue } 53 | 54 | # 55 | # Only collide circles with other circles 56 | # 57 | ## Circle.each_collision(Circle) { |o, o2| o.color, o2.color = @blue, @blue } 58 | 59 | # 60 | # Only collide Boxes with Boxes and Circles 61 | # 62 | ## Box.each_collision(Box,Circle) { |o, o2| o.color, o2.color = @blue, @blue } 63 | 64 | self.caption = "traits bounding_box/circle & collision_detection. Q/W: Size. A/S: Speed. FPS: #{fps} Objects: #{game_objects.size}" 65 | end 66 | end 67 | 68 | class Circle < GameObject 69 | trait :bounding_circle, :debug => true 70 | traits :velocity, :collision_detection 71 | 72 | def setup 73 | @image = Image["circle.png"] 74 | self.velocity_x = 3 - rand * 6 75 | self.velocity_y = 3 - rand * 6 76 | self.input = [:holding_left, :holding_right, :holding_down, :holding_up] # NOTE: giving input an Array, not a Hash 77 | cache_bounding_circle 78 | end 79 | 80 | def holding_left; @x -= 1; end 81 | def holding_right; @x += 1; end 82 | def holding_down; @y += 1; end 83 | def holding_up; @y -= 1; end 84 | 85 | def update 86 | self.velocity_x = -self.velocity_x if @x < 0 || @x > $window.width 87 | self.velocity_y = -self.velocity_y if @y < 0 || @y > $window.height 88 | end 89 | end 90 | 91 | class Box < GameObject 92 | trait :bounding_box, :debug => true 93 | traits :velocity, :collision_detection 94 | 95 | def setup 96 | @image = Image["rect.png"] 97 | self.velocity_x = 3 - rand * 6 98 | self.velocity_y = 3 - rand * 6 99 | 100 | # Test to make sure the bounding_box works with all bellow combos 101 | #self.factor = 2 102 | #self.factor = -2 103 | self.rotation_center = :left_top 104 | #self.rotation_center = :center 105 | #self.rotation_center = :right_bottom 106 | 107 | cache_bounding_box 108 | end 109 | 110 | def update 111 | self.velocity_x = -self.velocity_x if @x < 0 || @x > $window.width 112 | self.velocity_y = -self.velocity_y if @y < 0 || @y > $window.height 113 | end 114 | end 115 | 116 | Game.new.show -------------------------------------------------------------------------------- /examples/example15_trait_timer2.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Testing advanced options of timer-trait 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super(640,400) 14 | self.input = {:esc => :exit} 15 | switch_game_state(Stuff) 16 | end 17 | end 18 | 19 | class Stuff < GameState 20 | trait :timer 21 | 22 | def initialize(options = {}) 23 | super 24 | self.input = {:s => :stop, :"1" => :fast, :"2" => :slow} 25 | @thing = Thing.create(:x => $window.width/2, :y => $window.height / 2 ) 26 | 27 | every(500, :name => :blink) { @thing.visible? ? @thing.hide! : @thing.show! } 28 | p timer_exists?(:blink) 29 | $window.caption = "Timer-usage. 's' to stop timer, '1' for fast timer, '2' for slow timer" 30 | end 31 | 32 | def stop 33 | stop_timer(:blink) 34 | end 35 | 36 | def fast 37 | every(200, :name => :blink) { @thing.visible? ? @thing.hide! : @thing.show! } 38 | end 39 | 40 | def slow 41 | every(1000, :name => :blink) { @thing.visible? ? @thing.hide! : @thing.show! } 42 | end 43 | 44 | def update 45 | super 46 | game_objects.destroy_if { |object| object.outside_window? } 47 | end 48 | end 49 | 50 | class Thing < GameObject 51 | def initialize(options = {}) 52 | super 53 | @image = Image["circle.png"] 54 | end 55 | end 56 | 57 | Game.new.show -------------------------------------------------------------------------------- /examples/example16_online_high_scores.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Demonstrating Chingus OnlineHighScoreList-class 10 | # 11 | # Syncs against http://www.gamercv.com/games/1-test 12 | # 13 | class Game < Chingu::Window 14 | def initialize 15 | super(640,400) 16 | self.caption = "OnlineHighScoreList example. Press 'A' to add a top-score, 'R' to add a random." 17 | push_game_state(HighScore) 18 | end 19 | end 20 | 21 | class HighScore < GameState 22 | 23 | def setup 24 | self.input = {:esc => :exit, :a => :enter_name, :r => :add_random} 25 | 26 | PulsatingText.create("class OnlineHighScoreList & www.gamvercv.com", :x => $window.width/2, :y => 50, :size => 30) 27 | Text.create("Syncs from/to http://www.gamercv.com/games/1-test", :x => $window.width/2, :y => 80, :size => 24, :rotation_center => :center) 28 | 29 | 30 | # 31 | # Load a remote high score list 32 | # 33 | @high_score_list = OnlineHighScoreList.load(:game_id => 1, :login => "chingu", :password => "chingu", :limit => 10) 34 | 35 | create_text 36 | end 37 | 38 | def enter_name 39 | push_game_state GameStates::EnterName.new(:callback => method(:add) ) 40 | end 41 | 42 | def add(name = nil) 43 | return unless name 44 | return unless name.size > 0 45 | data = {:name => name, :score => (@high_score_list[0][:score].to_i + 10), :text => "Hello from Chingus example16.rb"} 46 | position = @high_score_list.add(data) 47 | puts "Got position: #{position.to_s}" 48 | create_text 49 | end 50 | 51 | def add_random 52 | data = {:name => "RND", :score => @high_score_list[0][:score]-rand(100), :text => "Random from Chingus example16.rb"} 53 | position = @high_score_list.add(data) 54 | puts "Got position: #{position.to_s}" 55 | create_text 56 | end 57 | 58 | def create_text 59 | Text.destroy_if { |text| text.size == 20 } 60 | 61 | # 62 | # Iterate through all high scores and create the visual represenation of it 63 | # 64 | @high_score_list.each_with_index do |high_score, index| 65 | y = index * 25 + 100 66 | Text.create(high_score[:name], :x => 200, :y => y, :size => 20) 67 | Text.create(high_score[:score], :x => 400, :y => y, :size => 20) 68 | end 69 | end 70 | end 71 | 72 | # 73 | # colorful pulsating text... 74 | # 75 | class PulsatingText < Text 76 | traits :timer, :effect 77 | @@red = Color.new(0xFFFF0000) 78 | @@green = Color.new(0xFF00FF00) 79 | @@blue = Color.new(0xFF0000FF) 80 | 81 | def initialize(text, options = {}) 82 | super(text, options) 83 | 84 | options = text if text.is_a? Hash 85 | @pulse = options[:pulse] || false 86 | self.rotation_center(:center_center) 87 | every(20) { create_pulse } if @pulse == false 88 | end 89 | 90 | def create_pulse 91 | pulse = PulsatingText.create(@text, :x => @x, :y => @y, :height => @height, :pulse => true, :image => @image, :zorder => @zorder+1) 92 | colors = [@@red, @@green, @@blue] 93 | pulse.color = colors[rand(colors.size)].dup 94 | pulse.mode = :additive 95 | pulse.alpha -= 150 96 | pulse.scale_rate = 0.002 97 | pulse.fade_rate = -3 + rand(2) 98 | pulse.rotation_rate = rand(2)==0 ? 0.05 : -0.05 99 | end 100 | 101 | def update 102 | destroy if self.alpha == 0 103 | end 104 | 105 | end 106 | 107 | Game.new.show 108 | -------------------------------------------------------------------------------- /examples/example17_gosu_tutorial.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # Set to true to see bounding circles used for collision detection 9 | DEBUG = false 10 | 11 | class Game < Chingu::Window 12 | def initialize 13 | super(640,400) 14 | self.input = {:esc => :exit} 15 | 16 | @player = Player.create(:zorder => 2, :x=>320, :y=>240) 17 | @score = 0 18 | @score_text = Text.create("Score: #{@score}", :x => 10, :y => 10, :zorder => 55, :size=>20) 19 | end 20 | 21 | def update 22 | super 23 | 24 | if rand(100) < 4 && Star.all.size < 25 25 | Star.create 26 | end 27 | 28 | # 29 | # Collide @player with all instances of class Star 30 | # 31 | @player.each_collision(Star) do |player, star| 32 | star.destroy 33 | @score+=10 34 | end 35 | 36 | @score_text.text = "Score: #{@score}" 37 | self.caption = "Chingu Game - " + @score_text.text 38 | end 39 | end 40 | 41 | class Player < GameObject 42 | trait :bounding_circle, :debug => DEBUG 43 | traits :collision_detection, :effect, :velocity 44 | 45 | def initialize(options={}) 46 | super(options) 47 | @image = Image["Starfighter.bmp"] 48 | self.input = {:holding_right=>:turn_right, :holding_left=>:turn_left, :holding_up=>:accelerate} 49 | self.max_velocity = 10 50 | end 51 | 52 | def accelerate 53 | self.velocity_x = Gosu::offset_x(self.angle, 0.5)*self.max_velocity_x 54 | self.velocity_y = Gosu::offset_y(self.angle, 0.5)*self.max_velocity_y 55 | end 56 | 57 | def turn_right 58 | # The same can be achieved without trait 'effect' as: self.angle += 4.5 59 | rotate(4.5) 60 | end 61 | 62 | def turn_left 63 | # The same can be achieved without trait 'effect' as: self.angle -= 4.5 64 | rotate(-4.5) 65 | end 66 | 67 | def update 68 | self.velocity_x *= 0.95 # dampen the movement 69 | self.velocity_y *= 0.95 70 | 71 | @x %= $window.width # wrap around the screen 72 | @y %= $window.height 73 | end 74 | end 75 | 76 | class Star < GameObject 77 | trait :bounding_circle, :debug => DEBUG 78 | trait :collision_detection 79 | 80 | def initialize(options={}) 81 | super 82 | @animation = Chingu::Animation.new(:file => "Star.png", :size => 25) 83 | @image = @animation.next 84 | self.zorder = 1 85 | self.color = Gosu::Color.new(0xff000000) 86 | self.color.red = rand(255 - 40) + 40 87 | self.color.green = rand(255 - 40) + 40 88 | self.color.blue = rand(255 - 40) + 40 89 | self.x =rand * 640 90 | self.y =rand * 480 91 | 92 | # 93 | # A cached bounding circle will not adapt to changes in size, but it will follow objects X / Y 94 | # Same is true for "cache_bounding_box" 95 | # 96 | cache_bounding_circle 97 | end 98 | 99 | def update 100 | # Move the animation forward by fetching the next frame and putting it into @image 101 | # @image is drawn by default by GameObject#draw 102 | @image = @animation.next 103 | end 104 | end 105 | 106 | Game.new.show -------------------------------------------------------------------------------- /examples/example18_animation_trait.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # animation-trait example 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super(400, 400) 14 | self.input = { :escape => :exit } 15 | switch_game_state(Play) 16 | end 17 | end 18 | 19 | class Play < Chingu::GameState 20 | trait :timer 21 | 22 | def setup 23 | Droid.create(:x => 200, :y => 300, :factor => 4, :alpha => 100) 24 | every(1000) { Spaceship.create(:x => 10, :y => 300, :velocity_x => 1) } 25 | every(1000) { Plane.create(:x => 10, :y => 350 + rand(20), :velocity => [1,0]) } 26 | every(500) { FireBullet.create(:x => 10, :y => 370, :velocity_x => 1) } 27 | every(500) { Star.create(:x => 400, :y => 400, :velocity => [-2,-rand*2]) } 28 | every(1400) { Heli.create(:x => 40, :y => 40, :velocity_x => 1) } 29 | end 30 | 31 | def update 32 | super 33 | game_objects.select { |game_object| game_object.outside_window? }.each(&:destroy) 34 | $window.caption = "game_objects: #{game_objects.size}" 35 | end 36 | 37 | def draw 38 | fill(Gosu::Color::GRAY) 39 | super 40 | end 41 | end 42 | 43 | class Actor < GameObject 44 | trait :velocity 45 | 46 | def setup 47 | @image = Image["#{self.filename}.png"] 48 | @zorder = 10 49 | end 50 | 51 | end 52 | class Spaceship < Actor; end # spaceship.png will be loaded 53 | class Plane < Actor; end # plane.png will be loaded 54 | class FireBullet < Actor; end # fire_bullet.png will be loaded 55 | 56 | # 57 | # droid_11x15.png will be loaded and animated with :delay parameter, each frame beeing 11 x 15 pixels 58 | # 59 | class Droid < GameObject 60 | trait :velocity 61 | trait :animation, :delay => 200, :size => [11,15] 62 | 63 | def update 64 | @image = self.animation.next if self.animation 65 | end 66 | end 67 | 68 | # 69 | # heli.png will be loaded 70 | # since it doesn't contain any framesize information, chingu will assume same width and height 71 | # 72 | class Heli < GameObject 73 | trait :velocity 74 | trait :animation, :delay => 200, :size => [32,32] 75 | 76 | def update 77 | @image = self.animation.next if self.animation 78 | end 79 | end 80 | 81 | # 82 | # star_25x25_default.png and star_25x25_explode.png will be loaded. 83 | # Access the 2 animation-"states" with self.animations[:default] and self.animations[:explode] 84 | # self.animation will point to self.animations[:default] 85 | # 86 | class Star < Actor 87 | trait :animation, :delay => 100 88 | 89 | def setup 90 | self.animations[:explode].loop = false 91 | end 92 | 93 | def update 94 | 95 | if @x < $window.width/2 || @y < $window.height/2 96 | @image = self.animations[:explode].next 97 | else 98 | @image = self.animations[:default].next 99 | end 100 | end 101 | 102 | end 103 | 104 | Game.new.show -------------------------------------------------------------------------------- /examples/example1_basics.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | # 8 | # A minimalistic Chingu example. 9 | # Chingu::Window provides #update and #draw which calls corresponding methods for all objects based on Chingu::Actors 10 | # 11 | # Image["picture.png"] is a deployment safe shortcut to Gosu's Image.new and supports multiple locations for "picture.png" 12 | # By default current dir, media\ and gfx\ is searched. To add own directories: 13 | # 14 | # Image.autoload_dirs << File.join(self.root, "data", "my_image_dir") 15 | # 16 | class Game < Chingu::Window 17 | def initialize 18 | super(640,480,false) # leave it blank and it will be 800,600,non fullscreen 19 | self.input = { :escape => :exit } # exits example on Escape 20 | 21 | @player = Player.create(:x => 200, :y => 200, :image => Image["spaceship.png"]) 22 | @player.input = { :holding_left => :move_left, :holding_right => :move_right, 23 | :holding_up => :move_up, :holding_down => :move_down } 24 | end 25 | 26 | def update 27 | super 28 | self.caption = "FPS: #{self.fps} milliseconds_since_last_tick: #{self.milliseconds_since_last_tick}" 29 | end 30 | end 31 | 32 | class Player < Chingu::GameObject 33 | def move_left; @x -= 3; end 34 | def move_right; @x += 3; end 35 | def move_up; @y -= 3; end 36 | def move_down; @y += 3; end 37 | end 38 | 39 | 40 | Game.new.show -------------------------------------------------------------------------------- /examples/example20_trait_inheritence_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # This is more of a technical test to sort out the class inheritable accessor .. 10 | # ..more then it's a fun-to-watch demo. 11 | # 12 | class Game < Chingu::Window 13 | def initialize 14 | super 15 | self.input = { :escape => :exit } 16 | 17 | star = SpinningStar.create(:x => 400, :y => 100) 18 | star2 = MovingStar.create(:x => 10, :y => 200) 19 | 20 | p star.class.superclass 21 | p "=== Should only have collision detection" 22 | p star.class 23 | p star.class.trait_options 24 | p "=== Should have collision detection and velocity" 25 | p star2.class 26 | p star2.class.trait_options 27 | end 28 | end 29 | 30 | 31 | class Star < GameObject 32 | trait :collision_detection 33 | 34 | def initialize(options={}) 35 | super 36 | 37 | @animation = Chingu::Animation.new(:file => "Star.png", :size => 25) 38 | @image = @animation.next 39 | end 40 | 41 | end 42 | 43 | class MovingStar < Star 44 | trait :velocity 45 | 46 | def initialize(options={}) 47 | super 48 | self.velocity_x = 1 49 | end 50 | end 51 | 52 | class SpinningStar < Star 53 | def update 54 | @image = @animation.next 55 | end 56 | end 57 | 58 | 59 | Game.new.show -------------------------------------------------------------------------------- /examples/example22_text.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | 9 | class Game < Chingu::Window 10 | def initialize 11 | super(640,480,false) # leave it blank and it will be 800,600,non fullscreen 12 | self.input = { :escape => :exit } # exits example on Escape 13 | self.caption = "Demonstration of Text" 14 | push_game_state(Woff) 15 | end 16 | end 17 | 18 | class Woff < GameState 19 | def setup 20 | self.input = { [:space, :esc] => :exit } 21 | 22 | 23 | # Some Text with adapting :background 24 | Text.create("tiny", :x => 200, :y => 200, :size => 20, :background => "talk_bubble.png", :color => Color::BLACK) 25 | 26 | Text.size = 50 27 | Text.padding = 20 28 | Text.create("Hello", :x => 100, :y => 30, :background => "talk_bubble.png", :color => Color::BLACK) 29 | Text.create("YES YOU! Bla bla bla bla.", :size => 40, :x => 200, :y => 300, :background => "talk_bubble.png", :color => Color::BLACK) 30 | 31 | # 32 | # TODO: More text examples! 33 | # 34 | 35 | t = Text.create("Scaletest", :x => 10, :y => 100) 36 | t.factor_x = 2 37 | t.factor_y = 1 38 | end 39 | 40 | def draw 41 | fill_gradient(:from => Color::CYAN, :to => Color::BLUE) 42 | super 43 | end 44 | end 45 | 46 | 47 | Game.new.show -------------------------------------------------------------------------------- /examples/example23_chipmunk.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | 9 | class Game < Chingu::Window 10 | def initialize 11 | super(640,480,false) # leave it blank and it will be 800,600,non fullscreen 12 | self.input = { :escape => :exit } # exits example on Escape 13 | 14 | Player.create(:x => 0, :y => 0, :rotation_center => :top_left) 15 | Text.create("NOTHING TO SEE HERE YET ;-)", :align => :center) 16 | end 17 | end 18 | 19 | class Player < BasicGameObject 20 | trait :sprite, :image => "spaceship.png" 21 | 22 | def setup 23 | end 24 | end 25 | 26 | 27 | Game.new.show -------------------------------------------------------------------------------- /examples/example24_enter_name.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require File.join(File.dirname($0), "..", "lib", "chingu") 4 | include Gosu 5 | include Chingu 6 | 7 | class Game < Chingu::Window 8 | def initialize 9 | super(800,400,false) # leave it blank and it will be 800,600,non fullscreen 10 | self.input = { :escape => :exit } # exits example on Escape 11 | self.caption = "Demonstration of GameStates::EnterName" 12 | push_game_state(GameStates::EnterName.new(:callback => method(:got_name))) 13 | end 14 | 15 | def got_name(name) 16 | puts "Got name: #{name}" 17 | exit 18 | end 19 | end 20 | 21 | Game.new.show -------------------------------------------------------------------------------- /examples/example25_fibers_state_machine.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require File.join(File.dirname($0), "..", "lib", "chingu") 4 | include Gosu 5 | include Chingu 6 | Velocity = Struct.new(:x, :y, :z) 7 | 8 | # 9 | # Press 'E' when demo is running to edit the playfield! 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | p RUBY_VERSION 14 | super(1000,600) 15 | end 16 | 17 | def setup 18 | retrofy 19 | switch_game_state(Example25) 20 | end 21 | end 22 | 23 | class Example25 < GameState 24 | def setup 25 | self.input = { :escape => :exit, :e => :edit } 26 | load_game_objects 27 | @droid = Droid.create(:x => 100, :y => 400) 28 | end 29 | 30 | def edit 31 | push_game_state(GameStates::Edit.new(:grid => [32,32], :classes => [Block])) 32 | end 33 | 34 | def update 35 | super 36 | $window.caption = "x/y: #{@droid.x.to_i}/#{@droid.y.to_i} - FPS: #{$window.fps}" 37 | end 38 | 39 | def draw 40 | super 41 | #fill_rect(Rect.new(0,400,$window.width, 200), Color::White) 42 | end 43 | end 44 | 45 | # 46 | # DROID 47 | # 48 | class Droid < Chingu::GameObject 49 | trait :bounding_box, :scale => 0.80 50 | traits :timer, :collision_detection , :timer, :velocity 51 | 52 | attr_reader :jumpign 53 | 54 | def setup 55 | self.input = { [:holding_left, :holding_a] => :holding_left, 56 | [:holding_right, :holding_d] => :holding_right, 57 | [:up, :w] => :jump, 58 | } 59 | 60 | # Load the full animation from tile-file media/droid.bmp 61 | @animations = Chingu::Animation.new(:file => "droid_11x15.bmp") 62 | @animations.frame_names = { :scan => 0..5, :up => 6..7, :down => 8..9, :left => 10..11, :right => 12..13 } 63 | 64 | @animation = @animations[:scan] 65 | @speed = 3 66 | @jumping = false 67 | 68 | self.zorder = 300 69 | self.factor = 3 70 | self.acceleration_y = 0.5 71 | self.max_velocity = 10 72 | self.rotation_center = :bottom_center 73 | 74 | @fiber = Fiber.new do |command| 75 | puts "got #{command}"; loop { v = Fiber.yield(); puts "got #{v}" } 76 | end 77 | @fiber.resume :one 78 | @fiber.resume :two 79 | #exit 80 | 81 | #fiber = Fiber.new do |command| 82 | #while true 83 | # Fiber.yield 84 | #end 85 | #velocity ||= Velocity.new 86 | 87 | #if command == :left 88 | # move(-@speed, 0) 89 | # @animation = @animations[:left] 90 | #elsif command == :right 91 | # move(@speed, 0) 92 | # @animation = @animations[:right] 93 | #end 94 | 95 | #velocity 96 | #end 97 | 98 | update 99 | end 100 | 101 | def holding_left 102 | @fiber.resume :left 103 | end 104 | 105 | def holding_right 106 | @fiber.resume :right 107 | end 108 | 109 | def jump 110 | return if @jumping 111 | @jumping = true 112 | self.velocity_y = -10 113 | @animation = @animations[:up] 114 | end 115 | 116 | def move(x,y) 117 | self.x += x 118 | self.each_collision(Block) do |me, stone_wall| 119 | self.x = previous_x 120 | break 121 | end 122 | 123 | self.y += y 124 | end 125 | 126 | def update 127 | @fiber.resume :jump# if holding?(:a) 128 | 129 | @image = @animation.next 130 | 131 | self.each_collision(Block) do |me, stone_wall| 132 | if self.velocity_y < 0 # Hitting the ceiling 133 | me.y = stone_wall.bb.bottom + me.image.height * self.factor_y 134 | self.velocity_y = 0 135 | else # Land on ground 136 | @jumping = false 137 | me.y = stone_wall.bb.top-1 138 | end 139 | end 140 | 141 | @animation = @animations[:scan] unless moved? 142 | end 143 | end 144 | 145 | # 146 | # BLOCK, our basic level building block 147 | # 148 | class Block < GameObject 149 | trait :bounding_box, :debug => false 150 | trait :collision_detection 151 | 152 | def self.solid 153 | all.select { |block| block.alpha == 255 } 154 | end 155 | 156 | def self.inside_viewport 157 | all.select { |block| block.game_state.viewport.inside?(block) } 158 | end 159 | 160 | def setup 161 | @image = Image["black_block.png"] 162 | @color = Color.new(0xff808080) 163 | end 164 | end 165 | 166 | 167 | Game.new.show -------------------------------------------------------------------------------- /examples/example26_splash_screen.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | # 8 | # Creates a window, then lets the user change to another one. Use this to change resolution or change to/from 9 | # full-screen mode 10 | # 11 | class SplashScreen < Chingu::Window 12 | def initialize 13 | super(400,200,false) 14 | self.input = { :escape => :close, :return => :load } # exits example on Escape 15 | 16 | Chingu::GameObject.create(:x => 200, :y => 100, :image => Image["spaceship.png"]) 17 | Chingu::Text.create("Press to load game", :y => 100) 18 | end 19 | 20 | def update 21 | super 22 | self.caption = "FPS: #{self.fps} milliseconds_since_last_tick: #{self.milliseconds_since_last_tick}" 23 | end 24 | 25 | def load 26 | # MUST close current window before we open the new one! 27 | close 28 | MainGame.new.show 29 | end 30 | end 31 | 32 | class MainGame < Chingu::Window 33 | def initialize 34 | super(640,480,false) 35 | self.input = { :escape => :close } # exits example on Escape 36 | 37 | Chingu::GameObject.create(:x => 200, :y => 200, :image => Image["spaceship.png"]) 38 | Chingu::Text.create("Main game loaded", :y => 100) 39 | end 40 | 41 | def update 42 | super 43 | self.caption = "FPS: #{self.fps} milliseconds_since_last_tick: #{self.milliseconds_since_last_tick}" 44 | end 45 | end 46 | 47 | 48 | SplashScreen.new.show -------------------------------------------------------------------------------- /examples/example27_console.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require File.join(File.dirname($0), "..", "lib", "chingu") 4 | include Gosu 5 | include Chingu 6 | 7 | # 8 | # Chingu::Console tries to emulate how Chingu::Window works but without GFX and keyboardinput 9 | # 10 | class Game < Chingu::Console 11 | 12 | def update 13 | super 14 | sleep(0.02) # fake some cpu intensive game logic :P 15 | puts "Gameloop running at #{fps} FPS. milliseconds since last tick #{dt}." 16 | end 17 | 18 | end 19 | 20 | Game.new.show -------------------------------------------------------------------------------- /examples/example28_networking.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | require File.join(File.dirname($0), "..", "lib", "chingu") 4 | include Gosu 5 | include Chingu 6 | 7 | # 8 | # Simple 9 | # 10 | class Game < Chingu::Window 11 | 12 | def setup 13 | on_input(:esc, :exit) 14 | 15 | @client = Client.new 16 | @server = Server.new 17 | 18 | @server.start("0.0.0.0", 1234) 19 | #@client.connect("192.168.0.1", 1234) 20 | @client.connect("127.0.0.1", 1234) 21 | end 22 | 23 | def update 24 | # 25 | # Since we have 2 games states going we don't switch to them ( i.e. push_game_state @server ) 26 | # Rather we just call #update manually so they can do their thing. 27 | # 28 | # Server#update will poll incoming connections, handle sockets and read/parse incomong data. 29 | # Client#update will read/parse incoming data, send output. 30 | # 31 | @client.update 32 | @server.update 33 | end 34 | 35 | end 36 | 37 | # 38 | # Our Client. We inherit from Chingu::GameStates::NetworkClient 39 | # 40 | class Client < GameStates::NetworkClient 41 | 42 | def on_connect 43 | puts "[#{self.ip}] Connected! Sending a msg..." 44 | send_msg(:hi => :there) 45 | end 46 | 47 | # 48 | # Only 1 argument, the msg, since client only does 1 connecion (the one to the server) 49 | # 50 | def on_msg(msg) 51 | puts "Client Got: #{msg.inspect}" 52 | end 53 | 54 | def on_connection_refused 55 | puts "connection refused" 56 | end 57 | 58 | end 59 | 60 | # 61 | # Our Server. We inherit from Chingu::GameStates::NetworkServer 62 | # 63 | class Server < GameStates::NetworkServer 64 | 65 | # 66 | # Overload on_msg callback in server to make something happen 67 | # Servers #on_msg takes 2 arguments, since server can handle many clients 68 | # 69 | def on_connect(socket) 70 | send_msg(socket, {:hi => :there}) 71 | end 72 | 73 | def on_msg(socket, msg) 74 | puts "Server Got: #{msg.inspect}" 75 | send_msg(socket, {:woff => :mjau, :numbers => [1,2,3]}) 76 | end 77 | 78 | end 79 | 80 | Game.new.show -------------------------------------------------------------------------------- /examples/example29_asynchronous.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | $stderr.sync = $stdout.sync = true 8 | 9 | Chingu::Text.trait :asynchronous 10 | 11 | class BadGuy < Chingu::GameObject 12 | trait :asynchronous 13 | 14 | def fire! 15 | # Create a text object to represent shooting sounds. 16 | text = Chingu::Text.create("Pew!", :x => x, :y => y - height) 17 | 18 | # Now, make that text object fade out and disappear asynchronously. 19 | text.async do |q| 20 | q.tween(750, :alpha => 0, :scale => 2) 21 | q.call :destroy 22 | end 23 | end 24 | 25 | end 26 | 27 | class Game < Chingu::Window 28 | 29 | def initialize 30 | super 480, 240, false 31 | 32 | self.caption = "Press [SPACE] to run the demo" 33 | 34 | # Create our bad guy! 35 | @boss = BadGuy.create :image => 'Starfighter.bmp' 36 | 37 | # Instruct the boss to move along a path while rotating clockwise. 38 | # Nothing is actually done until the run loop calls #update. 39 | @boss.async do |q| 40 | # These tasks are performed sequentially and asynchronously 41 | # using the magic of queues. Each GameObject has its own! 42 | q.wait { button_down? Gosu::KbSpace } 43 | q.tween(1000, :x => 100, :y => 100, :angle => 45) 44 | q.tween(1000, :y => 200, :angle => 90) 45 | q.call :fire! 46 | end 47 | 48 | # Single tasks can also be given with a less verbose syntax. 49 | # Wait a second... 50 | @boss.async.wait 1000 51 | 52 | # Move and fire one more time, just for good measure. 53 | @boss.async do |q| 54 | q.tween(1000, :x => 200, :y => 100, :angle => 360) 55 | 3.times do 56 | q.wait 500 57 | q.call :fire! 58 | end 59 | end 60 | 61 | end 62 | 63 | end 64 | 65 | Game.new.show 66 | -------------------------------------------------------------------------------- /examples/example2_gamestate_basics.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | # 8 | # A little more complicated example where we do our own #update and #draw code. 9 | # We also add another GameObject - a bullet fired with space. 10 | # 11 | # Also tests out the Debug game state. 12 | # 13 | class Game < Chingu::Window 14 | def initialize 15 | # 16 | # See http://www.libgosu.org/rdoc/classes/Gosu/Window.html#M000034 for options 17 | # By default Chingu does 640 x 480 non-fullscreen. 18 | # 19 | super 20 | 21 | push_game_state(Play) 22 | end 23 | end 24 | 25 | # 26 | # Our Player 27 | # 28 | class Player < Chingu::GameObject 29 | def initialize(options = {}) 30 | super 31 | @image = Image["spaceship.png"] 32 | end 33 | 34 | def move_left; @x -= 1; end 35 | def move_right; @x += 1; end 36 | def move_up; @y -= 1; end 37 | def move_down; @y += 1; end 38 | 39 | def fire 40 | Bullet.create(:x => @x, :y => @y) 41 | end 42 | end 43 | 44 | class Bullet < Chingu::GameObject 45 | # 46 | # If we need our own initialize, just call super and Chingu does it's thing. 47 | # Here we merge in an extra argument, specifying the bullet-image. 48 | # 49 | def initialize(options) 50 | super(options.merge(:image => Image["fire_bullet.png"])) 51 | end 52 | 53 | # Move the bullet forward 54 | def update 55 | @y -= 2 56 | end 57 | 58 | end 59 | 60 | class Play < Chingu::GameState 61 | 62 | def initialize 63 | super 64 | @player = Player.create(:x => 200, :y => 200) 65 | 66 | # 67 | # More advanced input-maps, showing of multiple keys leading to the same method 68 | # 69 | @player.input = { [:holding_a, :holding_left, :holding_pad_left] => :move_left, 70 | [:holding_d, :holding_right, :holding_pad_right] => :move_right, 71 | [:holding_w, :holding_up, :holding_pad_up] => :move_up, 72 | [:holding_s, :holding_down, :holding_pad_down] => :move_down, 73 | [:space, :return, :pad_button_2] => :fire 74 | } 75 | self.input = { :f1 => :debug, [:q, :escape] => :exit } 76 | end 77 | 78 | def debug 79 | push_game_state(Chingu::GameStates::Debug.new) 80 | end 81 | 82 | # 83 | # If we want to add extra graphics drawn just define your own draw. 84 | # Be sure to call #super for enabling Chingus autodrawing of instances of GameObject. 85 | # Putting #super before or after the background-draw-call really doesn't matter since Gosu work with "zorder". 86 | # 87 | def draw 88 | # Raw Gosu Image.draw(x,y,zorder)-call 89 | Image["background1.png"].draw(0, 0, 0) 90 | super 91 | end 92 | 93 | # 94 | # Gosus place for gamelogic is #update in the mainwindow 95 | # 96 | # A #super call here would call #update on all Chingu::GameObject-instances and check their inputs, and call the specified method. 97 | # 98 | def update 99 | super 100 | 101 | Bullet.destroy_if { |bullet| bullet.outside_window? } 102 | $window.caption = "FPS: #{$window.fps} - milliseconds_since_last_tick: #{$window.milliseconds_since_last_tick} - game objects# #{current_game_state.game_objects.size} Bullets# #{Bullet.size}" 103 | end 104 | end 105 | 106 | Game.new.show -------------------------------------------------------------------------------- /examples/example3_parallax.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | # 8 | # Parallax-example 9 | # Images from http://en.wikipedia.org/wiki/Parallax_scrolling 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super(600,200) 14 | self.caption = "Chingu::Parallax example. Scroll with left/right arrows. Space for new parallax example!" 15 | switch_game_state(Wood) 16 | end 17 | end 18 | 19 | class Scroller < Chingu::GameState 20 | 21 | def initialize(options = {}) 22 | super(options) 23 | @text_color = Color.new(0xFF000000) 24 | self.input = { :holding_left => :camera_left, 25 | :holding_right => :camera_right, 26 | :holding_up => :camera_up, 27 | :holding_down => :camera_down, 28 | :space => :next_game_state, 29 | :escape => :exit 30 | } 31 | end 32 | 33 | def next_game_state 34 | if current_game_state.class == Wood 35 | switch_game_state(Jungle) 36 | else 37 | switch_game_state(Wood) 38 | end 39 | end 40 | 41 | def camera_left 42 | # This is essentially the same as @parallax.x += 2 43 | @parallax.camera_x -= 2 44 | end 45 | 46 | def camera_right 47 | # This is essentially the same as @parallax.x -= 2 48 | @parallax.camera_x += 2 49 | end 50 | 51 | def camera_up 52 | # This is essentially the same as @parallax.y += 2 53 | @parallax.camera_y -= 2 54 | end 55 | 56 | def camera_down 57 | # This is essentially the same as @parallax.y -= 2 58 | @parallax.camera_y += 2 59 | end 60 | end 61 | 62 | class Wood < Scroller 63 | def setup 64 | @parallax = Chingu::Parallax.create(:x => 150, :y => 150, :rotation_center => :top_left) 65 | @parallax << { :image => "wood.png", :repeat_x => true, :repeat_y => true} 66 | Chingu::Text.create("82x64 image with repeat_x and repeat_y set to TRUE", :x => 0, :y => 0, :size => 30, :color => @text_color) 67 | end 68 | end 69 | 70 | class Jungle < Scroller 71 | def initialize(options = {}) 72 | super 73 | @parallax = Chingu::Parallax.create(:x => 0, :y => 0, :rotation_center => :top_left) 74 | 75 | # 76 | # If no :zorder is given to @parallax.add_layer it defaults to first added -> lowest zorder 77 | # Everywhere the :image argument is used, theese 2 values are the Same: 78 | # 1) Image["foo.png"] 2) "foo.png" 79 | # 80 | # Notice we add layers to the parallax scroller in 3 different ways. 81 | # They all end up as ParallaxLayer-instances internally 82 | # 83 | @parallax.add_layer(:image => "Parallax-scroll-example-layer-0.png", :damping => 100) 84 | @parallax.add_layer(:image => "Parallax-scroll-example-layer-1.png", :damping => 10) 85 | @parallax << Chingu::ParallaxLayer.new(:image => "Parallax-scroll-example-layer-2.png", :damping => 5, :parallax => @parallax) 86 | @parallax << {:image => "Parallax-scroll-example-layer-3.png", :damping => 1} 87 | 88 | Chingu::Text.create("Multiple layers with repeat_x set to TRUE", :x => 0, :y => 0, :size => 30, :color => @text_color) 89 | end 90 | end 91 | 92 | Game.new.show -------------------------------------------------------------------------------- /examples/example5_gamestates_in_pure_gosu.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | # 8 | # Using Chingus game state mananger in pure Gosu. 9 | # 10 | class Game < Gosu::Window 11 | attr_reader :font 12 | def initialize 13 | $window = super(800,600,false) 14 | 15 | @font = Font.new($window, default_font_name(), 20) 16 | 17 | # Create our game state manager 18 | @manager = Chingu::GameStateManager.new 19 | 20 | # Switch to first state 21 | @manager.switch_game_state(State1) 22 | 23 | # Insert FadeTo state between every push, pop and switch 24 | @manager.transitional_game_state(Chingu::GameStates::FadeTo, :speed => 10, :game_state_manager => @manager) 25 | end 26 | 27 | def button_down(id) 28 | # This makes sure button_down(id) is called on the active game state 29 | # Enables input-handling in game states, you might wanna do the same with button_up() 30 | @manager.button_down(id) 31 | 32 | if @manager.current_game_state.class != Chingu::GameStates::FadeTo 33 | 34 | @manager.push_game_state(State1) if(id==Button::Kb1) 35 | @manager.push_game_state(State2) if(id==Button::Kb2) 36 | @manager.push_game_state(State3) if(id==Button::Kb3) 37 | @manager.push_game_state(Chingu::GameStates::Pause) if(id==Button::KbP) 38 | @manager.pop_game_state if(id==Button::KbBackspace) 39 | end 40 | 41 | exit if(id==Button::KbEscape) 42 | end 43 | 44 | def update 45 | # This makes sure update() is called on the active game state 46 | @manager.update 47 | end 48 | 49 | def draw 50 | @font.draw("Game State Stack. 1-3 to push a game state. Backspace to pop.", 100, 200, 0) 51 | @manager.game_states.each_with_index do |game_state, index| 52 | @font.draw("#{index+1}) #{game_state.to_s}", 100, 220+index*20, 0) 53 | end 54 | 55 | # This makes sure draw() is called on the active game state 56 | @manager.draw 57 | end 58 | end 59 | 60 | class State1 < Chingu::GameState 61 | def setup 62 | @spinner = ["|", "/", "-", "\\", "|", "/", "-", "\\"] 63 | @spinner_index = 0.0 64 | end 65 | 66 | def update 67 | @spinner_index += 0.1 68 | @spinner_index = 0 if @spinner_index >= @spinner.size 69 | end 70 | 71 | def draw 72 | $window.font.draw("Inside State1: #{@spinner[@spinner_index.to_i]}", 100, 100, 0) 73 | end 74 | end 75 | 76 | class State2 < Chingu::GameState 77 | def setup 78 | @factor = 0.0 79 | @ticks = 0.0 80 | end 81 | 82 | def update 83 | @ticks += 0.01 84 | @factor = 1.5 + Math.sin(@ticks).to_f 85 | end 86 | 87 | def draw 88 | $window.font.draw("Inside State2 - factor_y: #{@factor.to_s}", 100, 100, 0, 1.0, @factor) 89 | end 90 | end 91 | 92 | 93 | class State3 < Chingu::GameState 94 | def setup 95 | @factor = 0.0 96 | @ticks = 0.0 97 | end 98 | 99 | def update 100 | @ticks += 0.01 101 | @factor = 1.5 + Math.sin(@ticks).to_f 102 | end 103 | def draw 104 | $window.font.draw("Inside State3 - factor_x: #{@factor.to_s}", 100, 100, 0, @factor, 1.0) 105 | end 106 | end 107 | 108 | 109 | Game.new.show -------------------------------------------------------------------------------- /examples/example6_transitional_game_state.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | # 8 | # Example of using a special GameState to fade between game states 9 | # 10 | # Using a simple game state, Chingu::GameStates::FadeTo, shipped with Chingu. 11 | # 12 | class Game < Chingu::Window 13 | def initialize 14 | super(640,800) 15 | switch_game_state(State1) 16 | self.input = {:space => :push, :return => :switch, :esc => :exit} 17 | self.caption = "Example of transitional game state FadeTo when switchin between two game states" 18 | transitional_game_state(Chingu::GameStates::FadeTo, {:speed => 5, :debug => true}) 19 | end 20 | 21 | def push 22 | # 23 | # Since we have a transitional game state set, the bellow code in practice become: 24 | # 25 | # if current_game_state.is_a?(State1) 26 | # push_game_state(Chingu::GameStates::FadeTo.new(State2.new, :speed => 10)) 27 | # elsif current_game_state.is_a?(State2) 28 | # push_game_state(Chingu::GameStates::FadeTo.new(State1.new, :speed => 10)) 29 | # end 30 | # 31 | if current_game_state.is_a?(State1) 32 | push_game_state(State2.new) 33 | elsif current_game_state.is_a?(State2) 34 | push_game_state(State1.new) 35 | end 36 | end 37 | 38 | def switch 39 | if current_game_state.is_a?(State1) 40 | switch_game_state(State2.new) 41 | elsif current_game_state.is_a?(State2) 42 | switch_game_state(State1.new) 43 | end 44 | end 45 | end 46 | 47 | class State1 < Chingu::GameState 48 | 49 | # 50 | # This is another way of achieving the same thing as the out-commeted draw-code 51 | # Since .create is used, it's automatically updated and drawn 52 | # 53 | def initialize(options = {}) 54 | super 55 | Chingu::GameObject.create(:image => "ruby.png", :rotation_center => :top_left) 56 | end 57 | 58 | #def draw 59 | # Image["ruby.png"].draw(0,0,0) 60 | #end 61 | end 62 | 63 | class State2 < Chingu::GameState 64 | def draw 65 | Image["video_games.png"].draw(0,0,0) 66 | end 67 | end 68 | 69 | Game.new.show -------------------------------------------------------------------------------- /examples/example8_traits.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Demonstrating traits "velocity" and "effect" 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super(640,400) 14 | self.input = {:esc => :exit} 15 | self.caption = "Example of game object traits 'velocity' and 'effect'" 16 | push_game_state(Particles) 17 | end 18 | 19 | def next_effect; pop_game_state; end 20 | end 21 | 22 | class Plasma < Chingu::GameObject 23 | traits :velocity 24 | attr_accessor :fade_rate 25 | 26 | def setup 27 | @image = Image["particle.png"] 28 | @mode = :additive 29 | 30 | # initialize with a rightwards velocity with some damping to look more realistic 31 | @velocity_x = options[:velocity_x] || 10 32 | @acceleration_x = -0.1 33 | 34 | # Simulate gravity 35 | @acceleration_y = 0.4 36 | end 37 | 38 | def update 39 | self.alpha -= @fade_rate if defined?(@fade_rate) 40 | end 41 | end 42 | 43 | class Particles < Chingu::GameState 44 | def setup 45 | @color1 = Color.new(0xFFFFEA02) 46 | @color2 = Color.new(0xFF078B20) 47 | 48 | # 49 | # +1 fps 50 | # 51 | #@ground_y = $window.height * 0.95 52 | @ground_y = ($window.height * 0.95).to_i 53 | end 54 | 55 | def update 56 | 57 | # 58 | # old velocity.rb 350 particles, 49 fps 59 | # first optimization: 490 particles, 47 fps (350 @ 60) 60 | # optimized GameObject if/elsif: 490 particles, 50 fps 61 | # 62 | Plasma.create(:x => 0, :y => 0 + rand(5), :color => Color::RED.dup, :velocity_x => 10) 63 | Plasma.create(:x => 0, :y => 50 + rand(5), :color => Color.new(0xFF86EFFF), :velocity_x => 14) 64 | Plasma.create(:x => 0, :y => 100 + rand(5), :color => Color.new(0xFF86EFFF), :velocity_x => 7) 65 | Plasma.create(:x => 0, :y => 200 + rand(5), :color => Color.new(0xFF86EFFF), :velocity_x => 6) 66 | 67 | Plasma.each do |particle| 68 | # 69 | # +1 fps 70 | # 71 | # particle.x += 1 - rand(2) 72 | # -just removed, not replaced- 73 | 74 | # 75 | # If particle hits the ground: 76 | # 77 | if particle.y >= @ground_y 78 | 79 | # 1) "Bounce" it up particle by reversing velocity_y with damping 80 | slower = particle.velocity_y/3 81 | particle.velocity_y = -(slower + rand(slower)) 82 | 83 | # 2) "Bounce" it randomly to left and right 84 | if rand(2) == 0 85 | particle.velocity_x = particle.velocity_y/2 + rand(2) # Randomr.randomr / 50 86 | particle.acceleration_x = -0.02 87 | else 88 | particle.velocity_x = -particle.velocity_y/2 - rand(2) # Randomr.randomr / 50 89 | particle.acceleration_x = 0.02 90 | end 91 | 92 | # 3) Start fading the alphachannel 93 | particle.fade_rate = 3 94 | end 95 | end 96 | 97 | # 98 | # +4 fps 99 | # 100 | #self.game_objects.reject! { |object| object.outside_window? || object.color.alpha == 0 } 101 | self.game_objects.destroy_if { |object| object.color.alpha == 0 } 102 | 103 | super 104 | end 105 | 106 | def draw 107 | $window.caption = "particle example (esc to quit) [particles#: #{game_objects.size} - framerate: #{$window.fps}]" 108 | fill_gradient(:from => Color.new(255,0,0,0), :to => Color.new(255,60,60,80), :rect => [0,0,$window.width,@ground_y]) 109 | fill_gradient(:from => Color.new(255,100,100,100), :to => Color.new(255,50,50,50), :rect => [0,@ground_y,$window.width,$window.height-@ground_y]) 110 | super 111 | end 112 | end 113 | 114 | Game.new.show 115 | -------------------------------------------------------------------------------- /examples/example9_collision_detection.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | include Chingu 7 | 8 | # 9 | # Demonstrating traits "velocity" and "collision_detection" 10 | # 11 | class Game < Chingu::Window 12 | def initialize 13 | super(800,800) 14 | self.input = {:esc => :exit} 15 | self.caption = "Example of game object traits 'velocity' and 'effect'" 16 | push_game_state(ParticleState) 17 | end 18 | 19 | def next_effect; pop_game_state; end 20 | end 21 | 22 | class FireCube < Chingu::GameObject 23 | traits :velocity, :collision_detection, :bounding_circle 24 | attr_accessor :color 25 | 26 | def initialize(options) 27 | super 28 | @mode = :additive 29 | 30 | @image = Image["circle.png"] 31 | 32 | # initialize with a rightwards velocity with some damping to look more realistic 33 | self.velocity_x = options[:velocity_x] || 1 + rand(2) 34 | self.velocity_y = options[:velocity_y] || 1 + rand(2) 35 | self.factor = 2 36 | 37 | @color = Color::BLUE 38 | 39 | cache_bounding_circle # This does a lot for performance 40 | end 41 | 42 | def update 43 | @color = Color::BLUE 44 | end 45 | 46 | def die! 47 | @color = Color::RED 48 | end 49 | 50 | end 51 | 52 | class ParticleState < Chingu::GameState 53 | def setup 54 | self.input = { :space => :new_fire_cube } 55 | 100.times { new_fire_cube } 56 | end 57 | 58 | def new_fire_cube 59 | FireCube.create(:x => rand($window.width), :y => rand($window.height)) 60 | end 61 | 62 | def update 63 | super 64 | 65 | FireCube.each do |particle| 66 | if particle.x < 0 || particle.x > $window.width 67 | particle.velocity_x = -particle.velocity_x 68 | end 69 | 70 | if particle.y < 0 || particle.y > $window.height 71 | particle.velocity_y = -particle.velocity_y 72 | end 73 | end 74 | 75 | # 76 | # GameObject.each_collsion / each_bounding_box_collision wont collide an object with itself 77 | # 78 | # FireCube.each_bounding_circle_collision(FireCube) do |cube1, cube2| # 30 FPS on my computer 79 | # 80 | # Let's see if we can optimize each_collision, starts with 19 FPS with radius collision 81 | # 30 FPS by checking for radius and automatically delegate to each_bounding_circle_collision 82 | # 83 | # For bounding_box collision we start out with 7 FPS 84 | # Got 8 FPS, the bulk CPU consumtion is in the rect vs rect check, not in the loops. 85 | # 86 | FireCube.each_collision(FireCube) do |cube1, cube2| 87 | cube1.die! 88 | cube2.die! 89 | end 90 | 91 | end 92 | 93 | def draw 94 | $window.caption = "radius based iterative collision detection. Particles#: #{game_objects.size}, Collisionchecks each gameloop: ~#{game_objects.size**2} - FPS: #{$window.fps}" 95 | super 96 | end 97 | end 98 | 99 | Game.new.show 100 | -------------------------------------------------------------------------------- /examples/high_score_list.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - :score: 10197 3 | :text: from example13.rb 4 | :name: NEW 5 | - :score: 10187 6 | :text: from example13.rb 7 | :name: NEW 8 | - :score: 10177 9 | :text: from example13.rb 10 | :name: NEW 11 | - :score: 10167 12 | :text: from example13.rb 13 | :name: NEW 14 | - :score: 10157 15 | :text: from example13.rb 16 | :name: NEW 17 | - :score: 10147 18 | :text: from example13.rb 19 | :name: NEW 20 | - :score: 10137 21 | :text: from example13.rb 22 | :name: NEW 23 | - :score: 10127 24 | :text: from example13.rb 25 | :name: NEW 26 | - :score: 10117 27 | :text: from example13.rb 28 | :name: NEW 29 | - :score: 10112 30 | :text: from example13.rb 31 | :name: NEW 32 | - :score: 10107 33 | :text: from example13.rb 34 | :name: NEW 35 | -------------------------------------------------------------------------------- /examples/media/Parallax-scroll-example-layer-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/Parallax-scroll-example-layer-0.png -------------------------------------------------------------------------------- /examples/media/Parallax-scroll-example-layer-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/Parallax-scroll-example-layer-1.png -------------------------------------------------------------------------------- /examples/media/Parallax-scroll-example-layer-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/Parallax-scroll-example-layer-2.png -------------------------------------------------------------------------------- /examples/media/Parallax-scroll-example-layer-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/Parallax-scroll-example-layer-3.png -------------------------------------------------------------------------------- /examples/media/Star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/Star.png -------------------------------------------------------------------------------- /examples/media/Starfighter.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/Starfighter.bmp -------------------------------------------------------------------------------- /examples/media/background1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/background1.png -------------------------------------------------------------------------------- /examples/media/battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/battery.png -------------------------------------------------------------------------------- /examples/media/big_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/big_star.png -------------------------------------------------------------------------------- /examples/media/big_stone_wall.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/big_stone_wall.bmp -------------------------------------------------------------------------------- /examples/media/black_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/black_block.png -------------------------------------------------------------------------------- /examples/media/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/bullet.png -------------------------------------------------------------------------------- /examples/media/bullet_hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/bullet_hit.wav -------------------------------------------------------------------------------- /examples/media/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/circle.png -------------------------------------------------------------------------------- /examples/media/city1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/city1.png -------------------------------------------------------------------------------- /examples/media/city2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/city2.png -------------------------------------------------------------------------------- /examples/media/city3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/city3.png -------------------------------------------------------------------------------- /examples/media/cog_wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/cog_wheel.png -------------------------------------------------------------------------------- /examples/media/droid.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/droid.bmp -------------------------------------------------------------------------------- /examples/media/droid_11x15.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/droid_11x15.bmp -------------------------------------------------------------------------------- /examples/media/droid_11x15.gal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/droid_11x15.gal -------------------------------------------------------------------------------- /examples/media/enemy_bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/enemy_bullet.png -------------------------------------------------------------------------------- /examples/media/enemy_plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/enemy_plane.png -------------------------------------------------------------------------------- /examples/media/explosion.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/explosion.wav -------------------------------------------------------------------------------- /examples/media/fire_bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/fire_bullet.png -------------------------------------------------------------------------------- /examples/media/fireball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/fireball.png -------------------------------------------------------------------------------- /examples/media/heli.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/heli.bmp -------------------------------------------------------------------------------- /examples/media/heli.gal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/heli.gal -------------------------------------------------------------------------------- /examples/media/laser.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/laser.wav -------------------------------------------------------------------------------- /examples/media/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/particle.png -------------------------------------------------------------------------------- /examples/media/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/plane.png -------------------------------------------------------------------------------- /examples/media/rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/rect.png -------------------------------------------------------------------------------- /examples/media/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/ruby.png -------------------------------------------------------------------------------- /examples/media/saucer.gal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/saucer.gal -------------------------------------------------------------------------------- /examples/media/saucer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/saucer.png -------------------------------------------------------------------------------- /examples/media/saw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/saw.png -------------------------------------------------------------------------------- /examples/media/spaceship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/spaceship.png -------------------------------------------------------------------------------- /examples/media/star_25x25_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/star_25x25_default.png -------------------------------------------------------------------------------- /examples/media/star_25x25_explode.gal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/star_25x25_explode.gal -------------------------------------------------------------------------------- /examples/media/star_25x25_explode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/star_25x25_explode.png -------------------------------------------------------------------------------- /examples/media/stone_wall.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/stone_wall.bmp -------------------------------------------------------------------------------- /examples/media/talk_bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/talk_bubble.png -------------------------------------------------------------------------------- /examples/media/tube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/tube.png -------------------------------------------------------------------------------- /examples/media/video_games.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/video_games.png -------------------------------------------------------------------------------- /examples/media/wood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/examples/media/wood.png -------------------------------------------------------------------------------- /examples/tests/holding_a_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | class Game < Chingu::Window 8 | def initialize 9 | super(640,480,false) # leave it blank and it will be 800,600,non fullscreen 10 | self.input = { :escape => :exit } # exits example on Escape 11 | 12 | @player = Player.create(:x => 200, :y => 200, :image => Image["spaceship.png"]) 13 | @player.input = [ :holding_left, :holding_right, :holding_up, :holding_down, :holding_a ] 14 | end 15 | 16 | def update 17 | super 18 | self.caption = "FPS: #{self.fps} milliseconds_since_last_tick: #{self.milliseconds_since_last_tick}" 19 | end 20 | end 21 | 22 | class Player < Chingu::GameObject 23 | def holding_left; @x -= 3; end 24 | def holding_right; @x += 3; end 25 | def holding_up; @y -= 3; end 26 | def holding_down; @y += 3; end 27 | def holding_a 28 | Bullet.create(:x => @x, :y => @y) 29 | end 30 | end 31 | 32 | class Bullet < Chingu::GameObject 33 | def setup 34 | @image = Image["fire_bullet.png"] 35 | end 36 | 37 | def update 38 | @y -= 2 39 | end 40 | end 41 | 42 | 43 | Game.new.show -------------------------------------------------------------------------------- /examples/tests/tool1_input_codes.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' rescue nil 3 | $LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib") 4 | require 'chingu' 5 | include Gosu 6 | 7 | # Show the user which Gosu codes and Chingu symbols that each key maps to. 8 | class Game < Chingu::Window 9 | def initialize 10 | super(640,480,false) # leave it blank and it will be 800,600,non fullscreen 11 | self.caption = "Press a key or mouse/gamepad button to see the input code" 12 | 13 | @gosu_text = Chingu::Text.create('Gosu code (Fixnum) =>', :x => 25, :y => 200, :size => 18) 14 | @chingu_text = Chingu::Text.create('Chingu name(s) (Symbol) =>', :x => 25, :y => 250, :size => 18) 15 | 16 | input_names = Gosu.constants.select {|constant| constant =~ /^(?:Kb|Ms|Gp)/ } 17 | input_names.delete_if {|name| name.to_s =~ /Range(?:Start|End)|Num$/ } # Special entries. 18 | @gosu_inputs = input_names.inject({}) {|hash, name| hash[Gosu.const_get name] = name; hash } 19 | 20 | # Get all possible key codes. 21 | @key_codes = (Gosu::KbRangeBegin..Gosu::KbRangeEnd).to_a + (Gosu::GpRangeBegin..Gosu::GpRangeEnd).to_a + (Gosu::MsRangeBegin..Gosu::MsRangeEnd).to_a 22 | end 23 | 24 | def update 25 | super 26 | 27 | @code = nil 28 | @key_codes.each do |code| 29 | if button_down?(code) 30 | @gosu_text.text = "Gosu code (Fixnum) => #{code} (Gosu::#{@gosu_inputs[code] || ""})" 31 | symbols = Chingu::Input::CONSTANT_TO_SYMBOL[code].map {|s| s.inspect }.join(", ") rescue "" 32 | @chingu_text.text = "Chingu name(s) (Symbol) => #{symbols}" 33 | break 34 | end 35 | end 36 | end 37 | end 38 | 39 | 40 | 41 | Game.new.show -------------------------------------------------------------------------------- /lib/chingu.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | CHINGU_ROOT = File.dirname(File.expand_path(__FILE__)) 23 | ROOT = File.dirname(File.expand_path($0)) 24 | 25 | require 'rubygems' unless RUBY_VERSION =~ /1\.9/ 26 | require 'gosu' 27 | require 'yaml' 28 | require 'timeout' 29 | require 'socket' 30 | require File.join(CHINGU_ROOT,"chingu","require_all") # Thanks to http://github.com/tarcieri/require_all ! 31 | 32 | # Seems like we need to include chingu/helpers first for BasicGameObject 33 | # and GameObject to get the correct class_inheritable_accssor 34 | require_all "#{CHINGU_ROOT}/chingu/helpers" 35 | require_all "#{CHINGU_ROOT}/chingu/traits" 36 | require_all "#{CHINGU_ROOT}/chingu/async" 37 | require_all "#{CHINGU_ROOT}/chingu/async_tasks" 38 | require_all "#{CHINGU_ROOT}/chingu" 39 | require_all "#{CHINGU_ROOT}/chingu/version" 40 | 41 | module Chingu 42 | DEBUG_COLOR = Gosu::Color.new(0xFFFF0000) 43 | DEBUG_ZORDER = 9999 44 | INFINITY = 1.0 / 0 45 | end 46 | -------------------------------------------------------------------------------- /lib/chingu/assets.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Rubygames Named Resources for GOSU 3 | # Assumes a global variable $window having the Gosu::Window instance. 4 | # Quick 'n easy access to sprites, sounds and tiles! 5 | # 6 | module Chingu 7 | def media_path(file) 8 | File.join(ROOT, "media", file) 9 | end 10 | 11 | def root_path(file) 12 | File.join(ROOT, file) 13 | end 14 | 15 | def image_path(file) 16 | File.join(ROOT, "images", file) 17 | end 18 | 19 | class Asset 20 | include Chingu::NamedResource 21 | 22 | def self.autoload(name) 23 | find_file(name) 24 | end 25 | end 26 | end 27 | 28 | # 29 | # Extend GOSU's core classes with NamedResource 30 | # 31 | module Gosu 32 | class Image 33 | include Chingu::NamedResource 34 | 35 | def self.autoload(name) 36 | ret = (path = find_file(name)) ? Gosu::Image.new($window, path, false) : nil 37 | raise "Can't load image \"#{name}\"" if ret.nil? 38 | return ret 39 | end 40 | end 41 | 42 | class Font 43 | # Font is a special case, since it has both a name and a size. 44 | include Chingu::NamedResource 45 | 46 | # Load a font with the given name and size. 47 | # @param [String] name Name of the font (or path to TTF font) 48 | # @param [Number] size Size of the font. 49 | def self.autoload(name, size) 50 | font_name = if path = find_file(name) 51 | path # Use the full path, found in the autoload dirs. 52 | else 53 | name # Font not found in the path. Assume it is an OS font. 54 | end 55 | 56 | return Gosu::Font.new($window, font_name, size) 57 | end 58 | 59 | # @overload self.[](name, size) 60 | # Get a font with the given name and size. 61 | # @param [String] name Name of the font (or path to TTF font) 62 | # @param [Number] size Size of the font. 63 | # 64 | # @overload self.[](size) 65 | # Get a font of a given size using the Gosu.default_font_name. 66 | # @param [Number] size Size of the font. 67 | def self.[]( *args ) 68 | case args.size 69 | when 1 70 | name = Gosu.default_font_name 71 | size = args[0] 72 | when 2 73 | name, size = args 74 | else 75 | raise ArgumentError, "wrong number of arguments (#{args.size} for 1 or 2)" 76 | end 77 | 78 | result = @resources[[name, size]] 79 | 80 | if result.nil? 81 | result = autoload(name, size) 82 | if result 83 | self[name, size] = result 84 | result.name = name 85 | end 86 | end 87 | 88 | return result 89 | end 90 | 91 | # Save a font with the given name and size. 92 | # @param [String] name Name of the font (or path to TTF font) 93 | # @param [Number] size Size of the font. 94 | # @param [Gosu::Font] font Font object to save. 95 | def self.[]=( name, size, font ) 96 | @resources[[name, size]] = font 97 | end 98 | end 99 | 100 | class Song 101 | include Chingu::NamedResource 102 | 103 | def self.autoload(name) 104 | (path = find_file(name)) ? Gosu::Song.new(path) : nil 105 | end 106 | end 107 | 108 | class Sample 109 | include Chingu::NamedResource 110 | 111 | def self.autoload(name) 112 | (path = find_file(name)) ? Gosu::Sample.new(path) : nil 113 | end 114 | end 115 | Sound = Sample # Gosu uses Sample, but Sound makes sense too. 116 | end 117 | -------------------------------------------------------------------------------- /lib/chingu/async/basic_task.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | require 'weakref' 23 | 24 | module Chingu 25 | module Async 26 | 27 | class BasicTask 28 | 29 | def initialize 30 | end 31 | 32 | # 33 | # Returns true if the task has finished executing. The meaning of 34 | # "finished" is determined by the particular subclass. 35 | # 36 | def finished? 37 | true 38 | end 39 | 40 | def update(object) 41 | end 42 | 43 | end 44 | 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/chingu/async/task_builder.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Async 24 | 25 | # 26 | # Implements a DSL for appending new tasks to an task queue. 27 | # 28 | class TaskBuilder 29 | def initialize(tasks) 30 | @tasks = tasks 31 | end 32 | 33 | # 34 | # Add a new task to the queue. The first argument is a Symbol or 35 | # String naming the type of task; remaining arguments are passed 36 | # on to the task's constructor. 37 | # 38 | # If a block is supplied, it is scheduled to be executed as soon as the 39 | # task is finished. 40 | # 41 | def task(task, *args, &block) 42 | case task 43 | when Symbol, String 44 | klass_name = Chingu::Inflector.camelize(task) 45 | klass = Chingu::AsyncTasks.const_get(klass_name) 46 | 47 | task = klass.new(*args, &block) 48 | 49 | when Chingu::Async::BasicTask 50 | # pass 51 | 52 | when Class 53 | task = task.new(*args, &block) 54 | 55 | else raise TypeError, "task must be a Task object or task name" 56 | end 57 | 58 | @tasks.enq(task) 59 | task 60 | end 61 | 62 | # 63 | # Attempting to invoke a nonexistant method automatically calls 64 | # +task+ with the method name as the task type. 65 | # 66 | alias :method_missing :task 67 | 68 | end 69 | 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/chingu/async/task_list.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Async 24 | 25 | class TaskList 26 | # extend Forwardable 27 | # def_delegator :@queue, :push, :enq 28 | # def_delegator :@queue, :shift, :deq 29 | # def_delegator :@queue, :first, :front 30 | # def_delegator :@queue, :clear 31 | 32 | def initialize 33 | @queue = [] 34 | end 35 | 36 | # 37 | # Processes the first task on the queue, each tick removing the 38 | # task once it has finished. 39 | # 40 | def update(object) 41 | if task = front 42 | task.update object 43 | deq if task.finished? 44 | task 45 | end 46 | end 47 | 48 | def front 49 | @queue.first 50 | end 51 | 52 | def enq(*tasks) 53 | @queue.push(*tasks) 54 | end 55 | 56 | def deq 57 | @queue.shift 58 | end 59 | 60 | def clear 61 | @queue.clear 62 | end 63 | 64 | end 65 | 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/chingu/async_tasks/call.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module AsyncTasks 24 | 25 | # 26 | # Single method call as an asynchronous task. 27 | # 28 | class Call < Chingu::Async::BasicTask 29 | 30 | def initialize(method, *args) 31 | super() 32 | @method, @args = method, args 33 | @finished = false 34 | end 35 | 36 | def update(object) 37 | object.send(@method, *@args) 38 | @finished = true 39 | end 40 | 41 | def finished? 42 | !!@finished 43 | end 44 | 45 | end 46 | 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/chingu/async_tasks/exec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module AsyncTasks 24 | 25 | # 26 | # Block execution as an asynchronous task. 27 | # 28 | class Exec < Chingu::Async::BasicTask 29 | 30 | def initialize(&block) 31 | super() 32 | @block = block 33 | @finished = false 34 | end 35 | 36 | def update(object) 37 | @block[] 38 | @finished = true 39 | end 40 | 41 | def finished? 42 | !!@finished 43 | end 44 | 45 | end 46 | 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/chingu/async_tasks/move.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | require "#{CHINGU_ROOT}/chingu/async_tasks/tween" 23 | 24 | module Chingu 25 | module AsyncTasks 26 | 27 | # 28 | # Syntactic sugar for tween(duration, :x => x, :y => y, :angle => angle) 29 | # 30 | class Move < Tween 31 | 32 | def initialize(duration, x, y, angle = nil) 33 | properties = { } 34 | 35 | properties[:x] = x unless x.nil? 36 | properties[:y] = y unless y.nil? 37 | properties[:angle] = angle unless angle.nil? 38 | 39 | super(duration, properties) 40 | end 41 | 42 | end 43 | 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/chingu/async_tasks/parallel.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module AsyncTasks 24 | 25 | # 26 | # Executes all subtasks in parallel. 27 | # 28 | class Parallel < Chingu::Async::BasicTask 29 | 30 | def initialize(&block) 31 | @subtasks = [] 32 | 33 | # Make @subtasks behave like a TaskList for the TaskBuilder. 34 | # This is probably a dirty hack! 35 | class <<@subtasks 36 | alias :enq :push 37 | alias :deq :shift 38 | alias :front :first 39 | end 40 | 41 | add_tasks(&block) 42 | end 43 | 44 | def add_tasks(&block) 45 | builder = Chingu::Async::TaskBuilder.new(@subtasks) 46 | block[builder] 47 | end 48 | 49 | # 50 | # Returns true if all subtasks have finished executing. 51 | # 52 | def finished? 53 | @subtasks.empty? or @subtasks.all?(&:finished?) 54 | end 55 | 56 | def update(object) 57 | @subtasks.each { |task| task.update(object) } 58 | end 59 | 60 | end 61 | 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/chingu/async_tasks/tween.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module AsyncTasks 24 | 25 | # 26 | # Basic tweening for numerical properties. 27 | # 28 | # game_object.async.tween 1000, :property => new_value 29 | # 30 | class Tween < Chingu::Async::BasicTask 31 | 32 | # TODO Tweening is pretty dumb...make it smarter. 33 | 34 | def initialize(duration, properties) 35 | super() 36 | @have_initial_values = false 37 | @age, @life = 0, duration 38 | @properties = properties 39 | end 40 | 41 | def update(object) 42 | set_initial_values(object) unless have_initial_values? 43 | 44 | @age += $window.milliseconds_since_last_tick 45 | t = @age.to_f / @life 46 | t = 1.0 if t > 1 47 | @properties.each do |name, range| 48 | object.send "#{name}=", range.interpolate(t) 49 | end 50 | end 51 | 52 | def finished? 53 | @age >= @life 54 | end 55 | 56 | private 57 | 58 | def set_initial_values(object) 59 | # TODO find a better and more general way to set up tweening. 60 | @properties.each do |name, value| 61 | @properties[name] = object.send(name) .. value 62 | end 63 | @have_initial_values = true 64 | end 65 | 66 | def have_initial_values? 67 | !!@have_initial_values 68 | end 69 | 70 | end 71 | 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/chingu/async_tasks/wait.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module AsyncTasks 24 | 25 | # 26 | # Halts processing of tasks until the passed-in block returns a 27 | # true value, or the timeout expires. If no block is given, behaves as if 28 | # the block returned nil. 29 | # 30 | class Wait < Chingu::Async::BasicTask 31 | 32 | attr_accessor :timeout, :condition 33 | attr_reader :result 34 | 35 | def initialize(timeout = nil, &condition) 36 | super() 37 | @result = nil 38 | @age, @life = 0, timeout 39 | @condition = condition 40 | end 41 | 42 | def update(object) 43 | @age += $window.milliseconds_since_last_tick 44 | @result = (@condition and @condition[]) 45 | end 46 | 47 | def finished? 48 | !!@result or (@life != nil and @age >= @life) 49 | end 50 | 51 | end 52 | 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/chingu/console.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | # 24 | # Console is the non-gfx variant of 25 | # 26 | # 27 | class Console 28 | include Chingu::Helpers::FPSCounter # Adds FPSCounter delegators 29 | include Chingu::Helpers::GameState # Easy access to the global game state-queue 30 | include Chingu::Helpers::GameObject # Adds game_objects_of_class etc ... 31 | 32 | attr_reader :root, :game_state_manager, :game_objects, :milliseconds_since_last_tick, :factor 33 | 34 | def initialize(update_interval = 16.666666) 35 | $window = self 36 | @update_interval = update_interval 37 | @root = File.dirname(File.expand_path($0)) 38 | @game_objects = GameObjectList.new 39 | @fps_counter = FPSCounter.new 40 | @game_state_manager = GameStateManager.new 41 | @milliseconds_since_last_tick = 0 42 | @factor = 1 43 | setup 44 | end 45 | 46 | # 47 | # This is our "game-loop". Will loop forever, with framerate specified and call update() 48 | # The idea is to be very simular to how a Chingu::Window works. 49 | # 50 | def start 51 | loop do 52 | t1 = Time.now 53 | update 54 | t2 = Time.now 55 | update_duration = t2 - t1 56 | 57 | milliseconds = (@update_interval/1000 - update_duration) 58 | sleep(milliseconds) if milliseconds > 0 59 | end 60 | end 61 | alias :show :start 62 | 63 | # Placeholder to be overwritten 64 | def setup; end; 65 | 66 | # 67 | # Returns self inside GameState.initialize (a game state is not 'active' inside initialize()) 68 | # Or returns current active game state (as in a switched to or pushed game state) 69 | # ... Falls back to returning $window 70 | # 71 | # current_scope is used to make GameObject.all and friends work everywhere. 72 | # 73 | def current_scope 74 | game_state_manager.inside_state || game_state_manager.current_game_state || self 75 | end 76 | 77 | # 78 | # Chingus core-logic / loop. Gosu will call this each game-iteration. 79 | # 80 | def update 81 | # 82 | # Register a tick with our rather standard tick/framerate counter. 83 | # Returns the amount of milliseconds since last tick. This number is used in all update()-calls. 84 | # Without this self.fps would return an incorrect value. 85 | # If you override this in your Chingu::Window class, make sure to call super. 86 | # 87 | @milliseconds_since_last_tick = @fps_counter.register_tick 88 | 89 | intermediate_update 90 | end 91 | 92 | # 93 | # "game logic" update that is safe to call even between Gosus update-calls 94 | # 95 | def intermediate_update 96 | # 97 | # Call update() on all game objects belonging to the main window. 98 | # 99 | @game_objects.update 100 | 101 | # 102 | # Call update() on all game objects belonging to the current game state. 103 | # 104 | 105 | # 106 | # Call update() on our game_state_manger 107 | # -> call update on active states 108 | # -> call update on all game objects in that state 109 | # 110 | @game_state_manager.update 111 | end 112 | end 113 | end -------------------------------------------------------------------------------- /lib/chingu/core_ext/array.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | 3 | def each_collision(*klasses) 4 | self.each do |object1| 5 | object1.each_collision(*klasses) do |object, object2| 6 | yield object, object2 7 | end 8 | end 9 | end 10 | 11 | def each_bounding_circle_collision(*klasses) 12 | self.each do |object1| 13 | object1.each_bounding_circle_collision(*klasses) do |object1, object2| 14 | yield object1, object2 15 | end 16 | end 17 | end 18 | 19 | def each_bounding_box_collision(*klasses) 20 | self.each do |object1| 21 | object1.each_bounding_box_collision(*klasses) do |object1, object2| 22 | yield object1, object2 23 | end 24 | end 25 | end 26 | 27 | def each_at(x, y) 28 | self.each do |object| 29 | object.each_at(x, y) do |obj| 30 | yield obj 31 | end 32 | end 33 | end 34 | 35 | end -------------------------------------------------------------------------------- /lib/chingu/core_ext/range.rb: -------------------------------------------------------------------------------- 1 | class Range 2 | 3 | # 4 | # Linearly interpolates a value between begin and end. 5 | # 6 | def interpolate(t) 7 | # TODO possibly allow geometric or arbitrary interpolation 8 | a, b = self.begin, self.end 9 | ta, tb = (1.0 - t), t 10 | a * ta + b * tb 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /lib/chingu/fpscounter.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 Chingu 24 | # 25 | # Calculates a fps and a tick-time for use in update-calls 26 | # register_tick() must be called every game loop iteration 27 | # 28 | class FPSCounter 29 | attr_reader :milliseconds_since_last_tick 30 | alias :dt :milliseconds_since_last_tick 31 | 32 | # 33 | # Frames per second, access with $window.fps or $window.framerate 34 | # 35 | attr_reader :fps 36 | alias :framerate :fps 37 | 38 | # 39 | # Total amount of game iterations (ticks) 40 | # 41 | attr_reader :ticks 42 | 43 | def initialize 44 | @current_second = Gosu::milliseconds / 1000 45 | @accum_fps = 0 46 | @fps = 0 47 | @ticks = 0 48 | 49 | @milliseconds_since_last_tick = 0 50 | @last_value = Gosu::milliseconds 51 | end 52 | 53 | # 54 | # This should be called once every game-iteration, preferable in update() 55 | # 56 | def register_tick 57 | @accum_fps += 1 58 | @ticks += 1 59 | current_second = Gosu::milliseconds / 1000 60 | if current_second != @current_second 61 | @current_second = current_second 62 | @fps = @accum_fps 63 | @accum_fps = 0 64 | end 65 | 66 | # 67 | # Calculate how many milliseconds passed since last game loop iteration. 68 | # 69 | @milliseconds_since_last_tick = Gosu::milliseconds - @last_value 70 | @last_value = Gosu::milliseconds 71 | return @milliseconds_since_last_tick 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/chingu/game_object.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | require_rel 'helpers' 23 | module Chingu 24 | # 25 | # GameObject inherits from BasicGameObject to get traits and some class-methods like .all and .destroy 26 | # On top of that, it encapsulates GOSUs Image#draw_rot and all its parameters. 27 | # In Chingu GameObject is a visual object, something to put on screen, centers around the .image-parameter. 28 | # If you wan't a invisible object but with traits, use BasicGameObject. 29 | # 30 | class GameObject < Chingu::BasicGameObject 31 | trait :sprite 32 | include Chingu::Helpers::InputClient # Adds input and input= 33 | end 34 | end -------------------------------------------------------------------------------- /lib/chingu/game_states/debug.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module GameStates 24 | 25 | # 26 | # Debug game state (F1 is default key to start/exit debug win, 'p' 27 | # to pause game) 28 | # 29 | # Usage: 30 | # 31 | class Debug < Chingu::GameState 32 | include Chingu::Helpers::OptionsSetter 33 | 34 | attr_accessor :fade_color, :text_color, :text, :x_offset, :y_offset 35 | 36 | # TODO - centralize! 37 | Z = 999 38 | 39 | DEFAULTS = { 40 | :x_offset => 10, 41 | :y_offset => 10, 42 | :text_color => Gosu::Color.new(255,255,255,255), 43 | :fade_color => Gosu::Color.new(100,100,100,70), 44 | :paused => false 45 | } 46 | 47 | def initialize(options = {}) 48 | super 49 | set_options(options, DEFAULTS) 50 | 51 | # it fails when setup in DEFAULTS as it needs existing $window 52 | @font ||= Gosu::Font[16] 53 | 54 | self.input = {:p => :pause, :f1 => :return_to_game, :esc => :return_to_game} 55 | end 56 | 57 | def return_to_game 58 | game_state_manager.pop_game_state 59 | end 60 | 61 | def pause 62 | @paused = !@paused 63 | end 64 | 65 | def update 66 | game_state_manager.previous_game_state.update unless @paused 67 | end 68 | 69 | def draw 70 | previous_state.draw unless previous_state.nil? 71 | 72 | $window.draw_quad( 0,0,@fade_color, 73 | $window.width,0,@fade_color, 74 | $window.width,$window.height,@fade_color, 75 | 0,$window.height,@fade_color,10) 76 | 77 | @font.draw("DEBUG CONSOLE", @x_offset, @y_offset, Z) 78 | print_lines(@text || generate_info) 79 | end 80 | 81 | protected 82 | 83 | def print_lines(text) 84 | height = @font.height 85 | lines = text.respond_to?(:lines) ? text.lines : text 86 | 87 | lines.each_with_index do |line,i| 88 | @font.draw(line.strip, @x_offset, @y_offset + height * (i+3), Z,1,1, @text_color) 89 | end 90 | end 91 | 92 | def generate_info 93 | info = '' 94 | info << previous_state.to_s 95 | 96 | info << "\nObjects\n" 97 | 98 | previous_state.game_objects.each do |o| 99 | info << " #{o.class.to_s}: #{o.to_s}\n" 100 | end 101 | 102 | info 103 | end 104 | 105 | def previous_state 106 | game_state_manager.previous_game_state 107 | end 108 | 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /lib/chingu/game_states/fade_to.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 Chingu 24 | module GameStates 25 | 26 | # 27 | # Premade game state for chingu - Fade between two game states 28 | # Fade from the current game state to a new one whenever with: 29 | # 30 | # push_game_state(Chingu::GameStates::FadeTo.new(new_game_state, :speed => 3)) 31 | # 32 | # .. Or make your whole game look better with 1 line: 33 | # 34 | # transitional_game_state(Chingu::GameStates::FadeTo, :speed => 10) 35 | # 36 | class FadeTo < Chingu::GameState 37 | 38 | def initialize(new_game_state, options = {}) 39 | super(options) 40 | @options = {:speed => 3, :zorder => INFINITY}.merge(options) 41 | 42 | @new_game_state = new_game_state 43 | @new_game_state = new_game_state.new if new_game_state.is_a? Class 44 | end 45 | 46 | def setup 47 | @color = Gosu::Color.new(0,0,0,0) 48 | if previous_game_state 49 | p "* Setup: fading out" if options[:debug] 50 | @fading_in = false 51 | @alpha = 0.0 52 | else 53 | p "* Setup: fading in" if options[:debug] 54 | @fading_in = true 55 | @alpha = 255.0 56 | end 57 | 58 | update # Since draw is called before update 59 | end 60 | 61 | def update 62 | @alpha += (@fading_in ? -@options[:speed] : @options[:speed]) 63 | @alpha = 0 if @alpha < 0 64 | @alpha = 255 if @alpha > 255 65 | 66 | if @alpha == 255 67 | @fading_in = true 68 | p "* Update: fading in" if options[:debug] 69 | end 70 | 71 | @color.alpha = @alpha.to_i 72 | @drawn = false 73 | end 74 | 75 | def draw 76 | # Stop possible endless loops 77 | if @drawn == false 78 | @drawn = true 79 | 80 | if @fading_in 81 | @new_game_state.draw 82 | else 83 | previous_game_state.draw 84 | end 85 | 86 | $window.draw_quad( 0,0,@color, 87 | $window.width,0,@color, 88 | $window.width,$window.height,@color, 89 | 0,$window.height,@color,@options[:zorder]) 90 | end 91 | 92 | if @fading_in && @alpha == 0 93 | switch_game_state(@new_game_state, :transitional => false) 94 | end 95 | 96 | end 97 | end 98 | end 99 | end -------------------------------------------------------------------------------- /lib/chingu/game_states/network_state.rb: -------------------------------------------------------------------------------- 1 | module Chingu 2 | module GameStates 3 | # Abstract state, parent of NetworkClient and NetworkServer. 4 | class NetworkState < GameState 5 | PACKET_HEADER_LENGTH = 4 6 | PACKET_HEADER_FORMAT = "N" 7 | DEFAULT_PORT = 7778 8 | 9 | class PacketBuffer 10 | def initialize 11 | @data = '' # Buffered data. 12 | @length = nil # Length of the next packet. nil if header not read yet. 13 | end 14 | 15 | # Add data string to the buffer. 16 | def buffer_data(data) 17 | @data << data 18 | end 19 | 20 | # Call after adding data with #buffer_data until there are no more packets left. 21 | def next_packet 22 | # Read the header to find out the length of the next packet. 23 | unless @length 24 | if @data.length >= PACKET_HEADER_LENGTH 25 | @length = @data[0...PACKET_HEADER_LENGTH].unpack(PACKET_HEADER_FORMAT)[0] 26 | @data[0...PACKET_HEADER_LENGTH] = '' 27 | end 28 | end 29 | 30 | # If there is enough data after the header for the full packet, return it. 31 | if @length and @length <= @data.length 32 | begin 33 | packet = @data[0...@length] 34 | @data[0...@length] = '' 35 | @length = nil 36 | return packet 37 | rescue TypeError => ex 38 | puts "Bad data received:\n#{@data.inspect}" 39 | raise ex 40 | end 41 | else 42 | return nil 43 | end 44 | end 45 | end 46 | 47 | attr_reader :address, :port 48 | attr_reader :bytes_sent, :bytes_received 49 | attr_reader :packets_sent, :packets_received 50 | 51 | def initialize(options = {}) 52 | raise "Can't instantiate abstract class" if self.class == NetworkState 53 | 54 | super(options) 55 | 56 | reset_counters 57 | 58 | @address = options[:address] || "0.0.0.0" 59 | @port = options[:port] || DEFAULT_PORT 60 | @debug = options[:debug] 61 | end 62 | 63 | # Resets #bytes_sent, #bytes_received, #packets_sent and #packets_received to zero. 64 | def reset_counters 65 | @bytes_sent = @bytes_received = 0 66 | @packets_sent = @packets_received = 0 67 | 0 68 | end 69 | end 70 | end 71 | end -------------------------------------------------------------------------------- /lib/chingu/game_states/pause.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module GameStates 24 | 25 | # 26 | # Premade game state for chingu - A simple pause state. 27 | # Pause whenever with: 28 | # push_game_state(Chingu::GameStates::Pause) 29 | # 30 | # requires the global $window set to the instance of Gosu::Window (automaticly handled if you use Chingu::Window) 31 | # 32 | class Pause < Chingu::GameState 33 | 34 | def initialize(options = {}) 35 | super 36 | @white = Gosu::Color.new(255,255,255,255) 37 | @color = Gosu::Color.new(200,0,0,0) 38 | @font = Gosu::Font[35] 39 | @text = "PAUSED - press ESC to return to game." 40 | end 41 | 42 | def button_up(id) 43 | pop_game_state(:setup => false) if id == Gosu::KbEscape # Return the previous game state, dont call setup() 44 | end 45 | 46 | def draw 47 | previous_game_state.draw # Draw prev game state onto screen (in this case our level) 48 | $window.draw_quad( 0,0,@color, 49 | $window.width,0,@color, 50 | $window.width,$window.height,@color, 51 | 0,$window.height,@color, Chingu::DEBUG_ZORDER) 52 | 53 | @font.draw(@text, ($window.width/2 - @font.text_width(@text)/2), $window.height/2 - @font.height, Chingu::DEBUG_ZORDER + 1) 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/chingu/game_states/popup.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module GameStates 24 | 25 | # 26 | # Premade game state for chingu - A simple way if pausing the game + displaying a text. 27 | # Usage: 28 | # push_game_state(Chingu::GameStates::Popup.new(:text => "bla bla bla")) 29 | # 30 | # TODO: Use Gosu's new flush() instead of mucking around with ZORDER + 1000... 31 | # 32 | class Popup < Chingu::GameState 33 | 34 | def initialize(options = {}) 35 | super 36 | @white = Color.new(255,255,255,255) 37 | @color = Gosu::Color.new(200,0,0,0) 38 | @string = options[:text] || "Press ESC to return." 39 | @text = Text.new(@string, :x => 20, :y => 10, :align => :left, :zorder => Chingu::DEBUG_ZORDER + 1001, :factor => 1) 40 | end 41 | 42 | def button_up(id) 43 | pop_game_state(:setup => false) if id == Gosu::KbEscape # Return the previous game state, dont call setup() 44 | end 45 | 46 | def draw 47 | previous_game_state.draw # Draw prev game state 48 | $window.draw_quad( 0,0,@color, 49 | $window.width,0,@color, 50 | $window.width,$window.height,@color, 51 | 0,$window.height,@color, Chingu::DEBUG_ZORDER + 1000) 52 | @text.draw 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/chingu/gosu_ext/image.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | # Core extensions to GOSU 24 | # Some of these require the gem 'texplay' 25 | # 26 | module Gosu 27 | class Image 28 | # 29 | # Returns true if the pixel at x, y is 100% transperant (good for collisiondetection) 30 | # Requires texplay 31 | # 32 | def transparent_pixel?(x, y) 33 | begin 34 | self.get_pixel(x,y)[3] == 0 35 | rescue 36 | puts "Error in get_pixel at x/y: #{x}/#{y}: #{$!}" 37 | end 38 | end 39 | 40 | # 41 | # Retrofy should be called just after the image is loaded. 42 | # When retrofied an image will use a non-blurry zoom. 43 | # This could be used to make each pixel a sharp 4 pixelblock => retrofeeling. 44 | # 45 | def retrofy 46 | Gosu::enable_undocumented_retrofication 47 | self 48 | 49 | # 50 | # The below code depends on the bad opengl gem 51 | # And it could affect other images anyhow... 52 | # So let's use Gosu::enable_undocumented_retrofication until further notice. 53 | # 54 | 55 | #glBindTexture(GL_TEXTURE_2D, self.gl_tex_info.tex_name) 56 | #glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) 57 | #glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 58 | #self 59 | end 60 | end 61 | end -------------------------------------------------------------------------------- /lib/chingu/gosu_ext/sample.rb: -------------------------------------------------------------------------------- 1 | module Gosu 2 | class Sample 3 | DEFAULT_VOLUME = 1.0 # Default volume of new samples. 4 | 5 | class << self 6 | # Volume of all Samples. 7 | attr_reader :volume 8 | 9 | public 10 | # Volume of Samples, affected by Sample.volume and Window#volume and muting. 11 | def effective_volume 12 | @volume * $window.effective_volume 13 | end 14 | 15 | public 16 | # Set the global volume of Samples. 17 | def volume=(value) 18 | raise "Bad volume setting" unless value.is_a? Numeric 19 | 20 | @volume = [[value, 1.0].min, 0.0].max.to_f 21 | end 22 | 23 | public 24 | def init_sound 25 | @volume = DEFAULT_VOLUME 26 | nil 27 | end 28 | end 29 | 30 | init_sound 31 | 32 | # Volume of this Sample. This is multiplied by the volume in #play. 33 | attr_reader :volume 34 | 35 | alias_method :old_initialize, :initialize 36 | protected :old_initialize 37 | public 38 | # Accepts :volume (0.0..1.0) option, defaulting to 1.0. 39 | def initialize(filename, options = {}) 40 | options = { 41 | volume: DEFAULT_VOLUME, 42 | }.merge! options 43 | 44 | @volume = options[:volume] 45 | 46 | old_initialize(filename) 47 | end 48 | 49 | public 50 | # Set the volume of this Sample. This is multiplied by the volume in #play. 51 | def volume=(value) 52 | raise "Bad volume setting" unless value.is_a? Numeric 53 | 54 | @volume = [[value, 1.0].min, 0.0].max.to_f 55 | end 56 | 57 | public 58 | # Volume the Sample will actually be played at, affected by Sample.volume and Window#volume. 59 | def effective_volume 60 | @volume * self.class.effective_volume 61 | end 62 | 63 | alias_method :old_play, :play 64 | protected :old_play 65 | public 66 | def play(volume = 1, speed = 1, looping = false) 67 | volume *= effective_volume 68 | old_play(volume, speed, looping) if volume > 0.0 69 | end 70 | 71 | alias_method :old_play_pan, :play_pan 72 | protected :old_play_pan 73 | public 74 | def play_pan(pan = 0, volume = 1, speed = 1, looping = false) 75 | volume *= effective_volume 76 | old_play_pan(pan, volume, speed, looping) if volume > 0.0 77 | end 78 | end 79 | end -------------------------------------------------------------------------------- /lib/chingu/gosu_ext/song.rb: -------------------------------------------------------------------------------- 1 | module Gosu 2 | class Song 3 | DEFAULT_VOLUME = 1.0 4 | 5 | class << self 6 | attr_reader :resources 7 | protected :resources 8 | 9 | # Volume of Songs, not allowing for global volume settings. 10 | attr_reader :volume 11 | 12 | # Volume the song is played at, affected by Song.volume and Window#volume/muting. 13 | def effective_volume 14 | @volume * $window.effective_volume 15 | end 16 | 17 | # Volume of Songs, not allowing for global volume settings. 18 | def volume=(value) 19 | raise "Bad volume setting" unless value.is_a? Numeric 20 | 21 | old_volume = @volume 22 | @volume = [[value, 1.0].min, 0.0].max.to_f 23 | 24 | recalculate_volumes(old_volume, @volume) 25 | 26 | @volume 27 | end 28 | 29 | def init_sound 30 | @volume = DEFAULT_VOLUME 31 | nil 32 | end 33 | 34 | protected 35 | # Recalculate all song volumes, after a global volume (Window#volume or Song.volume) has updated. 36 | def recalculate_volumes(old_volume, new_volume) 37 | # Avoid divide-by-zero when working out how much to alter the value by. 38 | multiplier = if old_volume == 0.0 39 | (new_volume > 0) ? 1.0 : 0.0 40 | else 41 | new_volume / old_volume 42 | end 43 | resources.each_value {|song| song.send(:effective_volume=, song.volume * effective_volume * multiplier) } 44 | end 45 | end 46 | 47 | init_sound 48 | 49 | alias_method :old_initialize, :initialize 50 | protected :old_initialize 51 | 52 | # Volume, as played. 53 | alias_method :effective_volume, :volume 54 | 55 | # Set the volume, as played. 56 | alias_method :effective_volume=, :volume= 57 | protected :effective_volume= 58 | 59 | # Accepts :volume (0.0..1.0) option, defaulting to 1.0. 60 | def initialize(filename, options = {}) 61 | options = { 62 | volume: DEFAULT_VOLUME, 63 | }.merge! options 64 | 65 | old_initialize(filename) 66 | 67 | @muted = false 68 | self.volume = options[:volume] 69 | end 70 | 71 | # Volume, not affected by Song volume or the Window volume/muted. 72 | def volume 73 | @true_volume 74 | end 75 | 76 | def volume=(value) 77 | @true_volume = [[value, 0.0].max, 1.0].min 78 | 79 | self.effective_volume = @true_volume * self.class.effective_volume unless @muted 80 | 81 | volume 82 | end 83 | 84 | protected 85 | def mute 86 | self.effective_volume = 0.0 87 | self 88 | end 89 | 90 | protected 91 | def unmute 92 | self.effective_volume = @true_volume * self.class.effective_volume 93 | self 94 | end 95 | end 96 | end -------------------------------------------------------------------------------- /lib/chingu/helpers/class_inheritable_accessor.rb: -------------------------------------------------------------------------------- 1 | # 2 | # This code is from http://railstips.org/2008/6/13/a-class-instance-variable-update 3 | # But we use the rails-name for it, class_inheritable_accessor, which should make ppl more @ home. 4 | # 5 | module Chingu 6 | module Helpers 7 | module ClassInheritableAccessor 8 | def self.included(base) 9 | base.extend(ClassMethods) 10 | end 11 | 12 | module ClassMethods 13 | def class_inheritable_accessor(*args) 14 | @cattr_inheritable_attrs ||= [:cattr_inheritable_attrs] 15 | @cattr_inheritable_attrs += args 16 | args.each do |arg| 17 | class_eval %( 18 | class << self; attr_accessor :#{arg} end 19 | ) 20 | end 21 | @cattr_inheritable_attrs 22 | end 23 | 24 | def inherited(subclass) 25 | @cattr_inheritable_attrs.each do |inheritable_attribute| 26 | instance_var = "@#{inheritable_attribute}" 27 | subclass.instance_variable_set(instance_var, instance_variable_get(instance_var).dup) 28 | 29 | #if instance_var =~ /trait_options/ 30 | # puts "#{self.to_s} -> #{subclass.to_s}: #{instance_var}" # DEBUG 31 | # puts instance_variable_get(instance_var) 32 | # puts "------" 33 | #end 34 | end 35 | super 36 | end 37 | end 38 | end 39 | end 40 | end -------------------------------------------------------------------------------- /lib/chingu/helpers/fps_counter.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | require 'forwardable' 23 | 24 | module Chingu 25 | module Helpers 26 | 27 | # 28 | # Convenience-methods for classes that have an FPS counter 29 | # Mixed into Chingu::Window and Chingu::Console 30 | # 31 | module FPSCounter 32 | extend Forwardable 33 | def_delegator :@fps_counter, :ticks 34 | def_delegators :@fps_counter, :fps, :framerate # TODO: switch to Gosu::fps 35 | def_delegators :@fps_counter, :dt, :milliseconds_since_last_tick 36 | end 37 | 38 | end 39 | end -------------------------------------------------------------------------------- /lib/chingu/helpers/game_state.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- Game framework built on top of the opengl accelerated gamelib Gosu 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | require 'forwardable' 23 | 24 | module Chingu 25 | module Helpers 26 | 27 | # 28 | # push_game_state accepts either a class inherited from GameState or an object-instance from such a class. 29 | # 30 | # It will make call new() on a class, and just push an object. 31 | # 32 | module GameState 33 | extend Forwardable 34 | 35 | def_delegator :game_state_manager, :game_states 36 | def_delegator :game_state_manager, :push_game_state 37 | def_delegator :game_state_manager, :pop_game_state 38 | def_delegator :game_state_manager, :switch_game_state 39 | def_delegator :game_state_manager, :transitional_game_state 40 | def_delegator :game_state_manager, :current_game_state 41 | def_delegator :game_state_manager, :clear_game_states 42 | def_delegator :game_state_manager, :pop_until_game_state 43 | end 44 | 45 | end 46 | end -------------------------------------------------------------------------------- /lib/chingu/helpers/input_dispatcher.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Helpers 24 | # 25 | # Methods for parsing and dispatching Chingus input-maps 26 | # Mixed into Chingu::Window and Chingu::GameState 27 | # 28 | module InputDispatcher 29 | attr_reader :input_clients 30 | 31 | # Called by input_clients to add themselves from forwarding. 32 | def add_input_client(object) 33 | @input_clients << object unless @input_clients.include?(object) 34 | end 35 | 36 | # Called by input_clients to remove themselves from forwarding. 37 | def remove_input_client(object) 38 | @input_clients.delete(object) 39 | end 40 | 41 | # Dispatch button press to one of your clients. 42 | def dispatch_button_down(id, object) 43 | return unless Input::CONSTANT_TO_SYMBOL[id] 44 | if actions = object.input[Input::CONSTANT_TO_SYMBOL[id].first] 45 | dispatch_actions(actions) 46 | end 47 | end 48 | 49 | # Dispatch button release to one of your clients. 50 | def dispatch_button_up(id, object) 51 | return unless Input::CONSTANT_TO_SYMBOL[id] 52 | if actions = object.input[:"released_#{Input::CONSTANT_TO_SYMBOL[id].first}"] 53 | dispatch_actions(actions) 54 | end 55 | end 56 | 57 | # 58 | # Dispatches input-mapper for any given object 59 | # 60 | def dispatch_input_for(object, prefix = "holding_") 61 | pattern = /^#{prefix}/ 62 | object.input.each do |symbol, actions| 63 | if symbol.to_s =~ pattern and $window.button_down?(Input::SYMBOL_TO_CONSTANT[$'.to_sym]) 64 | dispatch_actions(actions) 65 | end 66 | end 67 | end 68 | 69 | # 70 | # For a given object, dispatch "action". 71 | # An action can be an array containing any of: 72 | # 73 | # * Proc/Lambda or Method, call() it 74 | # * GameState-instance, push it on top of stack 75 | # * GameState-inherited class, create a new instance, cache it and push it on top of stack 76 | # 77 | protected 78 | def dispatch_actions(actions) 79 | # puts "Dispatch Action: #{action} - Objects class: #{object.class.to_s}" 80 | actions.each do |action| 81 | case action 82 | when Proc, Method 83 | action[] 84 | when Chingu::GameState, Class 85 | # Don't need to check if the Class is a GameState, since that is already checked. 86 | push_game_state(action) 87 | else 88 | raise ArgumentError, "Unexpected action #{action}" 89 | end 90 | end 91 | # Other types will already have been resolved to one of these, so no need for checking. 92 | end 93 | end 94 | 95 | end 96 | end -------------------------------------------------------------------------------- /lib/chingu/helpers/options_setter.rb: -------------------------------------------------------------------------------- 1 | module Chingu 2 | module Helpers 3 | 4 | # 5 | # Provides #set_options method to simplify common initialization from Hash. 6 | # 7 | module OptionsSetter 8 | 9 | protected 10 | 11 | # 12 | # Takes hash and sets the instance variables denoted by 'keys' 13 | # to 'values'. Uses setter, when available, otherwise simple 14 | # 'instance_variable_set' is called. You can specify defaults 15 | # for your convenience. 16 | # 17 | def set_options(options, defaults = {}) 18 | options = defaults.merge(options) 19 | 20 | options.each do |attr,value| 21 | setter = "#{attr}=" 22 | 23 | if self.respond_to?(setter) 24 | self.send(setter, value) 25 | else 26 | self.instance_variable_set("@#{attr.to_s}", value) 27 | end 28 | end 29 | end 30 | 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/chingu/helpers/rotation_center.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Helpers 24 | module RotationCenter 25 | # 26 | # returns [center_x, center_y] (see Gosu::Image#draw_rot) 27 | # 28 | @@rotation_centers = { 29 | :top_left => [0,0], 30 | :left_top => [0,0], 31 | 32 | :center_left => [0,0.5], 33 | :middle_left => [0,0.5], 34 | :left_center => [0,0.5], 35 | :left_middle => [0,0.5], 36 | 37 | :bottom_left => [0,1], 38 | :left_bottom => [0,1], 39 | 40 | :top_center => [0.5,0], 41 | :top_middle => [0.5,0], 42 | :center_top => [0.5,0], 43 | :middle_top => [0.5,0], 44 | 45 | :center_center => [0.5,0.5], 46 | :middle_middle => [0.5,0.5], 47 | :center => [0.5,0.5], 48 | :middle => [0.5,0.5], 49 | 50 | :bottom_center => [0.5,1], 51 | :bottom_middle => [0.5,1], 52 | :center_bottom => [0.5,1], 53 | :middle_bottom => [0.5,1], 54 | 55 | :top_right => [1,0], 56 | :right_top => [1,0], 57 | 58 | :center_right => [1,0.5], 59 | :middle_right => [1,0.5], 60 | :right_center => [1,0.5], 61 | :right_middle => [1,0.5], 62 | 63 | :bottom_right => [1,1], 64 | :right_bottom => [1,1] 65 | } 66 | 67 | # 68 | # Sets @center_x and @center_y according to given alignment. Available alignments are: 69 | # 70 | # :top_left, :center_left, :bottom_left, :top_center, 71 | # :center_center, :bottom_center, :top_right, :center_right and :bottom_right 72 | # 73 | # They're also available in the opposite order with the same meaning. 74 | # They're also available (hey, hashlookups are speedy) with "middle" instead of "center" since 75 | # those 2 words basicly have the same meaning and are both understandable. 76 | # 77 | def rotation_center(alignment = nil) 78 | return unless alignment 79 | @center_x, @center_y = @@rotation_centers[alignment.to_sym] 80 | end 81 | 82 | def rotation_center=(alignment) 83 | rotation_center(alignment) 84 | end 85 | 86 | end 87 | end 88 | end -------------------------------------------------------------------------------- /lib/chingu/inflector.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 Chingu 24 | module Inflector 25 | 26 | # 27 | # "automatic_assets" -> "AutomaticAssets" 28 | # 29 | def Inflector.camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true) 30 | if first_letter_in_uppercase 31 | lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } 32 | else 33 | lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1] 34 | end 35 | end 36 | 37 | # 38 | # "Chingu::GameObject" -> "GameObject" 39 | # 40 | def Inflector.demodulize(class_name_in_module) 41 | class_name_in_module.to_s.gsub(/^.*::/, '') 42 | end 43 | 44 | # 45 | # "FireBall" -> "fire_ball" 46 | # 47 | def Inflector.underscore(camel_cased_word) 48 | camel_cased_word.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). 49 | gsub(/([a-z\d])([A-Z])/,'\1_\2'). 50 | tr("-", "_"). 51 | downcase 52 | end 53 | 54 | end 55 | end -------------------------------------------------------------------------------- /lib/chingu/particle.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Our basic particle class, basicly just a GameObject with trait "effect" 3 | # 4 | # TODO: expand on this further, as it is now it doesn't add enough to warrant a whole new class. 5 | # 6 | 7 | module Chingu 8 | class Particle < Chingu::GameObject 9 | has_trait :effect 10 | 11 | def initialize(options) 12 | super({:mode => :additive}.merge(options)) 13 | @animation = options[:animation] || nil 14 | end 15 | 16 | def update 17 | self.image = @animation.next if @animation 18 | end 19 | 20 | end 21 | end -------------------------------------------------------------------------------- /lib/chingu/simple_menu.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | # 24 | # When you want a quick 'n dirty menu, SimpleMenu might help. 25 | # Basic usage: 26 | # SimpleMenu.create(:menu_items => {"StartGame" => PlayState, "Edit" => :exit}) 27 | # 28 | # It will catch keys :up, :down, :return and :space and navigate through the menu 29 | # 30 | class SimpleMenu < BasicGameObject 31 | include Chingu::Helpers::InputClient 32 | attr_accessor :menu_items, :visible 33 | 34 | def initialize(options = {}) 35 | super 36 | 37 | # @font_size = options.delete(:font_size) || 30 38 | @menu_items = options.delete(:menu_items) 39 | @x = options.delete(:x) || $window.width/2 40 | @y = options.delete(:y) || 0 41 | @spacing = options.delete(:spacing) || 100 42 | @items = [] 43 | @visible = true 44 | 45 | y = @y 46 | menu_items.each do |key, value| 47 | item = if key.is_a? String 48 | Text.new(key, options.dup) 49 | elsif key.is_a? Image 50 | GameObject.new(options.merge!(:image => key)) 51 | elsif key.is_a? GameObject 52 | key.options.merge!(options.dup) 53 | key 54 | end 55 | 56 | item.options[:on_select] = method(:on_select) 57 | item.options[:on_deselect] = method(:on_deselect) 58 | item.options[:action] = value 59 | 60 | item.rotation_center = :center_top 61 | item.x = @x 62 | item.y = y 63 | y += item.height + @spacing 64 | @items << item 65 | end 66 | @selected = options[:selected] || 0 67 | step(0) 68 | 69 | self.input = {:up => lambda{step(-1)}, :down => lambda{step(1)}, [:return, :space] => :select} 70 | end 71 | 72 | # 73 | # Moves selection within the menu. Can be called with negative or positive values. -1 and 1 makes most sense. 74 | # 75 | def step(value) 76 | selected.options[:on_deselect].call(selected) 77 | @selected += value 78 | @selected = @items.count-1 if @selected < 0 79 | @selected = 0 if @selected == @items.count 80 | selected.options[:on_select].call(selected) 81 | end 82 | 83 | def select 84 | dispatch_action(selected.options[:action], self.parent) 85 | end 86 | 87 | def selected 88 | @items[@selected] 89 | end 90 | 91 | def on_deselect(object) 92 | object.color = ::Gosu::Color::WHITE 93 | end 94 | 95 | def on_select(object) 96 | object.color = ::Gosu::Color::RED 97 | end 98 | 99 | def draw 100 | @items.each { |item| item.draw } 101 | end 102 | 103 | private 104 | 105 | # 106 | # TODO - DRY this up with input dispatcher somehow 107 | # 108 | def dispatch_action(action, object) 109 | case action 110 | when Symbol, String 111 | object.send(action) 112 | when Proc, Method 113 | action[] 114 | when Chingu::GameState 115 | game_state.push_game_state(action) 116 | when Class 117 | if action.ancestors.include?(Chingu::GameState) 118 | game_state.push_game_state(action) 119 | end 120 | else 121 | # TODO possibly raise an error? This ought to be handled when the input is specified in the first place. 122 | end 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/chingu/traits/animation.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Traits 24 | # 25 | # A chingu trait providing automatic loading of tile-animations 26 | # 27 | # For example: 28 | # class FireBall; traits :animation; end; 29 | # 30 | # Will automatically load: 31 | # - fire_ball.png into self.animations[:default] 32 | # - fire_ball_exploding.png into self.animations[:exploding] 33 | # 34 | # Adds accessors animations -> Hash with all animations, hash-key is the name of the animation 35 | # 36 | module Animation 37 | 38 | module ClassMethods 39 | 40 | def initialize_trait(options = {}) 41 | trait_options[:animation] = {:directory => "media", :play => true, :delay => 100}.merge(options) 42 | end 43 | 44 | end 45 | 46 | def setup_trait(options) 47 | @animation_options = {:debug => false}.merge(options) 48 | @animations = load_animations 49 | super 50 | end 51 | 52 | # 53 | # Try loading animation from class-name 54 | # 55 | def load_animations 56 | animations = {} 57 | glob = "#{trait_options[:animation][:directory]}/#{self.filename}*" 58 | puts "Animations? #{glob}" if trait_options[:animation][:debug] 59 | Dir[glob].each do |tile_file| 60 | puts tile_file if trait_options[:animation][:debug] 61 | if tile_file =~ /[a-zA-Z\_+]_*(\d+)x(\d+)_*([a-zA-Z]*)\.(bmp|png)/ 62 | state = $3.length > 0 ? $3 : "default" 63 | animations[state.to_sym] = Chingu::Animation.new(trait_options[:animation].merge(:file => tile_file)) 64 | elsif tile_file =~ /[a-zA-Z\_+]\.(bmp|png)/ 65 | animations[:default] = Chingu::Animation.new(trait_options[:animation].merge(:file => tile_file)) 66 | end 67 | end 68 | return animations 69 | end 70 | 71 | def animation 72 | @animations[:default] 73 | end 74 | 75 | # 76 | # Returns all animations, then access invidual states with animations[:explode] etc. 77 | # 78 | def animations 79 | @animations 80 | end 81 | 82 | end 83 | end 84 | end -------------------------------------------------------------------------------- /lib/chingu/traits/asynchronous.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Traits 24 | 25 | # 26 | # A chingu trait providing an asynchronous tasks queue 27 | # 28 | # For example: 29 | # class Robot < GameObject 30 | # traits :asynchronous 31 | # 32 | # # robot stuff 33 | # end 34 | # 35 | # # later, controlling your robot... 36 | # robot.async do |q| 37 | # q.tween(5000, :x => 1024, :y => 64) 38 | # q.call :explode 39 | # end 40 | # 41 | # Will move the robot asynchronously from its current location to 42 | # (1024, 64), then blow it up. The +async+ method returns immediately 43 | # after adding tasks to the queue. 44 | # 45 | # The first task on the queue is processed each tick during the 46 | # update phase, then removed from the queue when it is deemed finished. 47 | # What constitutes "finished" is determined by the particular subclass of 48 | # +BasicTask+. 49 | # 50 | 51 | module Asynchronous 52 | 53 | attr_reader :tasks 54 | 55 | # 56 | # Setup 57 | # 58 | def setup_trait(options) 59 | @tasks = Chingu::Async::TaskList.new 60 | super 61 | end 62 | 63 | def update_trait 64 | @tasks.update self 65 | super 66 | end 67 | 68 | # 69 | # Add a set of tasks to the task queue to be executed 70 | # asynchronously. If no block is given, returns the TaskBuilder that 71 | # would have been passed to the block. 72 | # 73 | def async 74 | builder = Chingu::Async::TaskBuilder.new(@tasks) 75 | if block_given? 76 | yield builder 77 | else 78 | builder 79 | end 80 | end 81 | 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/chingu/traits/bounding_box.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Traits 24 | # 25 | # Providing a bounding_box-method which generates a AABB on the fly from: 26 | # x, y, factor_x, factor_y and rotation_center 27 | # 28 | module BoundingBox 29 | CENTER_TO_FACTOR = { 0 => -1, 0.5 => 0, 1 => 1 } 30 | attr_accessor :collidable 31 | 32 | module ClassMethods 33 | def initialize_trait(options = {}) 34 | trait_options[:bounding_box] = options 35 | end 36 | end 37 | 38 | def setup_trait(options) 39 | @cached_bounding_box = nil 40 | @collidable = true 41 | super 42 | end 43 | 44 | def collision_at?(x, y) 45 | bounding_box.collide_point?(x,y) 46 | end 47 | 48 | # 49 | # Returns an instance of class Rect 50 | # 51 | def bounding_box 52 | if @cached_bounding_box 53 | @cached_bounding_box.x = self.x + @_x_diff 54 | @cached_bounding_box.y = self.y + @_y_diff 55 | 56 | return @cached_bounding_box 57 | end 58 | 59 | width, height = self.width, self.height 60 | 61 | if scale = trait_options[:bounding_box][:scale] 62 | width_scale, height_scale = scale.is_a?(Array) ? [scale[0],scale[1]] : [scale,scale] 63 | width *= width_scale 64 | height *= height_scale 65 | end 66 | 67 | x = self.x - width * self.center_x 68 | y = self.y - height * self.center_y 69 | x += width * CENTER_TO_FACTOR[self.center_x] if self.factor_x < 0 70 | y += height * CENTER_TO_FACTOR[self.center_y] if self.factor_y < 0 71 | 72 | return Rect.new(x, y, width, height) 73 | end 74 | alias :bb :bounding_box 75 | 76 | def cache_bounding_box 77 | @cached_bounding_box = nil 78 | @cached_bounding_box = self.bounding_box 79 | @_x_diff = @cached_bounding_box.x - self.x 80 | @_y_diff = @cached_bounding_box.y - self.y 81 | end 82 | 83 | #def update_trait 84 | # cache_bounding_box if trait_options[:bounding_box][:cache] && !@cached_bounding_box 85 | # super 86 | #end 87 | 88 | def draw_trait 89 | draw_debug if trait_options[:bounding_box][:debug] 90 | super 91 | end 92 | 93 | # 94 | # Visualises the bounding box as a red rectangle. 95 | # 96 | def draw_debug 97 | $window.draw_rect(self.bounding_box, Chingu::DEBUG_COLOR, Chingu::DEBUG_ZORDER) 98 | end 99 | 100 | end 101 | end 102 | end -------------------------------------------------------------------------------- /lib/chingu/traits/bounding_circle.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Traits 24 | # 25 | # Providing a bounding circle in the form of 2 attributes, self.radius and self.diameter 26 | # It creates these 2 attributes from reading image.height, image.width, factor_x and factor_y 27 | # 28 | # ...this usually only makes sense with rotation_center = :center 29 | # 30 | module BoundingCircle 31 | attr_accessor :collidable 32 | 33 | module ClassMethods 34 | def initialize_trait(options = {}) 35 | trait_options[:bounding_circle] = options 36 | end 37 | end 38 | 39 | def setup_trait(options) 40 | @cached_radius = nil 41 | @collidable = true 42 | super 43 | end 44 | 45 | def collision_at?(x, y) 46 | Gosu.distance(self.x, self.y, x, y) < radius 47 | end 48 | 49 | def radius 50 | return @cached_radius if @cached_radius 51 | radius = (self.width + self.height) / 4 52 | radius = radius * trait_options[:bounding_circle][:scale] if trait_options[:bounding_circle][:scale] 53 | return radius 54 | end 55 | 56 | def diameter 57 | radius * 2 58 | end 59 | 60 | def cache_bounding_circle 61 | @cached_radius = self.radius 62 | end 63 | 64 | #def update_trait 65 | # cache_bounding_circle if trait_options[:bounding_circle][:cache] && !@cached_radius 66 | # super 67 | #end 68 | 69 | def circle_left; self.x - self.radius; end 70 | def circle_right; self.x + self.radius; end 71 | def circle_top; self.y - self.radius; end 72 | def circle_bottom; self.y + self.radius; end 73 | 74 | def draw_trait 75 | draw_debug if trait_options[:bounding_circle][:debug] 76 | super 77 | end 78 | 79 | # 80 | # Visualises the bounding circle as a red circle. 81 | # 82 | def draw_debug 83 | $window.draw_circle(self.x, self.y, self.radius, Chingu::DEBUG_COLOR) 84 | end 85 | 86 | end 87 | end 88 | end -------------------------------------------------------------------------------- /lib/chingu/traits/effect.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Traits 24 | 25 | # 26 | # Adds methods: 27 | # rotate(amount) # modifies @angle 28 | # scale(amount) # modifies @factor_x and @factor_y 29 | # fade(amount) # modifies @color.alpha 30 | # 31 | # Also adds attributes 32 | # rotation_rate = amount # adds amount to @angle each game loop 33 | # scale_rate = amount # adds amount to @factor_x and @factor_y each game loop 34 | # fade_rate = amount # adds amount to @color.alpha each game loop 35 | # 36 | # 37 | # WARNING, I'm very close to deprecating this trait, it doesn't do much and still introduces new names to learn. 38 | # After a long discussion in #gosu I feel it's just better to use the accessors angle=, alpha= and factor= 39 | # 40 | # BasicGameObject#alpha= contains the most important logic this trait had now anyhow. 41 | # 42 | 43 | module Effect 44 | attr_accessor :rotation_rate, :fade_rate, :scale_rate 45 | 46 | # 47 | # Setup 48 | # 49 | def setup_trait(options) 50 | ## puts "Effect#setup_trait(#{options})" 51 | @rotation_rate = options[:rotation_rate] || nil 52 | @scale_rate = options[:scale_rate] || nil 53 | @scale_x_rate = options[:scale_x_rate] || nil 54 | @scale_y_rate = options[:scale_y_rate] || nil 55 | @fade_rate = options[:fade_rate] || nil 56 | super 57 | end 58 | 59 | def update_trait 60 | rotate(@rotation_rate) if @rotation_rate 61 | fade(@fade_rate) if @fade_rate 62 | scale(@scale_rate) if @scale_rate 63 | super 64 | end 65 | 66 | # Increase @factor_x and @factor_y at the same time. 67 | def scale(amount = 0.1) 68 | self.factor_x += amount 69 | self.factor_y += amount 70 | end 71 | alias :zoom :scale 72 | 73 | # Ddecrease @factor_x and @factor_y at the same time. 74 | def scale_out(amount = 0.1) 75 | self.factor_x -= amount 76 | self.factor_y -= amount 77 | end 78 | alias :zoom_out :scale_out 79 | 80 | # Rotate object 'amount' degrees 81 | def rotate(amount = 1) 82 | self.angle += amount 83 | end 84 | 85 | # Fade object by decreasing/increasing color.alpha 86 | def fade(amount = 1) 87 | return if amount == 0 88 | self.alpha += amount 89 | end 90 | 91 | # Fade out objects color by decreasing color.alpha 92 | def fade_out(amount = 1) 93 | self.alpha -= amount 94 | end 95 | 96 | # Fade in objects color by increasing color.alpha 97 | def fade_in(amount = 1) 98 | self.alpha += amount 99 | end 100 | 101 | end 102 | end 103 | end -------------------------------------------------------------------------------- /lib/chingu/traits/retrofy.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Traits 24 | # 25 | # A chingu trait providing easier handling of the "retrofy" effect (non-blurry zoom) 26 | # Aims to help out when using scaling with "factor" to create a retrofeeling with big pixels. 27 | # Provides screen_x and screen_y which takes the scaling into account 28 | # Also provides new code for draw() which uses screen_x / screen_y instead of x / y 29 | # 30 | module Retrofy 31 | 32 | #def setup_trait(options) 33 | # @retrofy_options = {:debug => false}.merge(options) 34 | # super 35 | #end 36 | 37 | def retrofied_x=(x) 38 | self.x = x / self.factor 39 | end 40 | 41 | def retrofied_y=(y) 42 | self.y = y / self.factor 43 | end 44 | 45 | def real_x 46 | (self.x / self.factor).to_i 47 | end 48 | 49 | def real_y 50 | (self.y / self.factor).to_i 51 | end 52 | 53 | def retrofied_x 54 | (self.x * self.factor).to_i 55 | end 56 | 57 | def retrofied_y 58 | (self.y * self.factor).to_i 59 | end 60 | 61 | # Returns true if object is inside the game window, false if outside 62 | # this special version takes @factor into consideration 63 | def inside_window? 64 | self.x >= 0 && self.x <= $window.width/self.factor && self.y >= 0 && self.y <= $window.height/self.factor 65 | end 66 | 67 | # Returns true object is outside the game window 68 | # this special version takes @factor into consideration 69 | def outside_window? 70 | not inside_window? 71 | end 72 | 73 | def draw 74 | self.image.draw_rot(self.screen_x, self.screen_y, self.zorder, self.angle, self.center_x, self.center_y, self.factor_x, self.factor_y, self.color, self.mode) 75 | end 76 | end 77 | end 78 | end -------------------------------------------------------------------------------- /lib/chingu/traits/viewport.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Chingu -- OpenGL accelerated 2D game framework for Ruby 4 | # Copyright (C) 2009 ippa / ippa@rubylicio.us 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 | module Chingu 23 | module Traits 24 | # 25 | # A chingu trait providing velocity and acceleration logic. 26 | # Adds parameters: velocity_x/y, acceleration_x/y and modifies self.x / self.y 27 | # Also keeps previous_x and previous_y which is the x, y before modification. 28 | # Can be useful for example collision detection 29 | # 30 | module Viewport 31 | attr_accessor :viewport 32 | 33 | module ClassMethods 34 | def initialize_trait(options = {}) 35 | trait_options[:viewport] = {:apply => true}.merge(options) 36 | end 37 | end 38 | 39 | def setup_trait(options) 40 | @viewport_options = {:debug => false}.merge(options) 41 | 42 | @viewport = Chingu::Viewport.new() 43 | @viewport.x = options[:viewport_x] || 0 44 | @viewport.y = options[:viewport_y] || 0 45 | 46 | super 47 | end 48 | 49 | def inside_viewport?(object) 50 | puts "Deprecated, use self.viewport.inside?() instead" 51 | object.x >= @viewport.x && object.x <= (@viewport.x + $window.width) && 52 | object.y >= @viewport.y && object.y <= (@viewport.y + $window.height) 53 | end 54 | 55 | # Returns true object is outside the view port 56 | def outside_viewport?(object) 57 | puts "Deprecated, use self.viewport.outside?() instead" 58 | not inside_viewport?(object) 59 | end 60 | 61 | # Take care of laggy viewport movements 62 | def update_trait 63 | @viewport.move_towards_target 64 | super 65 | end 66 | 67 | # 68 | # Override game states default draw that draws objects relative to the viewport. 69 | # It only draws game objects inside the viewport. (GOSU does no such optimizations) 70 | # 71 | def draw 72 | #self.game_objects.draw_relative(-@viewport.x, -@viewport.y) 73 | @viewport.apply { super } 74 | end 75 | end 76 | end 77 | end -------------------------------------------------------------------------------- /lib/chingu/version.rb: -------------------------------------------------------------------------------- 1 | module Chingu 2 | VERSION = "0.9rc9" 3 | end 4 | 5 | -------------------------------------------------------------------------------- /spec/chingu/assets_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | describe Chingu::NamedResource do 5 | before :each do 6 | @game = Chingu::Window.new 7 | 8 | # Gosu uses the paths based on where rspec is, not where this file is, so we need to do it manually! 9 | Gosu::Image::autoload_dirs.unshift File.join(File.dirname(File.expand_path(__FILE__)), 'images') 10 | end 11 | 12 | after :each do 13 | @game.close 14 | end 15 | 16 | describe "Image" do 17 | it "should have default autoload dirs" do 18 | Gosu::Image.autoload_dirs.should include(".") 19 | Gosu::Image.autoload_dirs.should include("#{@game.root}/media") 20 | end 21 | 22 | it "should autoload image in Image.autoload_dirs" do 23 | Gosu::Image["rect_20x20.png"].should be_kind_of Gosu::Image 24 | end 25 | 26 | it "should return the same cached Gosu::Image if requested twice" do 27 | Gosu::Image["rect_20x20.png"].should == Gosu::Image["rect_20x20.png"] 28 | end 29 | 30 | #it "should raise error if image is nonexistent" do 31 | # Gosu::Image["nonexistent_image.png"].should raise_error RuntimeError 32 | #end 33 | 34 | end 35 | 36 | describe "Song" do 37 | it "should have default autoload dirs" do 38 | Gosu::Song.autoload_dirs.should include(".") 39 | Gosu::Song.autoload_dirs.should include("#{@game.root}/media") 40 | end 41 | end 42 | 43 | describe "Sample" do 44 | it "should have default autoload dirs" do 45 | Gosu::Sample.autoload_dirs.should include(".") 46 | Gosu::Sample.autoload_dirs.should include("#{@game.root}/media") 47 | end 48 | end 49 | 50 | describe "Font" do 51 | it "should have default autoload dirs" do 52 | Gosu::Font.autoload_dirs.should include(".") 53 | Gosu::Font.autoload_dirs.should include("#{@game.root}/media") 54 | end 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/chingu/basic_game_object_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class MyBasicGameObject < Chingu::BasicGameObject 4 | end 5 | 6 | class MyBasicGameObject2 < Chingu::BasicGameObject 7 | end 8 | 9 | class MyBasicGameObjectWithSetup < Chingu::BasicGameObject 10 | def setup 11 | @paused = true 12 | end 13 | end 14 | 15 | module Chingu 16 | 17 | describe BasicGameObject do 18 | before :each do 19 | @game = Chingu::Window.new 20 | end 21 | 22 | after :each do 23 | @game.close 24 | end 25 | 26 | it { should respond_to(:options) } 27 | it { should respond_to(:paused) } 28 | it { should respond_to(:setup_trait) } 29 | it { should respond_to(:setup) } 30 | it { should respond_to(:update_trait) } 31 | it { should respond_to(:draw_trait) } 32 | it { should respond_to(:filename) } 33 | 34 | context "A class inherited from BasicGameObject using classmethod create" do 35 | 36 | it "should be automatically stored in $window.game_objects" do 37 | MyBasicGameObject.instances = [] 38 | 3.times { go = MyBasicGameObject.create } 39 | $window.game_objects.size.should == 3 40 | end 41 | 42 | it "should have $window as parent" do 43 | go = MyBasicGameObject.create 44 | go.parent.should == $window 45 | end 46 | 47 | it "should keep track of its instances in class#all" do 48 | MyBasicGameObject.instances = [] 49 | 3.times { MyBasicGameObject.create } 50 | # 51 | # Can/should we remove the dependency on #update here before the created objects gets saved? 52 | # We mostly protect against adding to the object array while iterating over it 53 | # 54 | MyBasicGameObject.all.size.should == 3 55 | MyBasicGameObject.size.should == 3 56 | end 57 | 58 | it "should be removed from game_objects list when destroy() is called" do 59 | MyBasicGameObject.instances = [] 60 | go = MyBasicGameObject.create 61 | $window.game_objects.size.should == 1 62 | go.destroy 63 | $window.game_objects.size.should == 0 64 | end 65 | 66 | it "should have all internal list cleared with classmethod destroy_all()" do 67 | MyBasicGameObject.instances = [] 68 | 3.times { MyBasicGameObject.create } 69 | MyBasicGameObject.destroy_all 70 | MyBasicGameObject.size.should == 0 71 | end 72 | 73 | it "should have all instances removed from parent-list with classmethod destroy_all()" do 74 | MyBasicGameObject.instances = [] 75 | 3.times { MyBasicGameObject.create } 76 | MyBasicGameObject.destroy_all 77 | $window.game_objects.size.should == 0 78 | end 79 | end 80 | 81 | context "A class inherited from BasicGameObject" do 82 | it "should return empty array on classmethod all() if no objects have been created" do 83 | # Only place MyBasicGameObject2 is used 84 | MyBasicGameObject2.all.should == [] 85 | end 86 | 87 | it "should take hash-argument, parse it and save in options" do 88 | MyBasicGameObject.instances = [] 89 | game_object = MyBasicGameObject.new(:paused => false, :foo => :bar) 90 | game_object.paused?.should == false 91 | game_object.options.should == {:paused => false, :foo => :bar} 92 | end 93 | 94 | it "should call setup() at the end of initialization" do 95 | game_object = MyBasicGameObjectWithSetup.new(:paused => false) 96 | game_object.paused?.should == true 97 | end 98 | 99 | it "should be unpaused by default" do 100 | subject.paused?.should == false 101 | end 102 | 103 | it "should change paused status with pause()/unpause()" do 104 | subject.pause 105 | subject.paused?.should == true 106 | subject.unpause 107 | subject.paused?.should == false 108 | end 109 | 110 | it "should give a well named string with filename()" do 111 | MyBasicGameObject.new.filename.should == "my_basic_game_object" 112 | end 113 | 114 | end 115 | 116 | context "when created with defaults in Chingu::Window" do 117 | it "should belong to main window it not created inside a game state" do 118 | subject.parent.should == @game 119 | end 120 | end 121 | 122 | context "when created in Chingu::GameState" do 123 | 124 | end 125 | 126 | end 127 | end -------------------------------------------------------------------------------- /spec/chingu/console_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | 5 | describe "Console" do 6 | before :each do 7 | @console = Chingu::Console.new 8 | end 9 | 10 | after :each do 11 | $window = nil 12 | end 13 | 14 | it { @console.should respond_to :start } 15 | it { @console.should respond_to :fps } 16 | it { @console.should respond_to :update } 17 | it { @console.should respond_to :root } 18 | it { @console.should respond_to :game_state_manager } 19 | it { @console.should respond_to :root } 20 | it { @console.should respond_to :milliseconds_since_last_tick } 21 | 22 | context "a new Chingu::Console" do 23 | 24 | it "should return itself as current scope" do 25 | @console.current_scope.should == @console 26 | end 27 | 28 | it "should have 0 game objects" do 29 | @console.game_objects.size.should == 0 30 | end 31 | end 32 | 33 | context "each game iteration" do 34 | 35 | it "@console.update() should call update() on all unpaused game objects" do 36 | GameObject.create.should_receive(:update) 37 | GameObject.create(:paused => true).should_not_receive(:update) 38 | @console.update 39 | end 40 | 41 | it "should increment $window.ticks" do 42 | @console.ticks.should == 0 43 | @console.update 44 | @console.ticks.should == 1 45 | @console.update 46 | @console.ticks.should == 2 47 | end 48 | 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/chingu/fpscounter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | 5 | describe FPSCounter do 6 | 7 | it { should respond_to(:fps) } 8 | it { should respond_to(:milliseconds_since_last_tick) } 9 | it { should respond_to(:ticks) } 10 | 11 | describe "#register_tick" do 12 | before do 13 | Gosu.stub(:milliseconds).and_return(1000) 14 | subject { FPSCounter.new } 15 | end 16 | 17 | it "increases the tick counter" do 18 | expect { 19 | subject.register_tick 20 | }.to change(subject, :ticks).from(0).to(1) 21 | end 22 | 23 | it "keeps track of the fps" do 24 | expect { 25 | subject.register_tick 26 | Gosu.stub(:milliseconds).and_return(1500) 27 | subject.register_tick 28 | Gosu.stub(:milliseconds).and_return(2000) 29 | subject.register_tick 30 | }.to change(subject, :fps).from(0).to(3) # #register_tick has been called 3 times within 1 second = 3 FPS 31 | end 32 | 33 | it "calculates how many milliseconds passed since last game loop iteration and returns that value" do 34 | Gosu.stub(:milliseconds).and_return(2000) 35 | subject.register_tick.should equal 1000 36 | subject.milliseconds_since_last_tick.should eql(1000) 37 | end 38 | 39 | end 40 | 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /spec/chingu/game_object_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | 5 | describe GameObjectList do 6 | before :each do 7 | @game = Chingu::Window.new 8 | end 9 | 10 | after :each do 11 | @game.close 12 | end 13 | 14 | it { should respond_to :draw } 15 | it { should respond_to :update } 16 | it { should respond_to :each } 17 | it { should respond_to :each_with_index } 18 | it { should respond_to :select } 19 | it { should respond_to :first } 20 | it { should respond_to :last } 21 | it { should respond_to :show } 22 | it { should respond_to :hide } 23 | it { should respond_to :pause } 24 | it { should respond_to :unpause} 25 | 26 | context "$window.game_objects" do 27 | it "Should return created game objects" do 28 | go1 = GameObject.create 29 | go2 = GameObject.create 30 | @game.game_objects.first.should == go1 31 | @game.game_objects.last.should == go2 32 | end 33 | 34 | it "should be able to destroy game_objects while iterating" do 35 | 10.times { GameObject.create } 36 | @game.game_objects.each_with_index do |game_object, index| 37 | game_object.destroy if index >= 5 38 | end 39 | @game.game_objects.size.should == 5 40 | end 41 | 42 | it "should call update() on all unpaused game objects" do 43 | GameObject.create.should_receive(:update) 44 | GameObject.create(:paused => true).should_not_receive(:update) 45 | @game.game_objects.update 46 | end 47 | 48 | it "should keep track of unpaused game objects" do 49 | go = GameObject.create 50 | go.should_receive(:update) 51 | @game.game_objects.update 52 | go.pause 53 | go.should_not_receive(:update) 54 | @game.game_objects.update 55 | end 56 | 57 | it "should keep track of visible game objects" do 58 | go = GameObject.create 59 | go.should_receive(:draw) 60 | @game.game_objects.draw 61 | go.hide! 62 | go.should_not_receive(:draw) 63 | @game.game_objects.draw 64 | end 65 | 66 | it "should keep track of visible game objects with show!()" do 67 | go = GameObject.create(:visible => false) 68 | go.show! 69 | go.should_receive(:draw) 70 | @game.game_objects.draw 71 | end 72 | 73 | it "should call draw() on all visible game objects" do 74 | GameObject.create.should_receive(:draw) 75 | GameObject.create(:visible => false).should_not_receive(:draw) 76 | @game.game_objects.draw 77 | end 78 | 79 | it "should call draw_relative() on all visible game objects" do 80 | GameObject.create.should_receive(:draw_relative) 81 | GameObject.create(:visible => false).should_not_receive(:draw_relative) 82 | @game.game_objects.draw_relative 83 | end 84 | 85 | it "should pause all game objects with pause!" do 86 | 5.times { GameObject.create } 87 | @game.game_objects.pause! 88 | @game.game_objects.each do |game_object| 89 | game_object.paused.should == true 90 | end 91 | end 92 | 93 | it "should hide all game objects with hide!" do 94 | 5.times { GameObject.create } 95 | @game.game_objects.hide! 96 | @game.game_objects.each do |game_object| 97 | game_object.visible.should == false 98 | end 99 | end 100 | 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /spec/chingu/helpers/input_dispatcher_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Chingu::Helpers::InputDispatcher do 4 | before :each do 5 | @subject = Object.new.extend described_class 6 | @client = Object.new 7 | end 8 | 9 | it "should respond to methods" do 10 | @subject.should respond_to :input_clients 11 | @subject.should respond_to :add_input_client 12 | @subject.should respond_to :remove_input_client 13 | end 14 | 15 | {"button_down" => :a, "button_up" => :released_a}.each_pair do |event, key| 16 | describe "#dispatch_#{event}" do 17 | it "should dispatch key event if key is handled" do 18 | @client.should_receive(:handler).with(no_args) 19 | @client.stub!(:input).with(no_args).and_return({ key => [@client.method(:handler)] }) 20 | @subject.send("dispatch_#{event}", Gosu::KbA, @client) 21 | end 22 | 23 | it "should not dispatch key event if key is not handled" do 24 | @client.stub!(:input).with(no_args).and_return({}) 25 | @subject.send("dispatch_#{event}", Gosu::KbA, @client) 26 | end 27 | end 28 | end 29 | 30 | describe "#dispatch_input_for" do 31 | before :each do 32 | $window = mock Chingu::Window 33 | $window.stub!(:button_down?).and_return(false) 34 | end 35 | 36 | after :each do 37 | $window = nil 38 | end 39 | 40 | it "should dispatch if a key is being held" do 41 | @client.should_receive(:handler).with(no_args) 42 | $window.stub!(:button_down?).with(Gosu::KbA).and_return(true) 43 | @client.stub!(:input).with(no_args).and_return({:holding_a => [@client.method(:handler)]}) 44 | @subject.dispatch_input_for(@client) 45 | end 46 | 47 | it "should do nothing if a key is not held" do 48 | @client.stub!(:input).with(no_args).and_return({:holding_a => [lambda { raise "Shouldn't handle input!"}]}) 49 | @subject.dispatch_input_for(@client) 50 | end 51 | end 52 | 53 | 54 | describe "#dispatch_actions" do 55 | it "should call a method" do 56 | @client.should_receive(:handler).with(no_args) 57 | @subject.send(:dispatch_actions, [@client.method(:handler)]) 58 | end 59 | 60 | it "should call a proc" do 61 | @client.should_receive(:handler) 62 | @subject.send(:dispatch_actions, [lambda { @client.handler }]) 63 | end 64 | 65 | it "should push a game-state instance" do 66 | state = Chingu::GameState.new 67 | @subject.should_receive(:push_game_state).with(state) 68 | @subject.send(:dispatch_actions, [state]) 69 | end 70 | 71 | it "should push a game-state class" do 72 | @subject.should_receive(:push_game_state).with(Chingu::GameState) 73 | @subject.send(:dispatch_actions, [Chingu::GameState]) 74 | end 75 | 76 | it "should call multiple actions if more have been set" do 77 | other = Object.new 78 | other.should_receive(:handler).with(no_args) 79 | @client.should_receive(:handler).with(no_args) 80 | @subject.send(:dispatch_actions, [@client.method(:handler), other.method(:handler)]) 81 | end 82 | 83 | # Note, doesn't check if a passed class is incorrect. Life is too short. 84 | it "should raise an error with unexpected data" do 85 | lambda { @subject.send(:dispatch_actions, [12]) }.should raise_error ArgumentError 86 | end 87 | end 88 | end -------------------------------------------------------------------------------- /spec/chingu/helpers/options_setter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class Car 4 | include Chingu::Helpers::OptionsSetter 5 | 6 | attr_accessor :angle, :speed 7 | attr_reader :color 8 | 9 | def initialize(params) 10 | set_options(params, { :angle => 11, :speed => 22 }) 11 | end 12 | end 13 | 14 | 15 | module Chingu 16 | module Helpers 17 | describe OptionsSetter do 18 | 19 | context "using without defaults" do 20 | before do 21 | @car = Car.new(:angle => 44) 22 | end 23 | 24 | it "should set angle from options" do 25 | @car.angle.should == 44 26 | end 27 | 28 | it "should set speed from defaults" do 29 | @car.speed.should == 22 30 | end 31 | 32 | it "should handle attribute without writer" do 33 | Car.new(:color => :green).color.should == :green 34 | end 35 | end 36 | end 37 | 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/chingu/images/droid_11x15.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/spec/chingu/images/droid_11x15.bmp -------------------------------------------------------------------------------- /spec/chingu/images/rect_20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ippa/chingu/d3ed0ff0e0fa81ebc416014095d4ae9b139c785c/spec/chingu/images/rect_20x20.png -------------------------------------------------------------------------------- /spec/chingu/inflector_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | 5 | describe Inflector do 6 | 7 | describe ".camelize" do 8 | it "camelizes strings" do 9 | subject.camelize("automatic_assets").should eql("AutomaticAssets") 10 | end 11 | end 12 | 13 | describe ".underscore" do 14 | it "converts class-like strings to underscore" do 15 | subject.underscore("FireBall").should eql("fire_ball") 16 | end 17 | end 18 | 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/chingu/input_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Chingu::Input do 4 | it "should map all defined Gosu input constants to Chinu symbols" do 5 | # Simpler if all the inputs are in a big hash. 6 | input_names = Gosu.constants.select {|constant| constant =~ /^(?:Kb|Ms|Gp)/ } 7 | input_names.delete_if {|name| name.to_s =~ /Range(?:Start|End)|Num$/ } # Special entries. 8 | gosu_inputs = input_names.inject({}) {|hash, name| hash[name] = Gosu.const_get name; hash } 9 | 10 | gosu_inputs.each_value do |code| 11 | next if code==0 # todo, check into this, spooner? ;) 12 | symbols = described_class::CONSTANT_TO_SYMBOL[code] 13 | symbols.should_not be_empty 14 | symbols.each {|s| s.should be_kind_of Symbol } 15 | end 16 | end 17 | 18 | it "should map all Chingu symbols to Gosu input codes" do 19 | described_class::CONSTANT_TO_SYMBOL.values.flatten.uniq.each do |symbol| 20 | described_class::SYMBOL_TO_CONSTANT[symbol].should be_kind_of Fixnum 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /spec/chingu/parallax_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | describe Parallax do 5 | before :each do 6 | @game = Chingu::Window.new 7 | 8 | # Gosu uses the paths based on where rspec is, not where this file is, so we need to do it manually! 9 | Gosu::Image::autoload_dirs.unshift File.join(File.dirname(File.expand_path(__FILE__)), 'images') 10 | end 11 | 12 | after :each do 13 | @game.close 14 | end 15 | 16 | describe "layers" do 17 | it "should have 3 different ways of adding layers" do 18 | subject << {:image => "rect_20x20.png", :repeat_x => true, :repeat_y => true} 19 | subject.add_layer(:image => "rect_20x20.png", :repeat_x => true, :repeat_y => true) 20 | subject << ParallaxLayer.new(:image => "rect_20x20.png", :repeat_x => true, :repeat_y => true) 21 | 22 | subject.layers.count.should equal 3 23 | end 24 | 25 | it "should have incrementing zorder" do 26 | 3.times do 27 | subject.add_layer(:image => "rect_20x20.png") 28 | end 29 | subject.layers[1].zorder.should equal (subject.layers[0].zorder + 1) 30 | subject.layers[2].zorder.should equal (subject.layers[0].zorder + 2) 31 | end 32 | 33 | it "should start incrementing zorder in layers from Parallax-instance zorder if available" do 34 | parallax = Parallax.new(:zorder => 2000) 35 | 3.times { parallax.add_layer(:image => "rect_20x20.png") } 36 | parallax.layers[0].zorder.should == 2000 37 | parallax.layers[1].zorder.should == 2001 38 | parallax.layers[2].zorder.should == 2002 39 | end 40 | 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/chingu/text_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | 5 | describe Text do 6 | 7 | it "should initialize properly" 8 | #it { should respond_to :text } 9 | #it { should respond_to :height } 10 | #it { should respond_to :gosu_font } 11 | #it { should respond_to :line_spacing } 12 | #it { should respond_to :align } 13 | #it { should respond_to :max_width } 14 | #it { should respond_to :background } 15 | 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /spec/chingu/window_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Chingu 4 | 5 | describe "Window" do 6 | before :each do 7 | @game = Chingu::Window.new 8 | end 9 | 10 | after :each do 11 | @game.close 12 | end 13 | 14 | it { @game.should respond_to :close } 15 | it { @game.should respond_to :fps } 16 | it { @game.should respond_to :update } 17 | it { @game.should respond_to :draw } 18 | it { @game.should respond_to :root } 19 | it { @game.should respond_to :game_state_manager } 20 | it { @game.should respond_to :factor } 21 | it { @game.should respond_to :cursor } 22 | it { @game.should respond_to :root } 23 | it { @game.should respond_to :milliseconds_since_last_tick } 24 | 25 | context "a new Chingu::Window" do 26 | 27 | it "should return itself as current scope" do 28 | @game.current_scope.should == @game 29 | end 30 | 31 | it "should have 0 game objects" do 32 | @game.game_objects.size.should == 0 33 | end 34 | end 35 | 36 | context "each game iteration" do 37 | it "$window.update() should call update() on all unpaused game objects" do 38 | GameObject.create.should_receive(:update) 39 | GameObject.create(:paused => true).should_not_receive(:update) 40 | @game.update 41 | end 42 | 43 | it "$window.draw() should call draw() on all visible game objects" do 44 | GameObject.create.should_receive(:draw) 45 | @game.draw 46 | end 47 | 48 | it "$window.draw() should not call draw() on invisible game objects" do 49 | GameObject.create(:visible => false).should_not_receive(:draw) 50 | @game.game_objects.first.visible?.should == false 51 | @game.draw 52 | end 53 | 54 | it "should increment $window.ticks" do 55 | @game.ticks.should == 0 56 | @game.update 57 | @game.ticks.should == 1 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 4 | 5 | require 'rubygems' 6 | require 'rspec' 7 | require 'chingu/require_all' 8 | 9 | require 'chingu' 10 | 11 | def media_path(file) 12 | File.join($window.root, "..", "..", "examples", "media", file) 13 | end 14 | 15 | if defined?(Rcov) 16 | # all_app_files = Dir.glob('lib/**/*.rb') 17 | # all_app_files.each{|rb| require rb} 18 | end 19 | 20 | -------------------------------------------------------------------------------- /specs.watchr: -------------------------------------------------------------------------------- 1 | # Run me with: 2 | # 3 | # $ watchr specs.watchr 4 | 5 | # -------------------------------------------------- 6 | # Convenience Methods 7 | # -------------------------------------------------- 8 | # [32m [0m 9 | def growl(message) 10 | growlnotify = `which growlnotify`.chomp 11 | image = !message.include?('0 failures') ? "~/.watchr_images/failed.png" : "~/.watchr_images/passed.png" 12 | 13 | status = image =~ /failed/ ? '[31m' : '[32m' 14 | 15 | message = message.split('seconds')[1].split(status)[1].split('[0m')[0] 16 | 17 | options = "'Watchr - Chingu specs' -w --image '#{File.expand_path(image)}' -m '#{message}'" 18 | system %(#{growlnotify} #{options} &) 19 | end 20 | 21 | def all_spec_files 22 | Dir['spec/**/*_spec.rb'] 23 | end 24 | 25 | def run_spec_matching(thing_to_match) 26 | matches = all_spec_files.grep(/#{thing_to_match}/i) 27 | if matches.empty? 28 | puts "Sorry, thanks for playing, but there were no matches for #{thing_to_match}" 29 | else 30 | run matches.join(' ') 31 | end 32 | end 33 | 34 | def run(files_to_run) 35 | puts("Running: #{files_to_run}") 36 | result = `rspec -cfs #{files_to_run}` 37 | print result 38 | growl result rescue nil 39 | no_int_for_you 40 | end 41 | 42 | def run_all_specs 43 | run(all_spec_files.join(' ')) 44 | end 45 | 46 | # -------------------------------------------------- 47 | # Watchr Rules 48 | # -------------------------------------------------- 49 | watch('^spec/(.*)_spec\.rb') { |m| run_spec_matching(m[1]) } 50 | watch('^lib/(.*)\.rb') { |m| run_spec_matching(m[1]) } 51 | watch('^spec/spec_helper\.rb') { run_all_specs } 52 | watch('^spec/support/.*\.rb') { run_all_specs } 53 | 54 | # -------------------------------------------------- 55 | # Signal Handling 56 | # -------------------------------------------------- 57 | 58 | def no_int_for_you 59 | @sent_an_int = nil 60 | end 61 | 62 | Signal.trap 'INT' do 63 | if @sent_an_int then 64 | puts " A second INT? Ok, I get the message. Shutting down now." 65 | exit 66 | else 67 | puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again." 68 | @sent_an_int = true 69 | Kernel.sleep 1.5 70 | run_all_specs 71 | end 72 | end 73 | 74 | # vim:ft=ruby 75 | --------------------------------------------------------------------------------