chingu 0.8rc2 → 0.8rc3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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