chingu 0.8.0.5 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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