gosu 0.7.9-universal-darwin-9

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ Copyright (C) 2004-2007 Julian Raschke, Jan Lücker and all contributors.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
+ Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
20
+
21
+ Julian Raschke julian@raschke.de
22
+ Jan Lücker jan.luecker@gmx.de
23
+ http://code.google.com/p/gosu/
24
+
25
+ ***
26
+
27
+ Does NOT apply to fmod.dll shipped with the Windows version of Gosu,
28
+ or the bytecode of FMOD contained in Gosu.framework on OS X.
29
+ FMOD is a property of Firelight Technologies Pty, Ltd., see www.fmod.org.
data/README ADDED
@@ -0,0 +1,13 @@
1
+ Moin moin, dear Gosu user!
2
+
3
+ * The latest documentation on how to install/set up Gosu can be found on:
4
+ http://code.google.com/p/gosu/wiki/DocsOverview
5
+
6
+ * Try doing to the tutorial there if you don't know how to start out!
7
+
8
+ * If you have any questions or feedback,
9
+ leave a comment on one of Gosu's wiki page,
10
+ mail me at julian@raschke.de,
11
+ or try your luck in #gosu on irc.freenode.org.
12
+
13
+ We hope you'll enjoy using Gosu!
@@ -0,0 +1,283 @@
1
+ ## File: ChipmunkIntegration.rb
2
+ ## Author: Dirk Johnson
3
+ ## Version: 1.0.0
4
+ ## Date: 2007-10-05
5
+ ## License: Same as for Gosu (MIT)
6
+ ## Comments: Based on the Gosu Ruby Tutorial, but incorporating the Chipmunk Physics Engine
7
+ ## See http://code.google.com/p/gosu/wiki/RubyChipmunkIntegration for the accompanying text.
8
+
9
+ require 'rubygems'
10
+ require 'gosu'
11
+ require 'chipmunk'
12
+
13
+ SCREEN_WIDTH = 640
14
+ SCREEN_HEIGHT = 480
15
+
16
+ # The number of steps to process every Gosu update
17
+ # The Player ship can get going so fast as to "move through" a
18
+ # star without triggering a collision; an increased number of
19
+ # Chipmunk step calls per update will effectively avoid this issue
20
+ SUBSTEPS = 6
21
+
22
+ # Convenience methods for converting between Gosu degrees, radians, and Vec2 vectors
23
+ class Numeric
24
+ def gosu_to_radians
25
+ (self - 90) * Math::PI / 180.0
26
+ end
27
+
28
+ def radians_to_gosu
29
+ self * 180.0 / Math::PI + 90
30
+ end
31
+
32
+ def radians_to_vec2
33
+ CP::Vec2.new(Math::cos(self), Math::sin(self))
34
+ end
35
+ end
36
+
37
+ # Layering of sprites
38
+ module ZOrder
39
+ Background, Stars, Player, UI = *0..3
40
+ end
41
+
42
+ # This game will have one Player in the form of a ship
43
+ class Player
44
+ attr_reader :shape
45
+
46
+ def initialize(window, shape)
47
+ @image = Gosu::Image.new(window, "media/Starfighter.bmp", false)
48
+ @shape = shape
49
+ @shape.body.p = CP::Vec2.new(0.0, 0.0) # position
50
+ @shape.body.v = CP::Vec2.new(0.0, 0.0) # velocity
51
+
52
+ # Keep in mind that down the screen is positive y, which means that PI/2 radians,
53
+ # which you might consider the top in the traditional Trig unit circle sense is actually
54
+ # the bottom; thus 3PI/2 is the top
55
+ @shape.body.a = (3*Math::PI/2.0) # angle in radians; faces towards top of screen
56
+ end
57
+
58
+ # Directly set the position of our Player
59
+ def warp(vect)
60
+ @shape.body.p = vect
61
+ end
62
+
63
+ # Apply negative Torque; Chipmunk will do the rest
64
+ # SUBSTEPS is used as a divisor to keep turning rate constant
65
+ # even if the number of steps per update are adjusted
66
+ def turn_left
67
+ @shape.body.t -= 400.0/SUBSTEPS
68
+ end
69
+
70
+ # Apply positive Torque; Chipmunk will do the rest
71
+ # SUBSTEPS is used as a divisor to keep turning rate constant
72
+ # even if the number of steps per update are adjusted
73
+ def turn_right
74
+ @shape.body.t += 400.0/SUBSTEPS
75
+ end
76
+
77
+ # Apply forward force; Chipmunk will do the rest
78
+ # SUBSTEPS is used as a divisor to keep acceleration rate constant
79
+ # even if the number of steps per update are adjusted
80
+ # Here we must convert the angle (facing) of the body into
81
+ # forward momentum by creating a vector in the direction of the facing
82
+ # and with a magnitude representing the force we want to apply
83
+ def accelerate
84
+ @shape.body.apply_force((@shape.body.a.radians_to_vec2 * (3000.0/SUBSTEPS)), CP::Vec2.new(0.0, 0.0))
85
+ end
86
+
87
+ # Apply even more forward force
88
+ # See accelerate for more details
89
+ def boost
90
+ @shape.body.apply_force((@shape.body.a.radians_to_vec2 * (3000.0)), CP::Vec2.new(0.0, 0.0))
91
+ end
92
+
93
+ # Apply reverse force
94
+ # See accelerate for more details
95
+ def reverse
96
+ @shape.body.apply_force(-(@shape.body.a.radians_to_vec2 * (1000.0/SUBSTEPS)), CP::Vec2.new(0.0, 0.0))
97
+ end
98
+
99
+ # Wrap to the other side of the screen when we fly off the edge
100
+ def validate_position
101
+ l_position = CP::Vec2.new(@shape.body.p.x % SCREEN_WIDTH, @shape.body.p.y % SCREEN_HEIGHT)
102
+ @shape.body.p = l_position
103
+ end
104
+
105
+ def draw
106
+ @image.draw_rot(@shape.body.p.x, @shape.body.p.y, ZOrder::Player, @shape.body.a.radians_to_gosu)
107
+ end
108
+ end
109
+
110
+ # See how simple our Star is?
111
+ # Of course... it just sits around and looks good...
112
+ class Star
113
+ attr_reader :shape
114
+
115
+ def initialize(animation, shape)
116
+ @animation = animation
117
+ @color = Gosu::Color.new(0xff000000)
118
+ @color.red = rand(255 - 40) + 40
119
+ @color.green = rand(255 - 40) + 40
120
+ @color.blue = rand(255 - 40) + 40
121
+ @shape = shape
122
+ @shape.body.p = CP::Vec2.new(rand * SCREEN_WIDTH, rand * SCREEN_HEIGHT) # position
123
+ @shape.body.v = CP::Vec2.new(0.0, 0.0) # velocity
124
+ @shape.body.a = (3*Math::PI/2.0) # angle in radians; faces towards top of screen
125
+ end
126
+
127
+ def draw
128
+ img = @animation[Gosu::milliseconds / 100 % @animation.size];
129
+ img.draw(@shape.body.p.x - img.width / 2.0, @shape.body.p.y - img.height / 2.0, ZOrder::Stars, 1, 1, @color, :additive)
130
+ end
131
+ end
132
+
133
+ # The Gosu::Window is always the "environment" of our game
134
+ # It also provides the pulse of our game
135
+ class GameWindow < Gosu::Window
136
+ def initialize
137
+ super(SCREEN_WIDTH, SCREEN_HEIGHT, false, 16)
138
+ self.caption = "Gosu & Chipmunk Integration Demo"
139
+ @background_image = Gosu::Image.new(self, "media/Space.png", true)
140
+
141
+ # Put the beep here, as it is the environment now that determines collision
142
+ @beep = Gosu::Sample.new(self, "media/Beep.wav")
143
+
144
+ # Put the score here, as it is the environment that tracks this now
145
+ @score = 0
146
+ @font = Gosu::Font.new(self, Gosu::default_font_name, 20)
147
+
148
+ # Time increment over which to apply a physics "step" ("delta t")
149
+ @dt = (1.0/60.0)
150
+
151
+ # Create our Space and set its damping
152
+ # A damping of 0.8 causes the ship bleed off its force and torque over time
153
+ # This is not realistic behavior in a vacuum of space, but it gives the game
154
+ # the feel I'd like in this situation
155
+ @space = CP::Space.new
156
+ @space.damping = 0.8
157
+
158
+ # Create the Body for the Player
159
+ body = CP::Body.new(10.0, 150.0)
160
+
161
+ # In order to create a shape, we must first define it
162
+ # Chipmunk defines 3 types of Shapes: Segments, Circles and Polys
163
+ # We'll use s simple, 4 sided Poly for our Player (ship)
164
+ # You need to define the vectors so that the "top" of the Shape is towards 0 radians (the right)
165
+ shape_array = [CP::Vec2.new(-25.0, -25.0), CP::Vec2.new(-25.0, 25.0), CP::Vec2.new(25.0, 1.0), CP::Vec2.new(25.0, -1.0)]
166
+ shape = CP::Shape::Poly.new(body, shape_array, CP::Vec2.new(0,0))
167
+
168
+ # The collision_type of a shape allows us to set up special collision behavior
169
+ # based on these types. The actual value for the collision_type is arbitrary
170
+ # and, as long as it is consistent, will work for us; of course, it helps to have it make sense
171
+ shape.collision_type = :ship
172
+
173
+ @space.add_body(body)
174
+ @space.add_shape(shape)
175
+
176
+ @player = Player.new(self, shape)
177
+ @player.warp(CP::Vec2.new(320, 240)) # move to the center of the window
178
+
179
+ @star_anim = Gosu::Image::load_tiles(self, "media/Star.png", 25, 25, false)
180
+ @stars = Array.new
181
+
182
+ # Here we define what is supposed to happen when a Player (ship) collides with a Star
183
+ # I create a @remove_shapes array because we cannot remove either Shapes or Bodies
184
+ # from Space within a collision closure, rather, we have to wait till the closure
185
+ # is through executing, then we can remove the Shapes and Bodies
186
+ # In this case, the Shapes and the Bodies they own are removed in the Gosu::Window.update phase
187
+ # by iterating over the @remove_shapes array
188
+ # Also note that both Shapes involved in the collision are passed into the closure
189
+ # in the same order that their collision_types are defined in the add_collision_func call
190
+ @remove_shapes = []
191
+ @space.add_collision_func(:ship, :star) do |ship_shape, star_shape|
192
+ @score += 10
193
+ @beep.play
194
+ @remove_shapes << star_shape
195
+ end
196
+
197
+ # Here we tell Space that we don't want one star bumping into another
198
+ # The reason we need to do this is because when the Player hits a Star,
199
+ # the Star will travel until it is removed in the update cycle below
200
+ # which means it may collide and therefore push other Stars
201
+ # To see the effect, remove this line and play the game, every once in a while
202
+ # you'll see a Star moving
203
+ @space.add_collision_func(:star, :star, &nil)
204
+ end
205
+
206
+ def update
207
+ # Step the physics environment SUBSTEPS times each update
208
+ SUBSTEPS.times do
209
+ # This iterator makes an assumption of one Shape per Star making it safe to remove
210
+ # each Shape's Body as it comes up
211
+ # If our Stars had multiple Shapes, as would be required if we were to meticulously
212
+ # define their true boundaries, we couldn't do this as we would remove the Body
213
+ # multiple times
214
+ # We would probably solve this by creating a separate @remove_bodies array to remove the Bodies
215
+ # of the Stars that were gathered by the Player
216
+ @remove_shapes.each do |shape|
217
+ @stars.delete_if { |star| star.shape == shape }
218
+ @space.remove_body(shape.body)
219
+ @space.remove_shape(shape)
220
+ end
221
+ @remove_shapes.clear # clear out the shapes for next pass
222
+
223
+ # When a force or torque is set on a Body, it is cumulative
224
+ # This means that the force you applied last SUBSTEP will compound with the
225
+ # force applied this SUBSTEP; which is probably not the behavior you want
226
+ # We reset the forces on the Player each SUBSTEP for this reason
227
+ @player.shape.body.reset_forces
228
+
229
+ # Wrap around the screen to the other side
230
+ @player.validate_position
231
+
232
+ # Check keyboard
233
+ if button_down? Gosu::Button::KbLeft
234
+ @player.turn_left
235
+ end
236
+ if button_down? Gosu::Button::KbRight
237
+ @player.turn_right
238
+ end
239
+
240
+ if button_down? Gosu::Button::KbUp
241
+ if ( (button_down? Gosu::Button::KbRightShift) || (button_down? Gosu::Button::KbLeftShift) )
242
+ @player.boost
243
+ else
244
+ @player.accelerate
245
+ end
246
+ elsif button_down? Gosu::Button::KbDown
247
+ @player.reverse
248
+ end
249
+
250
+ # Perform the step over @dt period of time
251
+ # For best performance @dt should remain consistent for the game
252
+ @space.step(@dt)
253
+ end
254
+
255
+ # Each update (not SUBSTEP) we see if we need to add more Stars
256
+ if rand(100) < 4 and @stars.size < 25 then
257
+ body = CP::Body.new(0.0001, 0.0001)
258
+ shape = CP::Shape::Circle.new(body, 25/2, CP::Vec2.new(0.0, 0.0))
259
+ shape.collision_type = :star
260
+
261
+ @space.add_body(body)
262
+ @space.add_shape(shape)
263
+
264
+ @stars.push(Star.new(@star_anim, shape))
265
+ end
266
+ end
267
+
268
+ def draw
269
+ @background_image.draw(0, 0, ZOrder::Background)
270
+ @player.draw
271
+ @stars.each { |star| star.draw }
272
+ @font.draw("Score: #{@score}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffffff00)
273
+ end
274
+
275
+ def button_down(id)
276
+ if id == Gosu::Button::KbEscape
277
+ close
278
+ end
279
+ end
280
+ end
281
+
282
+ window = GameWindow.new
283
+ window.show
@@ -0,0 +1,231 @@
1
+ # Basically, the tutorial game taken to a jump'n'run perspective.
2
+
3
+ # Shows how to
4
+ # * implement jumping/gravity
5
+ # * implement scrolling
6
+ # * implement a simple tile-based map
7
+ # * load levels from primitive text files
8
+
9
+ # Some exercises, starting at the real basics:
10
+ # 0) understand the existing code!
11
+ # As shown in the tutorial:
12
+ # 1) change it use Gosu's Z-ordering
13
+ # 2) add gamepad support
14
+ # 3) add a score as in the tutorial game
15
+ # 4) similarly, add sound effects for various events
16
+ # Exploring this game's code and Gosu:
17
+ # 5) make the player wider, so he doesn't fall off edges as easily
18
+ # 6) add background music (check if playing in Window#update to implement
19
+ # looping)
20
+ # 7) implement parallax scrolling for the star background!
21
+ # Getting tricky:
22
+ # 8) optimize Map#draw so only tiles on screen are drawn (needs modulo, a pen
23
+ # and paper to figure out)
24
+ # 9) add loading of next level when all gems are collected
25
+ # ...Enemies, a more sophisticated object system, weapons, title and credits
26
+ # screens...
27
+
28
+ begin
29
+ # In case you use Gosu via rubygems.
30
+ require 'rubygems'
31
+ rescue LoadError
32
+ # In case you don't.
33
+ end
34
+
35
+ require 'gosu'
36
+ include Gosu
37
+
38
+ module Tiles
39
+ Grass = 0
40
+ Earth = 1
41
+ end
42
+
43
+ class CollectibleGem
44
+ attr_reader :x, :y
45
+
46
+ def initialize(image, x, y)
47
+ @image = image
48
+ @x, @y = x, y
49
+ end
50
+
51
+ def draw(screen_x, screen_y)
52
+ # Draw, slowly rotating
53
+ @image.draw_rot(@x - screen_x, @y - screen_y, 0,
54
+ 25 * Math.sin(milliseconds / 133.7))
55
+ end
56
+ end
57
+
58
+ # Player class.
59
+ class CptnRuby
60
+ attr_reader :x, :y
61
+
62
+ def initialize(window, x, y)
63
+ @x, @y = x, y
64
+ @dir = :left
65
+ @vy = 0 # Vertical velocity
66
+ @map = window.map
67
+ # Load all animation frames
68
+ @standing, @walk1, @walk2, @jump =
69
+ *Image.load_tiles(window, "media/CptnRuby.png", 50, 50, false)
70
+ # This always points to the frame that is currently drawn.
71
+ # This is set in update, and used in draw.
72
+ @cur_image = @standing
73
+ end
74
+
75
+ def draw(screen_x, screen_y)
76
+ # Flip vertically when facing to the left.
77
+ if @dir == :left then
78
+ offs_x = -25
79
+ factor = 1.0
80
+ else
81
+ offs_x = 25
82
+ factor = -1.0
83
+ end
84
+ @cur_image.draw(@x - screen_x + offs_x, @y - screen_y - 49, 0, factor, 1.0)
85
+ end
86
+
87
+ # Could the object be placed at x + offs_x/y + offs_y without being stuck?
88
+ def would_fit(offs_x, offs_y)
89
+ # Check at the center/top and center/bottom for map collisions
90
+ not @map.solid?(@x + offs_x, @y + offs_y) and
91
+ not @map.solid?(@x + offs_x, @y + offs_y - 45)
92
+ end
93
+
94
+ def update(move_x)
95
+ # Select image depending on action
96
+ if (move_x == 0)
97
+ @cur_image = @standing
98
+ else
99
+ @cur_image = (milliseconds / 175 % 2 == 0) ? @walk1 : @walk2
100
+ end
101
+ if (@vy < 0)
102
+ @cur_image = @jump
103
+ end
104
+
105
+ # Directional walking, horizontal movement
106
+ if move_x > 0 then
107
+ @dir = :right
108
+ move_x.times { if would_fit(1, 0) then @x += 1 end }
109
+ end
110
+ if move_x < 0 then
111
+ @dir = :left
112
+ (-move_x).times { if would_fit(-1, 0) then @x -= 1 end }
113
+ end
114
+
115
+ # Acceleration/gravity
116
+ # By adding 1 each frame, and (ideally) adding vy to y, the player's
117
+ # jumping curve will be the parabole we want it to be.
118
+ @vy += 1
119
+ # Vertical movement
120
+ if @vy > 0 then
121
+ @vy.times { if would_fit(0, 1) then @y += 1 else @vy = 0 end }
122
+ end
123
+ if @vy < 0 then
124
+ (-@vy).times { if would_fit(0, -1) then @y -= 1 else @vy = 0 end }
125
+ end
126
+ end
127
+
128
+ def try_to_jump
129
+ if @map.solid?(@x, @y + 1) then
130
+ @vy = -20
131
+ end
132
+ end
133
+
134
+ def collect_gems(gems)
135
+ # Same as in the tutorial game.
136
+ gems.reject! do |c|
137
+ (c.x - @x).abs < 50 and (c.y - @y).abs < 50
138
+ end
139
+ end
140
+ end
141
+
142
+ # Map class holds and draws tiles and gems.
143
+ class Map
144
+ attr_reader :width, :height, :gems
145
+
146
+ def initialize(window, filename)
147
+ # Load 60x60 tiles, 5px overlap in all four directions.
148
+ @tileset = Image.load_tiles(window, "media/CptnRuby Tileset.png", 60, 60, true)
149
+ @sky = Image.new(window, "media/Space.png", true)
150
+
151
+ gem_img = Image.new(window, "media/CptnRuby Gem.png", false)
152
+ @gems = []
153
+
154
+ lines = File.readlines(filename).map { |line| line.chop }
155
+ @height = lines.size
156
+ @width = lines[0].size
157
+ @tiles = Array.new(@width) do |x|
158
+ Array.new(@height) do |y|
159
+ case lines[y][x, 1]
160
+ when '"'
161
+ Tiles::Grass
162
+ when '#'
163
+ Tiles::Earth
164
+ when 'x'
165
+ @gems.push(CollectibleGem.new(gem_img, x * 50 + 25, y * 50 + 25))
166
+ nil
167
+ else
168
+ nil
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ def draw(screen_x, screen_y)
175
+ # Sigh, stars!
176
+ @sky.draw(0, 0, 0)
177
+
178
+
179
+ # Very primitive drawing function:
180
+ # Draws all the tiles, some off-screen, some on-screen.
181
+ @height.times do |y|
182
+ @width.times do |x|
183
+ tile = @tiles[x][y]
184
+ if tile
185
+ # Draw the tile with an offset (tile images have some overlap)
186
+ # Scrolling is implemented here just as in the game objects.
187
+ @tileset[tile].draw(x * 50 - screen_x - 5, y * 50 - screen_y - 5, 0)
188
+ end
189
+ end
190
+ end
191
+ @gems.each { |c| c.draw(screen_x, screen_y) }
192
+ end
193
+
194
+ # Solid at a given pixel position?
195
+ def solid?(x, y)
196
+ y < 0 || @tiles[x / 50][y / 50]
197
+ end
198
+ end
199
+
200
+ class Game < Window
201
+ attr_reader :map
202
+
203
+ def initialize
204
+ super(640, 480, false)
205
+ self.caption = "Cptn. Ruby"
206
+ @map = Map.new(self, "media/CptnRuby Map.txt")
207
+ @cptn = CptnRuby.new(self, 400, 100)
208
+ # Scrolling is stored as the position of the top left corner of the screen.
209
+ @screen_x = @screen_y = 0
210
+ end
211
+ def update
212
+ move_x = 0
213
+ move_x -= 5 if button_down? Button::KbLeft
214
+ move_x += 5 if button_down? Button::KbRight
215
+ @cptn.update(move_x)
216
+ @cptn.collect_gems(@map.gems)
217
+ # Scrolling follows player
218
+ @screen_x = [[@cptn.x - 320, 0].max, @map.width * 50 - 640].min
219
+ @screen_y = [[@cptn.y - 240, 0].max, @map.height * 50 - 480].min
220
+ end
221
+ def draw
222
+ @map.draw @screen_x, @screen_y
223
+ @cptn.draw @screen_x, @screen_y
224
+ end
225
+ def button_down(id)
226
+ if id == Button::KbUp then @cptn.try_to_jump end
227
+ if id == Button::KbEscape then close end
228
+ end
229
+ end
230
+
231
+ Game.new.show