singpolyma-xgame 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/COPYING +2 -0
  2. data/README +21 -2
  3. data/TODO +49 -1
  4. data/lib/xgame.rb +301 -260
  5. data/xgame.gemspec +5 -5
  6. metadata +5 -5
data/COPYING CHANGED
@@ -20,3 +20,5 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
20
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
21
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
22
  OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ "panda.png" copyright (C) 2004 John Croisant licensed under the Creative Commons Attribution-ShareAlike 2.5 License.
data/README CHANGED
@@ -1,8 +1,27 @@
1
1
  This is XGame, a cross-platform game development framework based on RubyGame and SDL.
2
2
 
3
- To run this demo you must have ruby and the rubygems system installed, then run:
3
+ You may install rubygame using:
4
4
 
5
5
  sudo gem install rubygame
6
6
 
7
- to install the rubygame dependency. You may need to install build tools, ruby headers, and SDL
7
+ or by simply placing it somewhere in your ruby require path.
8
+
9
+ You may need to install build tools, ruby headers, and SDL
8
10
  libraries/headers in order to install rubygame. These should be in your software repositories.
11
+
12
+ You must also install Chipmunk <http://wiki.slembcke.net/main/published/Chipmunk> in your ruby require path.
13
+
14
+ To install XGame on your system, just run the traditional:
15
+ ./configure
16
+ make
17
+ sudo make install
18
+
19
+ == Long Term ==
20
+
21
+ For those that are curious or poking around, XGame is part of a longer vision by singpolyma and psycotica0
22
+ to start a company that does game development and distribution. Our vision is to help other developers
23
+ create and distribute / sell games to a market they might not othewise be able to reach.
24
+
25
+ While the code is this repo is subject to the COPYING file, we would also ask that the community abide by
26
+ the spirit of FrieNDA while we get our product ready for launch. Everyone will benefit once the network
27
+ is up and able to be used by developers to distribute their games.
data/TODO CHANGED
@@ -1 +1,49 @@
1
- * Scrollable background
1
+ Run `git grep -e TODO: -e XXX: -e FIXME:` to see notes about cleanup in the source.
2
+
3
+ == Roadmap ==
4
+
5
+ == General ==
6
+
7
+ * Pick names for company and products. XGame is kind of random, and while good on Google, all related domains are taken.
8
+ * Get domain and website
9
+ * Get people to test stuff as more of it becomes available
10
+ * Run contests to get people interested in developing for the platform?
11
+
12
+ === For library ===
13
+
14
+ * Scrollable background (psycotica0 working on this?)
15
+ * EventListeners needs to wrap the new event stuff in rubygame2.4 -- the rubygame stuff is ugly raw though (Low priority until rubygame2.4 is official)
16
+ * Music/SFX wrapper/demo/tests?
17
+ * Board game-style demo + other demos
18
+ * Continue to improve self-documentation of code and RDoc
19
+
20
+ * Python port
21
+ * DOMScripting (aka JavaScript) port
22
+ * C port
23
+ * C++ port
24
+ * Java port?
25
+
26
+ == For packager ==
27
+
28
+ * Package Rubygame, Chipmunk, XGame, and demos as *.deb and *.rpm i386, AMD64, PPC, x86 for Windows, and source
29
+ ** Include the two extension files as defined at <http://github.com/singpolyma/xgame/wikis/package-file-extensions>
30
+ * Set up an APT repository somewhere
31
+ ** Set up MD5, SHA1, SHA256, and GPG verification
32
+ * Test with apt-get
33
+ * Write prototype script to install from *.deb, *.rpm, or APT repo (with OAuth support for APT)
34
+ ** Test security (only authorized accounts download certain packages)
35
+ * Set up gratis and non-gratis repos
36
+ ** Test end-to-end installing a "purchased" game with gratis dependencies
37
+ * Get nice installer environment and GUI in C and Tk
38
+
39
+ == For editor ==
40
+
41
+ * Prototype editor
42
+ ** User can add sprites
43
+ ** User can move sprites around
44
+ ** User can assign mass, and bind basic controls right from GUI
45
+ ** User can set backgrounds, air resistance, and gravity
46
+ ** Provide basic default behaviours (moving platforms)
47
+ ** Allow binding and storage of arbitrary code in the target language
48
+ * Decide on output/storage format (may not want to store as code, parsing is hard)
49
+ * Get nice installer and GUI in C and Tk
data/lib/xgame.rb CHANGED
@@ -1,9 +1,13 @@
1
+ # All distances in pixels
2
+ # All times in milliseconds
3
+
1
4
  begin
