singpolyma-xgame 0.0.1 → 0.1.0
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.
- 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
|
|