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
@@ -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.