chingu 0.8.0.5 → 0.8.1

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.
@@ -8,6 +8,8 @@ module Chingu
8
8
  # It will also acts as a container for the trait-system of chingu.
9
9
  #
10
10
  class BasicGameObject
11
+ class << self; attr_accessor :instances; end
12
+
11
13
  include Chingu::Helpers::ClassInheritableAccessor # adds classmethod class_inheritable_accessor
12
14
 
13
15
  attr_reader :options, :paused
@@ -16,7 +18,7 @@ module Chingu
16
18
  class_inheritable_accessor :trait_options
17
19
  @trait_options = Hash.new
18
20
  def trait_options; self.class.trait_options; end
19
-
21
+
20
22
  #
21
23
  # Adds a trait or traits to a certain game class
22
24
  # Executes a standard ruby "include" the specified module
@@ -66,7 +68,10 @@ module Chingu
66
68
  def initialize(options = {})
67
69
  @options = options
68
70
  @parent = options[:parent]
69
-
71
+
72
+ self.class.instances ||= Array.new
73
+ self.class.instances << self
74
+
70
75
  #
71
76
  # A GameObject either belong to a GameState or our mainwindow ($window)
72
77
  #
@@ -79,7 +84,7 @@ module Chingu
79
84
  # which then will pass it on to the next setup_trait() with a super-call.
80
85
  setup_trait(options)
81
86
 
82
- setup if respond_to?(:setup)
87
+ setup
83
88
  end
84
89
 
85
90
  #
@@ -99,6 +104,12 @@ module Chingu
99
104
  #
100
105
  instance.parent.add_game_object(instance) if instance.parent
101
106
 
107
+ #
108
+ # Keep track of instances in class-variable
109
+ # Used for quick access to all isntances of a certain class, for example Enemy.all
110
+ #
111
+ #instances ||= Array.new
112
+ #instances << instance
102
113
 
103
114
  return instance
104
115
  end
@@ -107,6 +118,7 @@ module Chingu
107
118
  # Disable automatic calling of update() and update_trait() each game loop
108
119
  #
109
120
  def pause!
121
+ @parent.game_objects.pause_game_object(self) if @parent && @paused == false
110
122
  @paused = true
111
123
  end
112
124
  alias :pause :pause!
@@ -115,6 +127,7 @@ module Chingu
115
127
  # Enable automatic calling of update() and update_trait() each game loop
116
128
  #
117
129
  def unpause!
130
+ @parent.game_objects.unpause_game_object(self) if @parent && @paused == true
118
131
  @paused = false
119
132
  end
120
133
  alias :unpause :unpause!
@@ -153,8 +166,10 @@ module Chingu
153
166
  # Bullet.all.each do {} # Iterate through all bullets in current game state
154
167
  #
155
168
  def self.all
156
- $window.current_scope.game_objects.of_class(self).dup
157
- end
169
+ instances ? instances.dup : []
170
+ # instances ? instances.keys : [] # for hash instance
171
+ # $window.current_scope.game_objects.of_class(self).dup # old school way
172
+ end
158
173
 
159
174
  #
160
175
  # As Array.each on the instances of the current class
@@ -177,12 +192,11 @@ module Chingu
177
192
  all.select { |object| yield object }
178
193
  end
179
194
 
180
-
181
195
  #
182
196
  # Returns the total amount of game objects based on this class
183
197
  #
184
198
  def self.size
185
- $window.current_scope.game_objects.of_class(self).size
199
+ all.size
186
200
  end
187
201
 
188
202
  #
@@ -200,7 +214,8 @@ module Chingu
200
214
  # Bullet.destroy_all # Removes all Bullet objects from the game
201
215
  #
202
216
  def self.destroy_all
203
- self.all.each { |object| object.destroy! }
217
+ all.each { |game_object| game_object.parent.remove_game_object(game_object) }
218
+ instances.clear if instances
204
219
  end
205
220
 
206
221
  #
@@ -208,7 +223,8 @@ module Chingu
208
223
  # If the object isn't being managed by Chingu (ie. you're doing manual update/draw calls) the object is only frozen, not removed from any updae cycle (because you are controlling that).
