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.
- data/COPYING +2 -0
- data/README +21 -2
- data/TODO +49 -1
- data/lib/xgame.rb +301 -260
- data/xgame.gemspec +5 -5
- 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
|
-
|
3
|
+
You may install rubygame using:
|
4
4
|
|
5
5
|
sudo gem install rubygame
|
6
6
|
|
7
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
56
|
-
module MovingSprite
|
104
|
+
module Sprite
|
57
105
|
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
68
|
-
|
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
|
72
|
-
@
|
164
|
+
def velocity
|
165
|
+
@shape.body.v
|
73
166
|
end
|
74
167
|
|
75
|
-
def
|
76
|
-
|
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
|
177
|
+
def go(v)
|
81
178
|
if v[0]
|
82
|
-
if v[0] <= 0
|
83
|
-
|
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
|
-
|
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
|
109
|
-
@
|
110
|
-
@velocity[direction] = 0
|
111
|
-
@going[direction] = false
|
189
|
+
def stop(direction)
|
190
|
+
@going[direction] = 0
|
112
191
|
end
|
113
192
|
|
114
|
-
#
|
115
|
-
def
|
116
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
128
|
-
|
212
|
+
def reset_jumps
|
213
|
+
@jumps = 0
|
129
214
|
end
|
130
215
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
-
#
|
166
|
-
module BoundedGroup
|
238
|
+
end # ChipmunkPhysicsSprite
|
167
239
|
|
168
|
-
|
169
|
-
attr_accessor :bounds
|
170
|
-
def bounds
|
171
|
-
@bounds ||= Rubygame::Screen.get_surface().make_rect()
|
172
|
-
end
|
240
|
+
module ChipmunkPhysicsSpaceGroup
|
173
241
|
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
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
|
-
|
264
|
-
|
265
|
-
|
266
|
+
# Set the damping/friction across all space
|
267
|
+
def damping=(v)
|
268
|
+
space.damping = v
|
269
|
+
end
|
266
270
|
|
267
|
-
#
|
268
|
-
def
|
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
|
-
#
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
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
|
-
|
302
|
+
side_shape.collision_type = :wall
|
303
|
+
space.add_static_shape side_shape
|
281
304
|
end
|
282
305
|
|
283
|
-
|
306
|
+
# Update the Chipmunk space
|
307
|
+
def update(time, *args)
|
308
|
+
super
|
309
|
+
space.step(time/1000.0)
|
310
|
+
end
|
284
311
|
|
285
|
-
end #
|
312
|
+
end # ChipmunkPhysicsSpaceGroup
|
286
313
|
|
287
314
|
end # module Sprites
|
288
315
|
|
289
|
-
|
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
|
-
|
330
|
+
end # module Rubygame
|
292
331
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
-
|
299
|
-
|
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
|
-
|
340
|
+
Rubygame.init() # Set stuff up
|
302
341
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
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
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
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
|
-
|
358
|
-
|
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
|
-
|
363
|
-
end
|
405
|
+
screen.title = "#{title} [#{clock.framerate.to_i} fps]" if $DEBUG
|
364
406
|
end
|
407
|
+
end
|
365
408
|
|
366
|
-
|
367
|
-
|
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.
|
4
|
-
s.date = "2008-
|
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", [">
|
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
|
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-
|
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:
|
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
|
|