chingu 0.8rc2 → 0.8rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.rdoc +66 -19
  2. data/benchmarks/arrays_bench.rb +22 -0
  3. data/benchmarks/game_objects_benchmark.rb +29 -35
  4. data/chingu.gemspec +16 -2
  5. data/examples/example18_animation_trait.rb +14 -9
  6. data/examples/example19.yml +305 -561
  7. data/examples/example19_edit_viewport.rb +3 -1
  8. data/examples/example1_basics.rb +4 -4
  9. data/examples/example21.yml +62 -54
  10. data/examples/example21_sidescroller_with_edit.rb +13 -21
  11. data/examples/example4_gamestates.rb +0 -2
  12. data/examples/example7_gfx_helpers.rb +1 -1
  13. data/examples/example8_traits.rb +1 -2
  14. data/examples/example9_collision_detection.rb +1 -1
  15. data/examples/game1.rb +1 -1
  16. data/lib/chingu.rb +1 -2
  17. data/lib/chingu/animation.rb +13 -5
  18. data/lib/chingu/assets.rb +2 -2
  19. data/lib/chingu/basic_game_object.rb +33 -30
  20. data/lib/chingu/game_object.rb +0 -5
  21. data/lib/chingu/game_object_list.rb +34 -21
  22. data/lib/chingu/game_states/edit.rb +2 -5
  23. data/lib/chingu/game_states/fade_to.rb +0 -1
  24. data/lib/chingu/game_states/pause.rb +1 -1
  25. data/lib/chingu/helpers/game_object.rb +0 -1
  26. data/lib/chingu/parallax.rb +3 -9
  27. data/lib/chingu/simple_menu.rb +7 -8
  28. data/lib/chingu/traits/animation.rb +2 -4
  29. data/lib/chingu/traits/sprite.rb +16 -13
  30. data/lib/chingu/window.rb +1 -1
  31. data/spec/chingu/animation_spec.rb +88 -0
  32. data/spec/chingu/assets_spec.rb +58 -0
  33. data/spec/chingu/basic_game_object_spec.rb +111 -0
  34. data/spec/chingu/game_object_list_spec.rb +78 -0
  35. data/spec/chingu/game_object_spec.rb +50 -14
  36. data/spec/chingu/game_state_manager_spec.rb +105 -0
  37. data/spec/chingu/helpers/input_client_spec.rb +4 -0
  38. data/spec/chingu/helpers/input_dispatcher_spec.rb +4 -0
  39. data/spec/chingu/images/droid_11x15.bmp +0 -0
  40. data/spec/chingu/parallax_spec.rb +25 -16
  41. data/spec/chingu/window_spec.rb +55 -0
  42. data/spec/spec_helper.rb +1 -5
  43. metadata +17 -3
@@ -25,7 +25,7 @@ module Chingu
25
25
  # A chingu trait providing automatic loading of tile-animations
26
26
  #
27
27
  # For example:
28
- # class FireBall; has_traits :animation; end;
28
+ # class FireBall; traits :animation; end;
29
29
  #
30
30
  # Will automatically load:
31
31
  # - fire_ball.png into self.animations[:default]
@@ -59,13 +59,11 @@ module Chingu
59
59
  Dir[glob].each do |tile_file|
60
60
  puts tile_file if trait_options[:animation][:debug]
61
61
  if tile_file =~ /[a-zA-Z\_+]_*(\d+)x(\d+)_*([a-zA-Z]*)\.(bmp|png)/
62
- state = $3.length > 0 ? $3 : "default"
62
+ state = $3.length > 0 ? $3 : "default"
63
63
  animations[state.to_sym] = Chingu::Animation.new(trait_options[:animation].merge(:file => tile_file))
64
64
  elsif tile_file =~ /[a-zA-Z\_+]\.(bmp|png)/
65
- puts tile_file
66
65
  animations[:default] = Chingu::Animation.new(trait_options[:animation].merge(:file => tile_file))
67
66
  end
68
-
69
67
  end
70
68
  return animations
71
69
  end
@@ -43,10 +43,9 @@ module Chingu
43
43
  def setup_trait(object_options = {})
44
44
  # default settings for all variables unless set in constructor
45
45
  defaults = {
46
- :x => 0, :y => 0, :angle => 0,
47
- :factor => ($window.factor||1.0),
46
+ :x => 0, :y => 0, :angle => 0, :factor => ($window.factor||1.0),
48
47
  :zorder => 100, :center_x => 0.5, :center_y => 0.5,
49
- :mode => :default, :color => nil
48
+ :mode => :default, :color => nil, :visible => true
50
49
  }
51
50
 
52
51
  # if user specs :image take care of it first since width, height etc depends on it.