209
224
  #
210
225
  def destroy
211
- @parent.remove_game_object(self) if @parent
226
+ @parent.remove_game_object(self) if @parent
227
+ self.class.instances.delete(self)
212
228
  end
213
229
  alias :destroy! :destroy
214
230
  end
@@ -26,11 +26,16 @@ module Chingu
26
26
  # An instance of GameObjectList is automaticly created as "game_objects" if using Chingu::Window
27
27
  #
28
28
  class GameObjectList
29
+ attr_reader :visible_game_objects, :unpaused_game_objects
29
30
 
30
31
  def initialize(options = {})
31
32
  @game_objects = options[:game_objects] || []
32
- @add_game_objects = []
33
- @remove_game_objects = []
33
+ @visible_game_objects = []
34
+ @unpaused_game_objects = []
35
+
36
+ #@game_objects = {}
37
+ #@visible_game_objects = {}
38
+ #@unpaused_game_objects = {}
34
39
  end
35
40
 
36
41
  def to_s
@@ -42,23 +47,42 @@ module Chingu
42
47
  end
43
48
 
44
49
  def destroy_all
45
- @game_objects.clear
50
+ @game_objects.each { |game_object| game_object.destroy }
46
51
  end
47
52
  alias :clear :destroy_all
48
53
  alias :remove_all :destroy_all
49
54
 
55
+ def show_game_object(object)
56
+ @visible_game_objects.push(object)
57
+ end
58
+ def hide_game_object(object)
59
+ @visible_game_objects.delete(object)
60
+ end
61
+ def pause_game_object(object)
62
+ @unpaused_game_objects.delete(object)
63
+ end
64
+ def unpause_game_object(object)
65
+ @unpaused_game_objects.push(object)
66
+ end
67
+
50
68
  def add_game_object(object)
51
69
  @game_objects.push(object)
52
- #@add_game_objects.push(object)
70
+ @visible_game_objects.push(object) if object.respond_to?(:visible) && object.visible
71
+ @unpaused_game_objects.push(object) if object.respond_to?(:paused) && !object.paused
72
+
73
+ #@game_objects[object] = true
74
+ #@visible_game_objects[object] = true if object.respond_to?(:visible) && object.visible
75
+ #@unpaused_game_objects[object] = true if object.respond_to?(:paused) && !object.paused
53
76
  end
54
77
 
55
78
  def remove_game_object(object)
56
79
  @game_objects.delete(object)
57
- #@remove_game_objects.push(object)
80
+ @visible_game_objects.delete(object)
81
+ @unpaused_game_objects.delete(object)
58
82
  end
59
83
 
60
84
  def destroy_if
61
- @game_objects.reject! { |object| yield(object) }
85
+ @game_objects.select { |object| object.destroy if yield(object) }
62
86
  end
63
87
 
64
88
  def size
@@ -68,27 +92,28 @@ module Chingu
68
92
  def empty?
69
93
  @game_objects.empty?
70
94
  end
95
+
96
+ def update
97
+ @unpaused_game_objects.each { |go| go.update_trait; go.update; }
98
+ end
99
+ def force_update
100
+ @game_objects.each { |go| go.update_trait; go.update; }
101
+ end
71
102
 
72
103
  def draw
73
- @game_objects.select { |object| object.visible }.each do |object|
74
- object.draw_trait
75
- object.draw
76
- end
104
+ @visible_game_objects.each { |go| go.draw_trait; go.draw; }
105
+ end
106
+ def force_draw
107
+ @game_objects.each { |go| go.draw_trait; go.draw }
77
108
  end
78
109
 
79
110
  def draw_relative(x=0, y=0, zorder=0, angle=0, center_x=0, center_y=0, factor_x=0, factor_y=0)
80
- @game_objects.select { |object| object.visible }.each do |object|
111
+ @visible_game_objects.each do |object|
81
112
  object.draw_trait
82
113
  object.draw_relative(x, y, zorder, angle, center_x, center_y, factor_x, factor_y)
83
114
  end
84
115
  end
85
116
 