2
5
  require 'rubygame'
6
+ require 'chipmunk'
3
7
 
4
8
  # If we are operating without rubygems (preferred) some features are still nice
5
9
  # define the Gem class to keep a standard API
6
- class Gem
10
+ module Gem
7
11
  @@user_home = '/'
8
12
  def self.user_home
9
13
  @@user_home ||= find_home
@@ -32,6 +36,25 @@ begin
32
36
  rescue LoadError
33
37
  require 'rubygems'
34
38
  require 'rubygame'
39
+ require 'chipmunk'
40
+ end
41
+
42
+ # Extend the CP namespace
43
+ module CP
44
+ INFINITY = 1.0/0.0
45
+
46
+ class Vec2
47
+ ZERO = self.new(0,0)
48
+
49
+ def [](k)
50
+ case k
51
+ when 0: x
52
+ when 1: y
53
+ else raise ArgumentError.new("Bad CP::Vec2 'index' #{k}")
54
+ end
55
+ end
56
+ end
57
+
35
58
  end
36
59
 
37
60
  # Extend the Rubygame namespace
@@ -39,333 +62,351 @@ module Rubygame
39
62
 
40
63
  # This class defines an easy way to manage callbacks
41
64
  class ListenerList < Hash
65
+ def world=(w)
66
+ @world = w
67
+ end
68
+
42
69
  def addEventListener(event, callback=nil, &block)
43
70
  callback = block unless callback
44
- self[event] = [] unless self.key?event
45
- self[event] << callback
71
+ if @world and event.is_a?CollisionEvent
72
+ @world.add_collision_func(event.by, event.to) { |by, to| callback.call(by, to) }
73
+ else
74
+ self[event] = [] unless self.key?event
75
+ self[event] << callback
76
+ end
46
77
  end
47
78
  end
48
79
 
49
80
  class LoopEvent < Event
50
81
  end
51
82
 
83
+ class CollisionEvent < Event
84
+ attr_accessor :by, :to
85
+ def initialize(by, to)
86
+ @by = by
87
+ @to = to
88
+ end
89
+ end
90
+
91
+ class Rect
92
+ def shape_for(body)
93
+ CP::Shape::Poly.new(body, vertices, CP::Vec2::ZERO)
94
+ end
95
+
96
+ def vertices
97
+ [CP::Vec2::ZERO, CP::Vec2.new(0, height), CP::Vec2.new(width, height), CP::Vec2.new(width, 0)]
98
+ end
99
+ end # end Rect
100
+
52
101
  # Extend the Sprites namespace
53
102
  module Sprites
54
103
 
55
- # This is a mixin module for sprites that move
56
- module MovingSprite
104
+ module Sprite
57
105
 
58
- def initialize(*args)
59
- super
60
- @velocity = {:left => 0, :right => 0, :up => 0, :down => 0} # These are the initial components of velocity
61
- @going = {:left => false, :right => false, :up => false, :down => false} # Are we "going" in these directions or just travelling?
62
- @reference = [0,0] # Moving frame of reference
63
- @animating = {:left => [0,0], :right => [0,0], :up => [0,0], :down => [0,0]}
64
- @speed = 50 # This can be overidden by implementations: it is how fast / massive the sprite is
106
+ # This method lets you check if the sprite is moving in a certain direction
107
+ def moving?(direction)
108
+ false # We're not a moving sprite
65
109
  end
66
110
 