@@ -58,7 +57,7 @@ module Chingu
58
57
  end
59
58
 
60
59
  def color=(color)
61
- @color = color.is_a?(Gosu::Color) ? color : Gosu::Color.new(color || 0xFFFFFFFF)
60
+ @color = color.is_a?(Gosu::Color) ? color : Gosu::Color.new(color || 0xFFFFFFFF)
62
61
  end
63
62
 
64
63
  #
@@ -111,12 +110,14 @@ module Chingu
111
110
  # Usually better to have a large image and make it smaller then the other way around.
112
111
  #
113
112
  def width=(width)
114
- @factor_x = width.to_f / @image.width.to_f
113
+ @factor_x = width.to_f / @image.width.to_f if @image
115
114
  end
116
-
117
- # Get effective on width by calculating it from image-width and factor
115
+
116
+ #
117
+ # Get effective width by calculating it from image-width and factor_x
118
+ #
118
119
  def width
119
- (@image.width * @factor_x).abs
120
+ (@image.width * @factor_x).abs if @image
120
121
  end
121
122
 
122
123
  #
@@ -125,12 +126,14 @@ module Chingu
125
126
  # Usually better to have a large image and make it smaller then the other way around.
126
127
  #
127
128
  def height=(height)
128
- @factor_y = height.to_f / @image.height.to_f
129
+ @factor_y = height.to_f / @image.height.to_f if @image
129
130
  end
130
131
 
131
- # Get effective on heightby calculating it from image-width and factor
132
+ #
133
+ # Get effective height by calculating it from image-width and factor
134
+ #
132
135
  def height
133
- (@image.height.to_f * @factor_y).abs
136
+ (@image.height.to_f * @factor_y).abs if @image
134
137
  end
135
138
 
136
139
  # Set width and height in one swoop
@@ -216,14 +219,14 @@ module Chingu
216
219
  # Our encapsulation of GOSU's image.draw_rot, uses the objects variables to draw it on screen if @visible is true
217
220
  #
218
221
  def draw
219
- draw_relative
222
+ @image.draw_rot(@x, @y, @zorder, @angle, @center_x, @center_y, @factor_x, @factor_y, @color, @mode) if @image
220
223
  end
221
224
 
222
225
  #
223
226
  # Works as #draw() but takes offsets for all draw_rot()-arguments. Used among others by the viewport-trait.
224
227
  #
225
228
  def draw_relative(x=0, y=0, zorder=0, angle=0, center_x=0, center_y=0, factor_x=0, factor_y=0)
226
- @image.draw_rot(@x+x, @y+y, @zorder+zorder, @angle+angle, @center_x+center_x, @center_y+center_y, @factor_x+factor_x, @factor_y+factor_y, @color, @mode)
229
+ @image.draw_rot(@x+x, @y+y, @zorder+zorder, @angle+angle, @center_x+center_x, @center_y+center_y, @factor_x+factor_x, @factor_y+factor_y, @color, @mode) if @image
227
230
  end
228
231
 
229
232
  #
@@ -128,7 +128,7 @@ module Chingu
128
128
  # Without this self.fps would return an incorrect value.
129
129
  # If you override this in your Chingu::Window class, make sure to call super.
130
130
  #
131
- @milliseconds_since_last_tick = @fps_counter.register_tick
131
+ @milliseconds_since_last_tick = @fps_counter.register_tick
132
132
 
133
133
  intermediate_update
134
134
  end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ module Chingu