86
- def update
87
- @game_objects.select { |object| not object.paused }.each do |object|
88
- object.update_trait
89
- object.update
90
- end
91
- end
92
117
 
93
118
  def each
94
119
  @game_objects.dup.each { |object| yield object }
@@ -146,14 +171,5 @@ module Chingu
146
171
  end
147
172
  alias :show :show!
148
173
 
149
- private
150
-
151
- def sync
152
- @game_objects += @add_game_objects
153
- @add_game_objects.clear
154
-
155
- @game_objects -= @remove_game_objects
156
- @remove_game_objects.clear
157
- end
158
174
  end
159
175
  end
@@ -23,14 +23,32 @@ module Chingu
23
23
  #
24
24
  # ** This class is under heavy development, API will most likely change! **
25
25
  #
26
- # GameObjectMap can convert any set of game objects into a 2D-array for fast lookup
27
- # You can set any gridsize with :grid, defaults to [32,32]. The smaller the grid the more memory it will eat.
26
+ # GameObjectMap can map any set of game objects into a 2D-array for fast lookup.
27
+ # You can choose gridsize with the :grid-parameter, defaults to [32,32].
28
+ # The smaller the grid the more memory GameObjectMap will eat.
29
+ #
30
+ # The game objects sent to GameObjectMap must respond to #bb (as provided by trait :bounding_box)
31
+ # This is needed to calcuate what cells in the grid each game object covers.
28
32
  #
29
33
  # Basic usage:
30
34
  # @map = GameObjectMap.new(:game_objects => TerrainObject.all, :grid => [32, 32])
31
35
  # @map.at(100, 100) # returns one TerrainObject at x/y: 100/100
32
36
  # @map.game_object(player) # returns one TerrainObject which collides with player.bounding_box
33
37
  #
38
+ # A GameObjectMap is ment to be used for static non-moving objects, where a map can be calculated once and then used for fast lookups.
39
+ # This makes GameObjectMap very well suited for terrain for a player to walk on / collide with.
40
+ #
41
+ # One cell in the GameObjectMap can only be occupied by one game object.
42
+ # If you need many objects at the same cell, use 2 GameObjectMaps, something like:
43
+ #
44
+ # @terrain = GameObjectMap.new(:game_objects => Terrain.all)
45
+ # @mines = GameObjectMap.new(:game_objects => Mine.all)
46
+ #
47
+ # @player.stop_falling if @terrain.at(@player.x, @player)
48
+ # @player.die if @mine.at(@player.x, @player)
49
+ #
50
+ # Take note, since there can be only 1 game object per cell a huge game object could very well "cover out" another smaller game objects occupying the same cells.
51
+ #
34
52
  # ** This class is under heavy development, API will most likely change! **
35
53
  #
36
54
  class GameObjectMap
@@ -52,7 +70,7 @@ module Chingu
52
70
  @game_object_positions = {}
53
71
 
54
72
  @game_objects.each do |game_object|
55
- puts "#{game_object.class} @ #{game_object.x} / #{game_object.y}" if @debug
73
+ puts "#{game_object.class} @ #{game_object.x}/#{game_object.y} - #{game_object.bb}" if @debug
56
74
  insert(game_object)
57
75
  end
58
76
  end
@@ -73,15 +91,17 @@ module Chingu
73
91
  @map[x] ||= []
74
92
  (start_y ... stop_y).each do |y|
75
93
  @map[x][y] = game_object
94
+ puts "#{game_object.class} => map[#{x}][#{y}]" if @debug
76
95
  end
77
96
  end
78
97
  end
79
98
 
80
99
  #
81
- # Removes a specific game object from the map
100
+ # Removes a specific game object from the map, replace the cell-value with nil
82
101
  #
83
102
  def delete(game_object)
84
103
  range_x, range_y = @game_object_positions[game_object]
104
+ return unless range_x && range_y
85
105
 
86
106
  range_x.each do |x|
87
107
  range_y.each do |y|
@@ -92,7 +112,7 @@ module Chingu
92
112
  alias :clear_game_object :delete
93
113
 
94
114
  #
