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
@@ -79,7 +79,7 @@ class Level < Chingu::GameState
79
79
  game_objects.destroy_if { |game_object| game_object.respond_to?("outside_window?") && game_object.outside_window? }
80
80
 
81
81
  # Collide bullets with terrain
82
- Bullet.all.select { |o| solid_pixel_at?(o.x, o.y)}.each { |o| o.die }
82
+ Bullet.select { |o| solid_pixel_at?(o.x, o.y)}.each { |o| o.die }
83
83
 
84
84
  # Collide player with terrain
85
85
  push_game_state(GameOver) if solid_pixel_at?(@player.x, @player.y)
@@ -37,8 +37,7 @@ require_all "#{CHINGU_ROOT}/chingu/traits"
37
37
  require_all "#{CHINGU_ROOT}/chingu"
38
38
 
39
39
  module Chingu
40
- # VERSION = "0.7.7.5"
41
- VERSION = "0.8rc2"
40
+ VERSION = "0.8rc3"
42
41
 
43
42
  DEBUG_COLOR = Gosu::Color.new(0xFFFF0000)
44
43
  DEBUG_ZORDER = 9999
@@ -10,7 +10,7 @@ module Chingu
10
10
  # Is autodetection of width / height possible?
11
11
  #
12
12
  class Animation
13
- attr_accessor :frames, :delay, :step, :loop, :bounce, :step
13
+ attr_accessor :frames, :delay, :step, :loop, :bounce, :step, :index
14
14
 
15
15
  #
16
16
  # Create a new Animation.
@@ -107,18 +107,25 @@ module Chingu
107
107
  end
108
108
 
109
109
  #
110
- # Returns first frame (GOSU::Image) from animation
110
+ # Returns the first frame (Gosu::Image) from animation
111
111
  #
112
112
  def first
113
113
  @frames.first
114
114
  end
115
115
 
116
116
  #
117
- # Returns last frame (GOSU::Image) from animation
117
+ # Returns the last frame (Gosu::Image) from animation
118
118
  #
119
119
  def last
120
120
  @frames.last
121
121
  end
122
+
123
+ #
124
+ # [width, height] for each frame in the animation
125
+ #
126
+ def size
127
+ [@width, @height]
128
+ end
122
129
 
123
130
  #
124
131
  # Returns true if the current frame is the last
@@ -151,7 +158,7 @@ module Chingu
151
158
  end
152
159
 
153
160
  #
154
- # Get the current frame (a Gosu#Image)
161
+ # Get the current frame (a Gosu::Image)
155
162
  #
156
163
  def image
157
164
  @frames[@index]
@@ -186,7 +193,7 @@ module Chingu
186
193
  #
187
194
  def next(recursion = true)
188
195
 
189
- if (@dt += $window.milliseconds_since_last_tick) > @delay
196
+ if (@dt += $window.milliseconds_since_last_tick) >= @delay
190
197
  @dt = 0
191
198
  @previous_index = @index
192
199
  @index += @step
@@ -196,6 +203,7 @@ module Chingu
196
203
  if @bounce
197
204
  @step *= -1 # invert number
198
205
  @index += @step
206
+ @index += @step
199
207
  elsif @loop
200
208
  @index = 0
201
209
  else
@@ -101,7 +101,7 @@ module Gosu
101
101
  include Chingu::NamedResource
102
102
 
103
103
  def self.autoload(name)
104
- (path = find_file(name)) ? Gosu::Song.new($window, path) : nil
104
+ (path = find_file(name)) ? Gosu::Song.new(path) : nil
105
105
  end
106
106
  end
107
107
 
@@ -109,7 +109,7 @@ module Gosu
109
109
  include Chingu::NamedResource
110
110
 
111
111
  def self.autoload(name)
112
- (path = find_file(name)) ? Gosu::Sample.new($window, path) : nil
112
+ (path = find_file(name)) ? Gosu::Sample.new(path) : nil
113
113
  end
114
114
  end
115
115
  Sound = Sample # Gosu uses Sample, but Sound makes sense too.
@@ -10,7 +10,7 @@ module Chingu
10
10
  class BasicGameObject
11
11
  include Chingu::Helpers::ClassInheritableAccessor # adds classmethod class_inheritable_accessor
12
12
 