67
- def velocity
68
- [@velocity[:left]*-1 + @velocity[:right], @velocity[:up]*-1 + @velocity[:down]]
111
+ # Call this function every frame to have the Sprite calculate things about itself
112
+ def update(time); end
113
+
114
+ end
115
+
116
+ # This class basically just turns the Sprite module into an inheritable class
117
+ class BasicSprite
118
+ include Rubygame::Sprites::Sprite
119
+ end
120
+
121
+ # This is a basic class for image-based, sprites with a rectangular box matching their image
122
+ class ImageSprite < Rubygame::Sprites::BasicSprite
123
+
124
+ # Override this in subclasses to have a default image
125
+ # (actually, Surface, you can just draw vector stuff on it)
126
+ def self.default_image; end;
127
+
128
+ # Create a new BasicSprite. Pass x, y coordinates for location to spawn.
129
+ # Pass path to image if you have not ovrriden default_image
130
+ def initialize(x,y,image=nil)
131
+ super()
132
+ if image
133
+ @image = Rubygame::Surface[image]
134
+ raise "Image #{image} failed to load. Looking in: #{Rubygame::Surface.autoload_dirs.join(":")}" unless @image
135
+ else
136
+ @image = default_image
137
+ raise "No image to load. No default image, no image specified." unless @image
138
+ end
139
+ @rect = Rubygame::Rect.new(x,y,*@image.size)
140
+ end
141
+
142
+ end # class ImageSprite
143
+
144
+ # This is a class for Sprites to be used with the Chipmunk Physics Engine
145
+ # It is a subclass of ImageSprite since overriding defaulit_image
146
+ # with any Surface solves not having an image file
147
+ class ChipmunkPhysicsSprite < ImageSprite
148
+
149
+ attr_accessor :shape
150
+
151
+ def default_mass; 5; end
152
+
153
+ def initialize(x, y, mass=nil, moment=nil, image=nil)
154
+ super(x, y, image)
155
+ mass = default_mass unless mass
156
+ moment = CP::moment_for_poly(mass, @rect.vertices, CP::Vec2::ZERO) unless moment
157
+ body = CP::Body.new(mass, moment)
158
+ @shape = @rect.shape_for(body)
159
+ body.p = CP::Vec2.new(rect.x, rect.y)
160
+ @going = { :left => 0, :right => 0, :up => 0, :down =>0 }
161
+ @jumps = 0
69
162
  end
70
163
 
71
- def going
72
- @going
164
+ def velocity
165
+ @shape.body.v
73
166
  end
74
167
 
75
- def reference=(v)
76
- @reference = v
168
+ def moving?(direction)
169
+ return velocity.x > 0 if direction == :right
170
+ return velocity.x < 0 if direction == :left
171
+ return velocity.y > 0 if direction == :down
172
+ return velocity.y < 0 if direction == :up
173
+ false
77
174
  end
78
175
 
79
176
  # Go in some direction [vx, vy]. If either is nil, motion on that axis will not be affected.
80
- def go(v, duration=0)
177
+ def go(v)
81
178
  if v[0]
82
- if v[0] <= 0
83
- @animating[:left] = [duration, @velocity[:left]]
84
- @velocity[:left] = v[0]*-1
85
- @going[:left] = true
86
- end
87
- if v[0] >= 0
88
- @animating[:right] = [duration, @velocity[:right]]
89
- @velocity[:right] = v[0]
90
- @going[:right] = true
91
- end
179
+ @going[:left] = v[0]*-1 if v[0] <= 0
180
+ @going[:right] = v[0] if v[0] >= 0
92
181
  end
93
182
  if v[1]
94
- if v[1] <= 0
95
- @animating[:up] = [duration, @velocity[:up]]
96
- @velocity[:up] = v[1]*-1
97
- @going[:up] = true
98
- end
99
- if v[1] >= 0
100
- @animating[:down] = [duration, @velocity[:down]]
101
- @velocity[:down] = v[1]
102
- @going[:down] = true
103
- end
183
+ @going[:up] = v[1]*-1 if v[1] <= 0
184
+ @going[:down] = v[1] if v[1] >= 0
104
185
  end
105
186
  end
106
187
 
107
188
  # Stop some component of motion
108
- def stop(direction, duration=0)
109
- @animating[direction] = [duration, @animating[direction][0] >= 1 ? @animating[direction][1] : @velocity[direction]]
110
- @velocity[direction] = 0
111
- @going[direction] = false
189
+ def stop(direction)
190
+ @going[direction] = 0
112
191
  end
113
192
 
114
- # Strike the sprite in a certain direction
115
- def hit(v, by=nil)
116
- self.go(v,[2,(1/(XGame.framerate/1000.0))/(@speed/4)].max)
193
+ # Apply a constant force along a certain vector
194
+ def apply_force(force)
195
+ case force
196
+ when Array
197
+ @shape.body.apply_force(CP::Vec2.new(force[0], force[1]), CP::Vec2::ZERO)
198
+ when CP::Vec2
199
+ @shape.body.apply_force(force, CP::Vec2::ZERO)
200
+ else
201
+ raise ArgumentError.new("Bad type for force: #{force.class}")
202
+ end
117
203
  end
118
204
 