95
- # Clear game object from the array-map on a certain X/Y
115
+ # Clear the game object residing in the cell given by real world coordinates x/y
96
116
  #
97
117
  def clear_at(x, y)
98
118
  lookup_x = (x / @grid[0]).to_i
@@ -101,14 +121,17 @@ module Chingu
101
121
  end
102
122
 
103
123
  #
104
- # Gets a game object from the array-map on a certain X/Y
124
+ # Gets game object from map that resides on real world coordinates x/y
105
125
  #
106
126
  def at(x, y)
107
127
  lookup_x = (x / @grid[0]).to_i
108
128
  lookup_y = (y / @grid[1]).to_i
109
- @map[lookup_x][lookup_y] rescue nil
129
+ @map[lookup_x][lookup_y] rescue nil # Benchmark this against @map[lookup_x] && @map[lookup_x][lookup_y] => prob faster
110
130
  end
111
131
 
132
+ #
133
+ # Return the first game object occupying any of the cells that given 'game_object' covers
134
+ #
112
135
  def from_game_object(game_object)
113
136
  start_x = (game_object.bb.left / @grid[0]).to_i
114
137
  stop_x = (game_object.bb.right / @grid[0]).to_i
@@ -126,7 +149,7 @@ module Chingu
126
149
  end
127
150
 
128
151
  #
129
- # Yields each object in the map colliding with the given game object
152
+ # Yields all game objects occupying any of the cells that given 'game_object' covers
130
153
  #
131
154
  def each_collision(game_object)
132
155
  start_x = (game_object.bb.left / @grid[0]).to_i
@@ -40,6 +40,20 @@ module Chingu
40
40
  @game_objects
41
41
  end
42
42
 
43
+ def show_game_object(object)
44
+ @game_objects.show_game_object(object)
45
+ end
46
+ def hide_game_object(object)
47
+ @game_objects.hide_game_object(object)
48
+ end
49
+ def pause_game_object(object)
50
+ @game_objects.pause_game_object(object)
51
+ end
52
+ def unpause_game_object(object)
53
+ @game_objects.unpause_game_object(object)
54
+ end
55
+
56
+
43
57
  #
44
58
  # Fetch game objects of a certain type/class
45
59
  #
@@ -29,24 +29,32 @@ module Chingu
29
29
  #
30
30
  module Retrofy
31
31
 
32
- def setup_trait(options)
33
- @retrofy_options = {:debug => false}.merge(options)
34
- super
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
35
39
  end
36
-
37
- def screen_width
38
- (self.image.width * self.factor).to_i
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
39
47
  end
40
48
 
41
- def screen_height
42
- (self.image.heigt * self.factor).to_i
49
+ def real_y
50
+ (self.y / self.factor).to_i
43
51
  end
44
52
 
45
- def screen_x
53
+ def retrofied_x
46
54
  (self.x * self.factor).to_i
47
55
  end
48
56
 
49
- def screen_y
57
+ def retrofied_y
50
58
  (self.y * self.factor).to_i
51
59
  end
52
60
 