4
+ describe Animation do
5
+ before :each do
6
+ @game = Chingu::Window.new
7
+ @test_dir = File.join(File.dirname(File.expand_path(__FILE__)), 'images')
8
+ @animation_clean = Animation.new(:file => File.join(@test_dir, "droid_11x15.bmp"))
9
+ @animation = Animation.new(:file => File.join(@test_dir, "droid_11x15.bmp"), :delay => 0)
10
+ end
11
+
12
+ after :each do
13
+ @game.close
14
+ end
15
+
16
+ describe "newly initialized object" do
17
+ it "should have default values" do
18
+ @animation_clean.bounce.should == false
19
+ @animation_clean.loop.should == true
20
+ @animation_clean.delay.should == 100
21
+ @animation_clean.index.should == 0
22
+ @animation_clean.step.should == 1
23
+ end
24
+ end
25
+
26
+ describe "Animation loading file: droid_11x15.bmp" do
27
+ it "should detect size and frames automatically from filename" do
28
+ @animation.size.should == [11,15]
29
+ @animation.frames.count.should == 14
30
+ end
31
+
32
+ it "should give correct frames for .first and .last" do
33
+ @animation.first.should == @animation.frames.first
34
+ @animation.last.should == @animation.frames.last
35
+ end
36
+
37
+ it "should return frame with []" do
38
+ @animation[0].should == @animation.frames.first
39
+ end
40
+
41
+ it "should step animation forward with .next" do
42
+ @animation.next
43
+ @animation.index.should == 1
44
+ end
45
+
46
+ it "should stop animation when reaching end if loop and bounce are both false" do
47
+ @animation.loop = false
48
+ @animation.bounce = false
49
+ @animation.index = 14
50
+ @animation.next
51
+ @animation.index.should == 14
52
+ end
53
+
54
+ it "should loop animation when reaching end if loop is true" do
55
+ @animation.index = 14
56
+ @animation.next
57
+ @animation.index.should == 0
58
+ end
59
+
60
+ it "should bounce animation when reaching end if bounce is true" do
61
+ @animation.bounce = true
62
+ @animation.index = 14
63
+ @animation.next
64
+ @animation.index.should == 13
65
+ end
66
+
67
+ it "should use .step when moving animation forward" do
68
+ @animation.step = 5
69
+ @animation.next
70
+ @animation.index.should == 5
71
+ @animation.next
72
+ @animation.index.should == 10
73
+ end
74
+
75
+ it "should handle 'frame_names' pointing to a new animation containing a subset of the original frames" do
76
+ @animation.frame_names = { :scan => 0..5, :up => 6..7, :down => 8..9, :left => 10..11, :right => 12..13 }
77
+
78
+ @animation[:scan].should be_kind_of(Animation)
79
+ @animation[:scan].frames.count.should == 6
80
+ @animation[:up].frames.count.should == 2
81
+ @animation[:down].frames.count.should == 2
82
+ @animation[:left].frames.count.should == 2
83
+ @animation[:right].frames.count.should == 2
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,58 @@
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
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ class MyBasicGameObject < Chingu::BasicGameObject
4
+ end
5
+
6
+ class MyBasicGameObjectWithSetup < Chingu::BasicGameObject
7
+ def setup
8
+ @paused = true
9
+ end
10
+ end
11
+
12
+ module Chingu
13
+
14
+ describe BasicGameObject do
15
+ before :each do
16
+ @game = Chingu::Window.new
17
+ end
18
+
19
+ after :each do
20
+ @game.close
21
+ end
22
+
23
+ it { should respond_to(:options) }
24
+ it { should respond_to(:paused) }
25
+ it { should respond_to(:setup_trait) }
26
+ it { should respond_to(:setup) }
27
+ it { should respond_to(:update_trait) }
28
+ it { should respond_to(:draw_trait) }
29
+ it { should respond_to(:filename) }
30
+
31
+ context "A class inherited from BasicGameObject" do
32
+ it "should be automatically stored in $window.game_objects" do
33
+ 3.times { go = MyBasicGameObject.create }
34
+ $window.update
35
+ $window.game_objects.size.should == 3
36
+ end
37
+
38
+ it "should have $window as parent" do
39
+ go = MyBasicGameObject.create
40
+ go.parent.should == $window
41
+ end
42
+
43
+ it "should keep track of its instances in class#all" do
44
+ 3.times { MyBasicGameObject.create }
45
+ #
46
+ # Can/should we remove the dependency on #update here before the created objects gets saved?
47
+ # We mostly protect against adding to the object array while iterating over it
48
+ #
49
+ $window.update
50
+ MyBasicGameObject.all.size.should == 3
51
+ MyBasicGameObject.size.should == 3
52
+ end
53
+
54
+ it "should be removed from game_objects list when destroy() is called" do
55
+ go = MyBasicGameObject.create
56
+ $window.update
57
+ $window.game_objects.size.should == 1
58
+ go.destroy
59
+ $window.update
60
+ $window.game_objects.size.should == 0
61
+ end
62
+
63
+ it "should have all instances removed with classmethod #destroy_all()" do
64
+ 3.times { MyBasicGameObject.create }
65
+ $window.update
66
+ MyBasicGameObject.destroy_all
67
+ $window.update
68
+ $window.game_objects.size.should == 0
69
+ end
70
+
71
+ it "should take hash-argument, parse it and save in options" do
72
+ game_object = MyBasicGameObject.new(:paused => false, :foo => :bar)
73
+ game_object.paused?.should == false
74
+ game_object.options.should == {:paused => false, :foo => :bar}
75
+ end
76
+
77
+ it "should call setup() at the end of initialization" do
78
+ game_object = MyBasicGameObjectWithSetup.new(:paused => false)
79
+ game_object.paused?.should == true
80
+ end
81
+
82
+ it "should be unpaused by default" do
83
+ subject.paused?.should == false
84
+ end
85
+
86
+ it "should change paused status with pause()/unpause()" do
87
+ subject.pause
88
+ subject.paused?.should == true
89
+ subject.unpause
90
+ subject.paused?.should == false
91
+ end
92
+
93
+ it "should give a well named string with filename()" do
94
+ MyBasicGameObject.new.filename.should == "my_basic_game_object"
95
+ end
96
+
97
+ end
98
+
99
+ context "when created with defaults in Chingu::Window" do
100
+
101
+ it "should belong to main window it not created inside a game state" do
102
+ subject.parent.should == @game
103
+ end
104
+ end
105
+
106
+ context "when created in Chingu::GameState" do
107
+
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,78 @@
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 call draw() on all visible game objects" do
49
+ GameObject.create.should_receive(:draw)
50
+ GameObject.create(:visible => false).should_not_receive(:draw)
51
+ @game.game_objects.draw
52
+ end
53
+
54
+ it "should call draw_relative() on all visible game objects" do
55
+ GameObject.create.should_receive(:draw_relative)
56
+ GameObject.create(:visible => false).should_not_receive(:draw_relative)
57
+ @game.game_objects.draw_relative
58
+ end
59
+
60
+ it "should pause all game objects with pause!" do
61
+ 5.times { GameObject.create }
62
+ @game.game_objects.pause!
63
+ @game.game_objects.each do |game_object|
64
+ game_object.paused.should == true
65
+ end
66
+ end
67
+
68
+ it "should hide all game objects with hide!" do
69
+ 5.times { GameObject.create }
70
+ @game.game_objects.hide!
71
+ @game.game_objects.each do |game_object|
72
+ game_object.visible.should == false
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -3,13 +3,17 @@ require 'spec_helper'
3
3
  module Chingu