119
- # Apply a force to this sprite [vx, vy, max_applied (optional)]
120
- def apply_force(value)
121
- @velocity[:left] += value[0]*-1 if value[0] < 0 and (!value[2][:left] or @velocity[:left] < value[2][:left])
122
- @velocity[:right] += value[0] if value[0] > 0 and (!value[2][:right] or @velocity[:right] < value[2][:right])
123
- @velocity[:up] += value[1]*-1 if value[1] < 0 and (!value[2][:up] or @velocity[:up] < value[2][:up])
124
- @velocity[:down] += value[1] if value[1] > 0 and (!value[2][:down] or @velocity[:down] < value[2][:down])
205
+ def jump(strength=1000)
206
+ if @jumps < 1
207
+ apply_impulse [0, -strength]
208
+ @jumps += 1
209
+ end
125
210
  end
126
211
 
127
- def self.included(base)
128
- base.class_eval {alias_method :update_no_moving, :update; alias_method :update, :update_moving}
212
+ def reset_jumps
213
+ @jumps = 0
129
214
  end
130
215
 
131
- def update_moving(time)
132
- update_no_moving(time)
133
-
134
- if @animating[:left][0] > 0
135
- @animating[:left][0] -= 1
136
- @velocity[:left] = @animating[:left][1] if @animating[:left][0] < 1
137
- end
138
- if @animating[:right][0] > 0
139
- @animating[:right][0] -= 1
140
- @velocity[:right] = @animating[:right][1] if @animating[:right][0] < 1
141
- end
142
- if @animating[:up][0] > 0
143
- @animating[:up][0] -= 1
144
- @velocity[:up] = @animating[:up][1] if @animating[:up][0] < 1
145
- end
146
- if @animating[:down][0] > 0
147
- @animating[:down][0] -= 1
148
- @velocity[:down] = @animating[:down][1] if @animating[:down][0] < 1
216
+ # Apply a momentary force impulse
217
+ def apply_impulse(impulse)
218
+ case impulse
219
+ when Array
220
+ @shape.body.apply_impulse(CP::Vec2.new(impulse[0], impulse[1]), CP::Vec2::ZERO)
221
+ when CP::Vec2
222
+ @shape.body.apply_impulse(impulse, CP::Vec2::ZERO)
223
+ else
224
+ raise ArgumentError.new("Bad type for impulse: #{force.class}")
149
225
  end
150
- x,y = @rect.center
151
- base = @speed * time/1000.0
152
-
153
- @rect.centerx = x + (@reference[0] + velocity[0]) * base
154
- @rect.centery = y + (@reference[1] + velocity[1]) * base
155
- @reference = [0,0] # Frame of reference must be reset every frame
156
226
  end
157
227
 
158
- end # module MovingSprite
228
+ def update(time)
229
+ super
230
+ reset_jumps if velocity.y.abs < 1 # XXX: velocity.y == 0 at the exact peak of a jump. not a huge problem in practice?
231
+ apply_impulse(CP::Vec2.new(@going[:left]*-1 + @going[:right], @going[:up]*-1 + @going[:down]))
159
232
 
160
- # This is a mixin module to allow a sprite to let things pass through some edges
161
- module EdgeSprite
162
- attr_accessor :edges
163
- end
233
+ #@image = @image.rotozoom(@shape.body.a, 1)
234
+ rect.x = @shape.body.p.x
235
+ rect.y = @shape.body.p.y
236
+ end
164
237
 
165
- # This is a mixin module for groups that keep their sprites in a particular region of the screen
166
- module BoundedGroup
238
+ end # ChipmunkPhysicsSprite
167
239
 
168
- # Bounding box (of type Rubygame::Rect)
169
- attr_accessor :bounds
170
- def bounds
171
- @bounds ||= Rubygame::Screen.get_surface().make_rect()
172
- end
240
+ module ChipmunkPhysicsSpaceGroup
173
241
 
174
- def update(*args)
175
- super(*args)
176
- self.each { |sprite|
177
- if sprite.rect.top < bounds.top
178
- sprite.rect.top = bounds.top
179
- sprite.stop(:up) if sprite.respond_to?(:stop)
180
- end
181
- if sprite.rect.bottom > bounds.bottom
182
- sprite.rect.bottom = bounds.bottom
183
- sprite.stop(:down) if sprite.respond_to?(:stop)
184
- end
185
- if sprite.rect.left < bounds.left
186
- sprite.rect.left = bounds.left
187
- sprite.stop(:left) if sprite.respond_to?(:stop)
188
- end
189
- if sprite.rect.right > bounds.right
190
- sprite.rect.right = bounds.right
191
- sprite.stop(:right) if sprite.respond_to?(:stop)
192
- end
193
- }
242
+ # Get the Chipmunk Space
243
+ def space
244
+ @space ||= CP::Space.new
194
245
  end