13
- attr_reader :options, :paused, :visible
13
+ attr_reader :options, :paused
14
14
  attr_accessor :parent
15
15
 
16
16
  class_inheritable_accessor :trait_options
@@ -24,7 +24,6 @@ module Chingu
24
24
  def self.trait(trait, options = {})
25
25
 
26
26
  if trait.is_a?(::Symbol) || trait.is_a?(::String)
27
- ## puts "trait #{trait}, #{options}"
28
27
  begin
29
28
  # Convert user-given symbol (eg. :timer) to a Module (eg. Chingu::Traits::Timer)
30
29
  mod = Chingu::Traits.const_get(Chingu::Inflector.camelize(trait))
@@ -56,11 +55,7 @@ module Chingu
56
55
  Array(traits).each { |trait_name| trait trait_name }
57
56
  end
58
57
  class << self; alias :has_traits :traits; end
59
-
60
- #def self.inherited(subclass)
61
- # subclass.initialize_inherited_trait if subclass.method_defined?(:initialize_inherited_trait)
62
- #end
63
-
58
+
64
59
  alias :game_state :parent
65
60
  alias :game_state= :parent=
66
61
 
@@ -75,17 +70,11 @@ module Chingu
75
70
  #
76
71
  # A GameObject either belong to a GameState or our mainwindow ($window)
77
72
  #
78
- #if !@parent && $window && $window.respond_to?(:game_state_manager)
79
- # @parent = $window.game_state_manager.inside_state || $window
80
- #end
81
73
  @parent = $window.current_scope if !@parent && $window
82
74
 
83
75
  # if true, BasicGameObject#update will be called
84
- @paused = options[:paused] || false
76
+ @paused = options[:paused] || options[:pause] || false
85
77
 
86
- # if true, BasicGameObject#draw will be called
87
- @visible = options[:visible] || true
88
-
89
78
  # This will call #setup_trait on the latest trait mixed in
90
79
  # which then will pass it on to the next setup_trait() with a super-call.
91
80
  setup_trait(options)
@@ -94,14 +83,14 @@ module Chingu
94
83
  end
95
84
 
96
85
  #
97
- # Creates a new object from class just as new() but also:
98
- # - adds game object to current game state
99
- # - or $window if no game state exists
86
+ # Works just as BasicGameObject#new with the addition that Chingu will keep track of the new object.
87
+ # The object will be assigned to a game_objects list. If created within a game state it will be added to that_game_state.game_objects.
88
+ # Otherwise it will be added to $window.game_objects list.
89
+ # The naming is inspired from ActiveRecord#create which will persist the object in the database right away.
100
90
  #
101
- # Use create() instead of new() if you want to keep track of your objects through
102
- # Chingus "game_objects" which is available in all game states and the main window.
91
+ # Chingu will automatically call update() and draw() on stored game objects.
92
+ # Often in a smaller game this is exaclty what you want. If not, use the normal new().
103
93
  #
104
- #def self.create(options = {})
105
94
  def self.create(*options, &block)
106
95
  instance = self.new(*options, &block)
107
96
 
@@ -113,15 +102,7 @@ module Chingu
113
102
 
114
103
  return instance
115
104
  end
116
-
117
- #
118
- # This ruby callback is called each time someone subclasses BasicGameObject or GameObject
119
- # We hook into it to keep track of all game object classes (just as we keep track of game objects instances)
120
- #
121
- ## def self.inherited(klass)
122
- ## instance.parent.add_game_object_class(klass) if instance.parent
123
- ## end
124
-
105
+
125
106
  #
126
107
  # Disable automatic calling of update() and update_trait() each game loop
127
108
  #
@@ -176,7 +157,29 @@ module Chingu
176
157
  end
177
158
 
178
159
  #
179
- # Returns
160
+ # As Array.each on the instances of the current class
161
+ #
162
+ def self.each
163
+ all.each { |object| yield object }
164
+ end
165
+
166
+ #
167
+ # As Array.each_with_index on the instances of the current class
168
+ #
169
+ def self.each_with_index
170
+ all.each_with_index { |object, index| yield object, index }
171
+ end
172
+
173
+ #
174
+ # As Array.select but on the instances of current class
175
+ #
176
+ def self.select
177
+ all.select { |object| yield object }
178
+ end
179
+
180
+
181
+ #
182
+ # Returns the total amount of game objects based on this class
180
183
  #