4
4
 
5
5
  describe GameObject do
6
- before :all do
6
+ before :each do
7
7
  @game = Chingu::Window.new
8
8
 
9
9
  # Gosu uses the paths based on where rspec is, not where this file is, so we need to do it manually!
10
10
  Gosu::Image::autoload_dirs.unshift File.join(File.dirname(File.expand_path(__FILE__)), 'images')
11
11
  end
12
12
 
13
+ after :each do
14
+ @game.close
15
+ end
16
+
13
17
  it { should respond_to(:x) }
14
18
  it { should respond_to(:y) }
15
19
  it { should respond_to(:angle) }
@@ -30,6 +34,7 @@ module Chingu
30
34
  subject.angle.should == 0
31
35
  subject.x.should == 0
32
36
  subject.y.should == 0
37
+ subject.zorder.should == 100
33
38
  subject.factor_x.should == 1
34
39
  subject.factor_y.should == 1
35
40
  subject.center_x.should == 0.5
@@ -141,22 +146,53 @@ module Chingu
141
146
  end
142
147
  end
143
148
 
144
- #context "when there's a global factor/scale" do
145
- # $window = Chingu::Window.new
146
- # $window.factor = 2
147
- # subject { described_class.new(:image => "rect_20x20.png") }
148
- # it "should use global factor/scale" do
149
- # subject.factor_x.should == 2
150
- # subject.factor_y.should == 2
151
- # subject.width.should == 40
152
- # subject.height.should == 40
153
- # end
154
- #end
149
+ context "when there's a global factor/scale" do
150
+ before :each do
151
+ $window.factor = 2
152
+ end
153
+
154
+ subject { described_class.new(:image => "rect_20x20.png") }
155
155
 
156
- after(:all) do
157
- $window.close
156
+ it "should use global factor/scale" do
157
+ subject.factor_x.should == 2
158
+ subject.factor_y.should == 2
159
+ subject.width.should == 40
160
+ subject.height.should == 40
161
+ end
158
162
  end
159
163
 
164
+ context "when there's missing parts" do
165
+ it "should return nil on width and height if there's no image available" do
166
+ game_object = GameObject.new
167
+ game_object.width.should == nil
168
+ game_object.height.should == nil
169
+ end
170
+ end
171
+
172
+ context "class methods" do
173
+ it "should go through all instances of class on #each" do
174
+ go1 = GameObject.create
175
+ go2 = GameObject.create
176
+
177
+ index = 0
178
+ GameObject.each do |game_object|
179
+ game_object.should == go1 if index==0
180
+ game_object.should == go2 if index==1
181
+ index += 1
182
+ end
183
+ end
184
+
185
+ it "should go through all instances of class on #each_with_index" do
186
+ go1 = GameObject.create
187
+ go2 = GameObject.create
188
+
189
+ GameObject.each_with_index do |game_object, index|
190
+ game_object.should == go1 if index==0
191
+ game_object.should == go2 if index==1
192
+ end
193
+ end
194
+
195
+ end
160
196
  end
161
197
 
162
198
  end