195
-
196
- end
197
246
 
198
- # This is a mixin module for groups of sprites with a constant force acting on them
199
- module ForceGroup
200
- # Force vector [vx, vy, max_applied (optional)]
201
- def force=(value)
202
- value[2] = {} unless value
203
- @force = value
247
+ # adds a collision callback (usually done by a listener)
248
+ def add_collision_func(by, to)
249
+ @space.add_collision_func(by, to) { |by, to| yield by, to }
204
250
  end
205
251
 
206
- def update(*args)
207
- super(*args)
208
- force = [@force[0] * args[0]/1000.0, @force[1] * args[0]/1000.0, @force[2]] # Make sure force is applied the same no matter how fast we're rendering
209
- self.each { |sprite|
210
- sprite.apply_force(force) if sprite.respond_to?(:apply_force)
211
- }
212
- end
213
- end # module ForceGroup
214
-
215
- # This is a mixin module for collisions between sprites
216
- module CollideGroup
217
-
218
- def update(*args)
219
- super(*args)
220
- self.each { |sprite|
221
- self.each { |sprite2|
222
- if sprite != sprite2 and sprite.respond_to?(:stop) and sprite.collide_sprite?sprite2
223
- @sprite2_edges = {:top => true, :bottom => true, :left => true, :right => true}
224
- @sprite2_edges = sprite2.edges if sprite2.respond_to?(:edges)
225
- d = sprite2.rect.top - sprite.rect.bottom
226
- if d < 0 and d > -5 and @sprite2_edges[:top] # Sprite is on top
227
- if !sprite.respond_to?(:velocity) or sprite.velocity[1] > 0
228
- sprite2.hit([nil,sprite.velocity[1]], sprite) if sprite2.respond_to?(:hit)
229
- sprite.rect.bottom += d if (!sprite.respond_to?(:edges) or sprite.edges[:bottom])
230
- end
231
- sprite.stop(:down, (!sprite.respond_to?(:going) or sprite.going[:down]) ? 1/(args[0]/1000.0) * 0.1 : 0)
232
- end
233
- d = sprite.rect.top - sprite2.rect.bottom
234
- if d < 0 and d > -5 and @sprite2_edges[:bottom] # Sprite is on bottom
235
- if !sprite.respond_to?(:velocity) or sprite.velocity[1] < 0
236
- sprite2.hit([nil,sprite.velocity[1]], sprite) if sprite2.respond_to?(:hit)
237
- sprite.rect.top += d if !sprite.respond_to?(:edges) or sprite.edges[:top]
238
- end
239
- sprite.stop(:up, (!sprite.respond_to?(:going) or sprite.going[:up]) ? 1/(args[0]/1000.0) * 0.1 : 0)
240
- end
241
- d = sprite2.rect.left - sprite.rect.right
242
- if d < 0 and d > -5 and @sprite2_edges[:left] # Sprite is on left
243
- if !sprite.respond_to?(:velocity) or sprite.velocity[0] > 0
244
- sprite2.hit([sprite.velocity[0],nil], sprite) if sprite2.respond_to?(:hit)
245
- sprite.rect.right += d if !sprite.respond_to?(:edges) or sprite.edges[:right]
246
- end
247
- sprite.stop(:right, (!sprite.respond_to?(:going) or sprite.going[:right]) ? 1/(args[0]/1000.0) * 0.1 : 0)
248
- end
249
- d = sprite.rect.left - sprite2.rect.right
250
- if d < 0 and d > -5 and @sprite2_edges[:right] # Sprite is on right
251
- if !sprite.respond_to?(:velocity) or sprite.velocity[0] < 0
252
- sprite2.hit([sprite.velocity[0],nil], sprite) if sprite2.respond_to?(:hit)
253
- sprite.rect.left += d if (!sprite.respond_to?(:edges) or sprite.edges[:left])
254
- end
255
- sprite.stop(:left, (!sprite.respond_to?(:going) or sprite.going[:left]) ? 1/(args[0]/1000.0) * 0.1 : 0)
256
- end
257
- end
258
- }
259
- }
252
+ # Set the gravity (set in a vector so that horizontal "gravity" is also possible)
253
+ def gravity=(v)
254
+ case v
255
+ when Array
256
+ space.gravity = CP::Vec2.new(v[0], v[1])
257
+ when CP::Vec2
258
+ space.gravity = v
259
+ when Numeric
260
+ space.gravity = CP::Vec2.new(0, v)
261
+ else
262
+ raise ArgumentError.new("Invalid gravity value type #{v.class}")
263
+ end
260
264
  end