181
184
  def self.size
182
185
  $window.current_scope.game_objects.of_class(self).size
@@ -30,10 +30,5 @@ module Chingu
30
30
  class GameObject < Chingu::BasicGameObject
31
31
  trait :sprite
32
32
  include Chingu::Helpers::InputClient # Adds input and input=
33
-
34
- #def initialize(options = {})
35
- # super
36
- # setup
37
- #end
38
33
  end
39
34
  end
@@ -29,8 +29,8 @@ module Chingu
29
29
 
30
30
  def initialize(options = {})
31
31
  @game_objects = options[:game_objects] || []
32
- @add_game_objects = []
33
- @remove_game_objects = []
32
+ @add_game_objects = []
33
+ @remove_game_objects = []
34
34
  end
35
35
 
36
36
  def to_s
@@ -48,13 +48,15 @@ module Chingu
48
48
  alias :remove_all :destroy_all
49
49
 
50
50
  def add_game_object(object)
51
- @add_game_objects.push(object)
51
+ @game_objects.push(object)
52
+ #@add_game_objects.push(object)
52
53
  end
53
54
 
54
55
  def remove_game_object(object)
55
- @remove_game_objects.push(object)
56
+ @game_objects.delete(object)
57
+ #@remove_game_objects.push(object)
56
58
  end
57
-
59
+
58
60
  def destroy_if
59
61
  @game_objects.reject! { |object| yield(object) }
60
62
  end
@@ -68,44 +70,46 @@ module Chingu
68
70
  end
69
71
 
70
72
  def draw
71
- @game_objects.each{ |object| object.visible }.each do |object|
73
+ @game_objects.select { |object| object.visible }.each do |object|
72
74
  object.draw_trait
73
75
  object.draw
74
76
  end
75
77
  end
76
78
 
77
79
  def draw_relative(x=0, y=0, zorder=0, angle=0, center_x=0, center_y=0, factor_x=0, factor_y=0)
78
- @game_objects.each{ |object| object.visible }.each do |object|
80
+ @game_objects.select { |object| object.visible }.each do |object|
79
81
  object.draw_trait
80
82
  object.draw_relative(x, y, zorder, angle, center_x, center_y, factor_x, factor_y)
81
83
  end
82
84
  end
83
-
84
- def sync
85
- @game_objects += @add_game_objects
86
- @add_game_objects.clear
87
-
88
- @game_objects -= @remove_game_objects
89
- @remove_game_objects.clear
90
- end
91
-
85
+
92
86
  def update
93
- sync
94
-
95
- @game_objects.select{ |object| not object.paused }.each do |object|
87
+ @game_objects.select { |object| not object.paused }.each do |object|
96
88
  object.update_trait
97
89
  object.update
98
90
  end
99
91
  end
100
92
 
101
93
  def each
102
- @game_objects.each { |object| yield object }
94
+ @game_objects.dup.each { |object| yield object }
95
+ end
96
+
97
+ def each_with_index
98
+ @game_objects.dup.each_with_index { |object, index| yield object, index }
103
99
  end
104
100
 
105
101
  def select
106
- @game_objects.select { |object| yield object }
102
+ @game_objects.dup.select { |object| yield object }
107
103
  end
108
104
 
105
+ def first
106
+ @game_objects.first
107
+ end
108
+
109
+ def last
110
+ @game_objects.last
111
+ end
112
+
109
113
  #
110
114
  # Disable automatic calling of update() and update_trait() each game loop for all game objects
111
115
  #
@@ -138,5 +142,14 @@ module Chingu
138
142
  end
139
143
  alias :show :show!
140
144
 
145
+ private
146
+
147
+ def sync
148
+ @game_objects += @add_game_objects
149
+ @add_game_objects.clear
150
+
151
+ @game_objects -= @remove_game_objects
152
+ @remove_game_objects.clear
153
+ end
141
154
  end
142
155
  end
@@ -244,11 +244,7 @@ END_OF_STRING
244
244
  #
245
245
  # UPDATE
246
246
  #
247
- def update
248
- # Sync all changes to previous game states game objects list
249
- # This is needed since we don't call update on it.
250
- previous_game_state.game_objects.sync
251
-
247
+ def update
252
248
  super