@@ -0,0 +1,252 @@
1
+ #--
2
+ # Part of Chingu -- OpenGL accelerated 2D game framework for Ruby
3
+ # Copyright (C) 2009 ippa / ippa@rubylicio.us
4
+ #
5
+ # Written/Refactored by Jakub Hozak - jakub.hozak@gmail.com
6
+ #
7
+ #++
8
+
9
+ module Chingu
10
+ module Traits
11
+
12
+ #
13
+ # A Chingu trait providing ability to be drawn as an image.
14
+ #
15
+ # Example:
16
+ #
17
+ # class Rocket < BasicGameObject
18
+ # trait :simple_sprite
19
+ # end
20
+ #
21
+ # Rocket.create(:x => 100, :y => 200)
22
+ #
23
+ # Options:
24
+ # :image - actual sprite to draw
25
+ # - see #image= for details as this method is used to set this option
26
+ #
27
+ # Introducing Variables:
28
+ # :x, :y, :zorder, :factor_x, :factor_y, :mode, :color, :visible
29
+ #
30
+ module SimpleSprite
31
+ module ClassMethods
32
+ def initialize_trait(options = {})
33
+ trait_options[:sprite] = options
34
+ end
35
+ end
36
+
37
+ attr_accessor :x, :y, :angle, :factor_x, :factor_y, :zorder, :mode, :color
38
+ attr_reader :factor, :center, :height, :width, :image
39
+ attr_accessor :visible # kill this? force use of setter
40
+
41
+ def setup_trait(object_options = {})
42
+ @visible = true unless options[:visible] == false
43
+ self.image = options[:image] if options[:image]
44
+ self.color = options[:color] || ::Gosu::Color::WHITE.dup
45
+ self.alpha = options[:alpha] if options[:alpha]
46
+ self.mode = options[:mode] || :default
47
+ self.x = options[:x] || 0
48
+ self.y = options[:y] || 0
49
+ self.zorder = options[:zorder] || 100
50
+
51
+ self.factor = options[:factor] || options[:scale] || $window.factor || 1.0
52
+ self.factor_x = options[:factor_x].to_f if options[:factor_x]
53
+ self.factor_y = options[:factor_y].to_f if options[:factor_y]
54
+
55
+ if self.image
56
+ self.width = options[:width] if options[:width]
57
+ self.height = options[:height] if options[:height]
58
+ self.size = options[:size] if options[:size]
59
+ end
60
+
61
+ super
62
+ end
63
+
64
+ #
65
+ # Let's have some useful information in to_s()
66
+ #
67
+ def to_s
68
+ "#{self.class.to_s} @ #{x.to_i} / #{y.to_i} " <<
69
+ "(#{width.to_i} x #{height.to_i}) - " <<
70
+ " ratio: #{sprintf("%.2f",width/height)} scale: #{sprintf("%.2f", factor_x)}/#{sprintf("%.2f", factor_y)} angle: #{angle.to_i} zorder: #{zorder} alpha: #{alpha}"
71
+ end
72
+
73
+ def color=(color)
74
+ @color = color.is_a?(Gosu::Color) ? color : Gosu::Color.new(color || 0xFFFFFFFF)
75
+ end
76
+
77
+ #
78
+ # Accepts String, callable object or any-other non-nil capable
79
+ # of drawing itself on screen.
80
+ #
81
+ # Examples:
82
+ # image = 'rocket.png'
83
+ # image = Gosu::Image.new($window, 'rocket.png')
84
+ #
85
+ # image = lambda do
86
+ # # TexPlay is library for Gosu image generation
87
+ # TexPlay.create_image($window,10,10).paint { circle(5,5,5, :color => :red) }
88
+ # end
89
+ #
90
+ def image=(image)
91
+ raise ArgumentError.new("No image set") if image.nil?
92
+
93
+ @image = if String === image
94
+ # 1) Try loading the image the normal way
95
+ # 2) Try looking up the picture using Chingus Image-cache
96
+ Gosu::Image.new($window, image,false) rescue Gosu::Image[image]
97
+ elsif image.respond_to? :call
98
+ image.call
99
+ else
100
+ image
101
+ end
102
+ end
103
+
104
+ #
105
+ # Get all settings from a game object in one array.
106
+ # Complemented by the GameObject#attributes= setter.
107
+ # Makes it easy to clone a objects x,y,angle etc.
108
+ #
109
+ def attributes
110
+ [@x, @y, @angle, @factor_x, @factor_y, @color, @mode, @zorder]
111
+ end
112
+
113
+ #
114
+ # Set all attributes on 1 line
115
+ # Mainly used in combination with game_object1.attributes = game_object2.attributes
116
+ #
117
+ def attributes=(attributes)
118
+ self.x, self.y, self.angle, self.factor_x, self.factor_y, self.color, self.mode, self.zorder = *attributes
119
+ end
120
+
121
+ #
122
+ # Set an effective width for the object on screen.
123
+ # Chingu does this by setting factor_x depending on imge.width and width given.
124
+ # Usually better to have a large image and make it smaller then the other way around.
125
+ #
126
+ def width=(width)
127
+ @factor_x = width.to_f / @image.width.to_f if @image
128
+ end
129
+
130
+ #
131
+ # Get effective width by calculating it from image-width and factor_x
132
+ #
133
+ def width
134
+ (@image.width * @factor_x).abs if @image
135
+ end
136
+
137
+ #
138
+ # Set an effective height for the object on screen.
139
+ # Chingu does this by setting factor_x depending on imge.width and width given.
140
+ # Usually better to have a large image and make it smaller then the other way around.
141
+ #
142
+ def height=(height)
143
+ @factor_y = height.to_f / @image.height.to_f if @image
144
+ end
145
+
146
+ #
147
+ # Get effective height by calculating it from image-width and factor
148
+ #
149
+ def height
150
+ (@image.height.to_f * @factor_y).abs if @image
151
+ end
152
+
153
+ # Set width and height in one swoop
154
+ def size=(size)
155
+ self.width, self.height = *size
156
+ end
157
+
158
+ # Get objects width and height in an array
159
+ def size
160
+ [self.width, self.height]
161
+ end
162
+
163
+ # Quick way of setting both factor_x and factor_y
164
+ def factor=(factor)
165
+ @factor = @factor_x = @factor_y = factor
166
+ end
167
+ alias scale= factor=
168
+ alias scale factor
169
+
170
+
171
+ # Get objects alpha-value (internally stored in @color.alpha)
172
+ def alpha
173
+ @color.alpha
174
+ end
175
+
176
+ # Set objects alpha-value (internally stored in @color.alpha)
177
+ # If out of range, set to closest working value. this makes fading simpler.
178
+ def alpha=(value)
179
+ value = 0 if value < 0
180
+ value = 255 if value > 255
181
+ @color.alpha = value
182
+ end
183
+
184
+ #
185
+ # Sets angle, normalize it to between 0..360
186
+ #
187
+ def angle=(value)
188
+ if value < 0
189
+ value = 360+value
190
+ elsif value > 360
191
+ value = value-360
192
+ end
193
+ @angle = value
194
+ end
195
+
196
+ #
197
+ # Disable automatic calling of draw and draw_trait each game loop
198
+ #
199
+ def hide!
200
+ @parent.game_objects.hide_game_object(self) if @parent && @visible == true
201
+ @visible = false
202
+ end
203
+
204
+ #
205
+ # Enable automatic calling of draw and draw_trait each game loop
206
+ #
207
+ def show!
208
+ @parent.game_objects.show_game_object(self) if @parent && @visible == false
209
+ @visible = true
210
+ end
211
+
212
+ #
213
+ # Returns true if visible (not hidden)
214
+ #
215
+ def visible?
216
+ @visible == true
217
+ end
218
+
219
+
220
+ # Returns true if object is inside the game window, false if outside
221
+ def inside_window?(x = @x, y = @y)
222
+ x >= 0 && x <= $window.width && y >= 0 && y <= $window.height
223
+ end
224
+
225
+ # Returns true object is outside the game window
226
+ def outside_window?(x = @x, y = @y)
227
+ not inside_window?(x,y)
228
+ end
229
+
230
+ #
231
+ # Our encapsulation of GOSU's image.draw_rot, uses the objects variables to draw it on screen if @visible is true
232
+ #
233
+ def draw
234
+ @image.draw(@x, @y, @zorder, @factor_x, @factor_y, @color, @mode) if @image
235
+ end
236
+
237
+ #
238
+ # Works as #draw() but takes offsets for all draw_rot()-arguments. Used among others by the viewport-trait.
239
+ #
240
+ def draw_relative(x=0, y=0, zorder=0, factor_x=0, factor_y=0)
241
+ @image.draw(@x+x, @y+y, @zorder+zorder, @factor_x+factor_x, @factor_y+factor_y, @color, @mode) if @image
242
+ end
243
+
244
+ #
245
+ # Works as #draw() but takes x/y arguments. Used among others by the edit-game state.
246
+ #
247
+ def draw_at(x, y)
248
+ draw_relative(x,y)
249
+ end
250
+ end
251
+ end
252
+ end