261
- end
262
265
 
263
- # This is a basic class for updatable, image-based, sprites with a rectangular box matching their image
264
- class BasicSprite
265
- include Rubygame::Sprites::Sprite
266
+ # Set the damping/friction across all space
267
+ def damping=(v)
268
+ space.damping = v
269
+ end
266
270
 
267
- # Override this in subclasses to have a default image
268
- def self.default_image; end;
271
+ # Add a sprite to this group
272
+ def push(*args)
273
+ super
274
+ args.each do |sprite|
275
+ next unless sprite.respond_to?:shape
276
+ space.add_body sprite.shape.body unless sprite.shape.body.m == CP::INFINITY
277
+ space.add_shape sprite.shape
278
+ end
279
+ end
269
280
 
270
- # Create a new BasicSprite. Pass x, y coordinates for location to spawn.
271
- # Pass path to image if you have not ovrriden default_image
272
- def initialize(x,y,image=nil)
273
- super()
274
- if image
275
- @image = Rubygame::Surface[image]
276
- throw "Image #{image} failed to load. Looking in: #{Rubygame::Surface.autoload_dirs.join(":")}" unless @image
277
- else
278
- @image = default_image
281
+ # Add bounds to sides of a rectangle (usually the screen)
282
+ def bound(screen, side)
283
+ case side
284
+ when Array
285
+ side.each { |side| bound(screen, side) }
286
+ return
287
+ when :top
288
+ side_shape = CP::Shape::Segment.new(CP::Body.new(CP::INFINITY, CP::INFINITY), CP::Vec2::ZERO, CP::Vec2.new(screen.width, 0), 0)
289
+ side_shape.body.p = CP::Vec2.new(0, 0)
290
+ when :bottom
291
+ side_shape = CP::Shape::Segment.new(CP::Body.new(CP::INFINITY, CP::INFINITY), CP::Vec2::ZERO, CP::Vec2.new(screen.width, 0), 0)
292
+ side_shape.body.p = CP::Vec2.new(0, screen.height)
293
+ when :left
294
+ side_shape = CP::Shape::Segment.new(CP::Body.new(CP::INFINITY, CP::INFINITY), CP::Vec2::ZERO, CP::Vec2.new(0, screen.height), 0)
295
+ side_shape.body.p = CP::Vec2.new(0, 0)
296
+ when :right
297
+ side_shape = CP::Shape::Segment.new(CP::Body.new(CP::INFINITY, CP::INFINITY), CP::Vec2::ZERO, CP::Vec2.new(0, screen.height), 0)
298
+ side_shape.body.p = CP::Vec2.new(screen.width, 0)
299
+ else
300
+ raise ArgumentError.new("Invalid side to bound #{side}")
279
301
  end
280
- @rect = Rubygame::Rect.new(x,y,*@image.size)
302
+ side_shape.collision_type = :wall
303
+ space.add_static_shape side_shape
281
304
  end
282
305
 
283
- def update(time); end
306
+ # Update the Chipmunk space
307
+ def update(time, *args)
308
+ super
309
+ space.step(time/1000.0)
310
+ end
284
311
 
285
- end # class BasicMovingBoundedSprit
312
+ end # ChipmunkPhysicsSpaceGroup
286
313
 
287
314
  end # module Sprites
288
315
 
289
- end # module Rubygame
316
+ # Default rubygame clock sucks the CPU. We can do better.
317
+ class Clock
318
+ def tick()
319
+ passed = Clock::runtime - @last_tick # how long since the last tick?
320
+ if @target_frametime and (wait = @target_frametime - passed) > 0
321
+ return Clock::wait(wait) + passed
322
+ end
323
+ return passed
324
+ ensure
325
+ @last_tick = Clock::runtime
326
+ @ticks += 1
327
+ end
328
+ end
290
329
 
291
- class XGame
330
+ end # module Rubygame
292
331
 