253
249
 
254
250
  @status_text.text = "#{self.mouse_x.to_i} / #{self.mouse_y.to_i}"
@@ -467,6 +463,7 @@ END_OF_STRING
467
463
  def game_object_icon_at(x, y)
468
464
  game_objects.select do |game_object|
469
465
  next if game_object.is_a? Text
466
+ next unless game_object.image
470
467
  bounding_box(game_object).collide_point?(x,y)
471
468
  end.first
472
469
  end
@@ -41,7 +41,6 @@ module Chingu
41
41
 
42
42
  @new_game_state = new_game_state
43
43
  @new_game_state = new_game_state.new if new_game_state.is_a? Class
44
- @new_game_state.game_objects.sync
45
44
  end
46
45
 
47
46
  def setup
@@ -33,7 +33,7 @@ module Chingu
33
33
 
34
34
  def initialize(options = {})
35
35
  super
36
- @white = Color.new(255,255,255,255)
36
+ @white = Gosu::Color.new(255,255,255,255)
37
37
  @color = Gosu::Color.new(200,0,0,0)
38
38
  @font = Gosu::Font[35]
39
39
  @text = "PAUSED - press ESC to return to game."
@@ -75,7 +75,6 @@ module Chingu
75
75
  end
76
76
  end
77
77
  end
78
- self.game_objects.sync
79
78
  end
80
79
 
81
80
  #
@@ -159,7 +159,6 @@ module Chingu
159
159
  # put the last layer added on top of the rest.
160
160
  #
161
161
  class ParallaxLayer < Chingu::GameObject
162
- @@zorder_counter = 0
163
162
  attr_reader :damping
164
163
  attr_accessor :repeat_x, :repeat_y
165
164
 
@@ -178,9 +177,9 @@ module Chingu
178
177
  options = {
179
178
  :repeat_x => true,
180
179
  :repeat_y => false,
181
- :zorder => (@@zorder_counter+=1)
180
+ :zorder => @parallax ? (@parallax.zorder + @parallax.layers.count) : 100
182
181
  }.merge(options)
183
-
182
+
184
183
  @repeat_x = options[:repeat_x]
185
184
  @repeat_y = options[:repeat_y]
186
185
 
@@ -188,12 +187,7 @@ module Chingu
188
187
 
189
188
  @damping = options[:damping] || 1
190
189
  end
191
-
192
- def draw
193
- super
194
- #p "draw @ #{x} / #{y}: #{image} - #{zorder} - #{color}"
195
- end
196
-
190
+
197
191
  #
198
192
  # Gets pixel from layers image
199
193
  # The pixel is from the window point of view, so coordinates are converted:
@@ -25,21 +25,20 @@ module Chingu
25
25
  #
26
26
  class SimpleMenu < BasicGameObject
27
27
  include Chingu::Helpers::InputClient
28
- attr_accessor :menu_items
28
+ attr_accessor :menu_items, :visible
29
29
 
30
30
  def initialize(options = {})
31
31
  super
32
32
 
33
- @menu_items = options.delete(:menu_items)# || {"Exit" => :exit}
34
-
33
+ @menu_items = options.delete(:menu_items) # || {"Exit" => :exit}
35
34
  @x = options.delete(:x) || $window.width/2
36
- @y = options.delete(:x) || 100
37
- @spacing = options.delete(:x) || 100
35
+ @y = options.delete(:y) || 100
36
+ @spacing = options.delete(:spacing) || 100
38
37
  @items = []
38
+ @visible = true
39
39
 
40
40
  y = @spacing
41
41
  menu_items.each do |key, value|
42
-
43
42
  item = if key.is_a? String
44
43
  Text.new(key, options.dup)
45
44
  elsif key.is_a? Image
@@ -105,10 +104,10 @@ module Chingu
105
104
  when Proc, Method
106
105
  action[]
107
106
  when Chingu::GameState
108
- push_game_state(action)
107
+ game_state.push_game_state(action)
109
108
  when Class
110
109
  if action.ancestors.include?(Chingu::GameState)
111
- push_game_state(action)
110
+ game_state.push_game_state(action)
112
111
  end
113
112
  else
114
113
  # TODO possibly raise an error? This ought to be handled when the input is specified in the first place.