293
- @@framerate = 60
294
- def self.framerate
295
- @@framerate
296
- end
332
+ module XGame
333
+ # NOTE: Remember to update this in ./configure as well as xgame.gemspec
334
+ VERSION = [0,1,0] # MAJOR, MINOR, PATCH
335
+ end
297
336
 
298
- # This method is the heart of XGame. Call it with a block that sets up your program.
299
- def self.run(title = 'XGame', size = [], framerate = @@framerate, ignore_events = [], &block)
337
+ # This method is the heart of XGame. Call it with a block that sets up your program.
338
+ def XGame(title = 'XGame', size = [], frametime = 15, ignore_events = [], &block)
300
339
 
301
- Rubygame.init() # Set stuff up
340
+ Rubygame.init() # Set stuff up
302
341
 
303
- if Rubygame::Screen.respond_to?(:get_resolution)
304
- size[0] = Rubygame::Screen.get_resolution[0] unless size[0]
305
- size[1] = Rubygame::Screen.get_resolution[1] unless size[1]
306
- else
307
- size[0] = 640 unless size[0]
308
- size[1] = 480 unless size[1]
309
- end
342
+ if Rubygame::Screen.respond_to?(:get_resolution)
343
+ size[0] = Rubygame::Screen.get_resolution[0] unless size[0]
344
+ size[1] = Rubygame::Screen.get_resolution[1] unless size[1]
345
+ else
346
+ size[0] = 320 unless size[0]
347
+ size[1] = 240 unless size[1]
348
+ end
310
349
 
311
- # The events queue gets filled up with all user input into our window
312
- events = Rubygame::EventQueue.new()
313
- events.ignore = ignore_events # Let's save cycles by ignoring events of some types
314
-
315
- # The clock keeps us from eating the CPU
316
- clock = Rubygame::Clock.new()
317
- clock.target_framerate = framerate # Let's aim to render at some framerate
318
-
319
- # Set up autoloading for Surfaces. Surfaces will be loaded automatically the first time you use Surface["filename"].
320
- Rubygame::Surface.autoload_dirs = [ File.dirname($0) ] # XXX: this should include other paths depending on the platform
321
-
322
- # Create a world for sprites to live in
323
- world = Rubygame::Sprites::Group.new
324
- world.extend(Rubygame::Sprites::DepthSortGroup) # Let them get in front of each other
325
-
326
- # Grab the screen and create a background
327
- screen = Rubygame::Screen.new(size, 0, [Rubygame::HWSURFACE, Rubygame::NOFRAME])
328
- background = Rubygame::Surface.new(screen.size)
329
-
330
- # This is where event handlers will get stored
331
- listeners = Rubygame::ListenerList.new
332
-
333
- # Include the user code
334
- yield screen, background, world, listeners
335
-
336
- # Refresh the screen once. During the loop, we'll use 'dirty rect' updating
337
- # to refresh only the parts of the screen that have changed.
338
- screen.update()
339
-
340
- catch(:quit) do
341
- loop do
342
- events.push Rubygame::LoopEvent.new
343
- events.each do |event|
344
- case event
345
- when Rubygame::ActiveEvent
346
- # ActiveEvent appears when the window gains or loses focus.
347
- # This helps to ensure everything is refreshed after the Rubygame window has been covered up by a different window.
348
- screen.update()
349
- when Rubygame::QuitEvent
350
- # QuitEvent appears when the user closes the window, or otherwise signals they wish to quit
351
- throw :quit
352
- else
353
- listeners[event.class].each { |callback| callback.call(event) } if listeners.key?(event.class)
354
- end
350
+ # The events queue gets filled up with all user input into our window
351
+ events = Rubygame::EventQueue.new()
352
+ events.ignore = ignore_events # Let's save cycles by ignoring events of some types
353
+
354
+ # The clock keeps us from eating the CPU
355
+ clock = Rubygame::Clock.new()
356
+ clock.target_frametime = frametime # Let's aim to render at some framerate
357
+
358
+ # Set up autoloading for Surfaces. Surfaces will be loaded automatically the first time you use Surface["filename"].
359
+ Rubygame::Surface.autoload_dirs = [ File.dirname($0) ] # XXX: this should include other paths depending on the platform
360
+
361
+ # Create a world for sprites to live in
362
+ world = Rubygame::Sprites::Group.new
363
+ world.extend(Rubygame::Sprites::UpdateGroup) # The world can undraw and draw its Sprites
364
+ world.extend(Rubygame::Sprites::DepthSortGroup) # Let them get in front of each other
365
+
366
+ # Grab the screen and create a background
367
+ screen = Rubygame::Screen.new(size, 0, [Rubygame::HWSURFACE, Rubygame::NOFRAME])
368
+ screen.title = title # Set the window title
369
+ background = Rubygame::Surface.new(screen.size)
370
+
371
+ # This is where event handlers will get stored
372
+ listeners = Rubygame::ListenerList.new
373
+ listeners.world = world
374
+
375
+ # Include the user code
376
+ yield screen, background, world, listeners
377
+
378
+ # Refresh the screen once. During the loop, we'll use 'dirty rect' updating
379
+ # to refresh only the parts of the screen that have changed.
380
+ screen.update()
381
+
382
+ catch(:quit) do
383
+ loop do
384
+
385
+ world.undraw(screen, background)
386
+
387
+ events.push Rubygame::LoopEvent.new
388
+ events.each do |event|
389
+ case event
390
+ when Rubygame::ActiveEvent
391
+ # ActiveEvent appears when the window gains or loses focus.
392
+ # This helps to ensure everything is refreshed after the Rubygame window has been covered up by a different window.
393
+ screen.update()
394
+ when Rubygame::QuitEvent
395
+ # QuitEvent appears when the user closes the window, or otherwise signals they wish to quit
396
+ throw :quit
397
+ else
398
+ listeners[event.class].each { |callback| callback.call(event) } if listeners.key?(event.class)
355
399
  end
400
+ end
356
401
 
357
- world.undraw(screen, background)
358
- @@framerate = clock.tick
359
- world.update(@@framerate)
360
- screen.update_rects(world.draw(screen))
402
+ world.update(clock.tick)
403
+ screen.update_rects(world.draw(screen))
361
404
 
362
- screen.title = "#{title} [#{clock.framerate.to_i} fps]"
363
- end
405
+ screen.title = "#{title} [#{clock.framerate.to_i} fps]" if $DEBUG
364
406
  end
407
+ end
365
408
 
366
- puts "#{title} is Quitting!"
367
- Rubygame.quit()
368
-
369
- end #run
409
+ puts "#{title} is Quitting!"
410
+ Rubygame.quit()
370
411
 
371
412
  end #XGame
data/xgame.gemspec CHANGED
@@ -1,11 +1,11 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "xgame"
3
- s.version = "0.0.1"
4
- s.date = "2008-10-18"
5
- s.summary = "High-level game framework based on rubygame"
3
+ s.version = "0.1.0" # NOTE: Don't forget to update the configure script and lib/xgame.rb
4
+ s.date = "2008-11-15"
5
+ s.summary = "High-level game framework based on rubygame and chipmunk"
6
6
  s.email = "singpolyma@singpolyma.net"
7
7
  s.homepage = "http://github.com/singpolyma/xgame"
8
- s.description = "High-level game framework based on rubygame"
8
+ s.description = "High-level game framework based on rubygame and chipmunk"
9
9
  s.has_rdoc = true
10
10
  s.authors = ['Stephen Paul Weber']
11
11
  s.files = ["README",
@@ -14,6 +14,6 @@ Gem::Specification.new do |s|
14
14
  "xgame.gemspec",
15
15
  "lib/xgame.rb"]
16
16
  s.extra_rdoc_files = ["README", "COPYING", "TODO"]
17
- s.add_dependency("rubygame", ["> 0.0.0"])
17
+ s.add_dependency("rubygame", ["> 2.3.0"])
18
18
  end
19
19
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: singpolyma-xgame
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Paul Weber
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-10-18 00:00:00 -07:00
12
+ date: 2008-11-15 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -19,9 +19,9 @@ dependencies:
19
19
  requirements:
20
20
  - - ">"
21
21
  - !ruby/object:Gem::Version
22
- version: 0.0.0
22
+ version: 2.3.0
23
23
  version:
24
- description: High-level game framework based on rubygame
24
+ description: High-level game framework based on rubygame and chipmunk
25
25
  email: singpolyma@singpolyma.net
26
26
  executables: []
27
27
 
@@ -62,6 +62,6 @@ rubyforge_project:
62
62
  rubygems_version: 1.2.0
63
63
  signing_key:
64
64
  specification_version: 2
65
- summary: High-level game framework based on rubygame
65
+ summary: High-level game framework based on rubygame and chipmunk
66
66
  test_files: []
67
67