gosu 0.8.6-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/Gosu/Audio.hpp +171 -0
  3. data/Gosu/AutoLink.hpp +16 -0
  4. data/Gosu/Bitmap.hpp +96 -0
  5. data/Gosu/Buttons.hpp +265 -0
  6. data/Gosu/Color.hpp +204 -0
  7. data/Gosu/Directories.hpp +36 -0
  8. data/Gosu/Font.hpp +83 -0
  9. data/Gosu/Fwd.hpp +31 -0
  10. data/Gosu/Gosu.hpp +34 -0
  11. data/Gosu/Graphics.hpp +115 -0
  12. data/Gosu/GraphicsBase.hpp +110 -0
  13. data/Gosu/IO.hpp +269 -0
  14. data/Gosu/Image.hpp +122 -0
  15. data/Gosu/ImageData.hpp +61 -0
  16. data/Gosu/Input.hpp +149 -0
  17. data/Gosu/Inspection.hpp +14 -0
  18. data/Gosu/Math.hpp +135 -0
  19. data/Gosu/Platform.hpp +93 -0
  20. data/Gosu/Sockets.hpp +156 -0
  21. data/Gosu/TR1.hpp +56 -0
  22. data/Gosu/Text.hpp +71 -0
  23. data/Gosu/TextInput.hpp +70 -0
  24. data/Gosu/Timing.hpp +16 -0
  25. data/Gosu/Utility.hpp +28 -0
  26. data/Gosu/Version.hpp +19 -0
  27. data/Gosu/WinUtility.hpp +75 -0
  28. data/Gosu/Window.hpp +145 -0
  29. data/examples/ChipmunkIntegration.rb +275 -0
  30. data/examples/CptnRuby.rb +223 -0
  31. data/examples/GosuZen.rb +68 -0
  32. data/examples/MoreChipmunkAndRMagick.rb +155 -0
  33. data/examples/OpenGLIntegration.rb +226 -0
  34. data/examples/RMagickIntegration.rb +417 -0
  35. data/examples/TextInput.rb +154 -0
  36. data/examples/Tutorial.rb +131 -0
  37. data/examples/media/Beep.wav +0 -0
  38. data/examples/media/CptnRuby Gem.png +0 -0
  39. data/examples/media/CptnRuby Map.txt +25 -0
  40. data/examples/media/CptnRuby Tileset.png +0 -0
  41. data/examples/media/CptnRuby.png +0 -0
  42. data/examples/media/Cursor.png +0 -0
  43. data/examples/media/Earth.png +0 -0
  44. data/examples/media/Explosion.wav +0 -0
  45. data/examples/media/Landscape.svg +10 -0
  46. data/examples/media/LargeStar.png +0 -0
  47. data/examples/media/Smoke.png +0 -0
  48. data/examples/media/Soldier.png +0 -0
  49. data/examples/media/Space.png +0 -0
  50. data/examples/media/Star.png +0 -0
  51. data/examples/media/Starfighter.bmp +0 -0
  52. data/lib/gosu.rb +20 -0
  53. data/lib/gosu/patches.rb +81 -0
  54. data/lib/gosu/preview.rb +139 -0
  55. data/lib/gosu/run.rb +11 -0
  56. data/lib/gosu/swig_patches.rb +60 -0
  57. data/lib/gosu/zen.rb +89 -0
  58. data/lib64/2.1/gosu.so +0 -0
  59. data/lib64/FreeImage.dll +0 -0
  60. data/lib64/OpenAL32.dll +0 -0
  61. data/lib64/SDL2.dll +0 -0
  62. data/lib64/libsndfile.dll +0 -0
  63. metadata +110 -0
@@ -0,0 +1,145 @@
1
+ //! \file Window.hpp
2
+ //! Interface of the Window class.
3
+
4
+ #ifndef GOSU_WINDOW_HPP
5
+ #define GOSU_WINDOW_HPP
6
+
7
+ #include <Gosu/Fwd.hpp>
8
+ #include <Gosu/Platform.hpp>
9
+ #include <Gosu/Input.hpp>
10
+ #include <Gosu/TR1.hpp>
11
+ #include <memory>
12
+ #include <string>
13
+
14
+ #ifdef GOSU_IS_WIN
15
+ #ifndef NOMINMAX
16
+ #define NOMINMAX
17
+ #endif
18
+ #include <windows.h>
19
+ #endif
20
+
21
+ namespace Gosu
22
+ {
23
+ //! Returns the width (in pixels) of the user's primary screen.
24
+ unsigned screenWidth();
25
+
26
+ //! Returns the height (in pixels) of the user's primary screen.
27
+ unsigned screenHeight();
28
+
29
+ //! Returns the maximum width (in 'points') that is available for a non-fullscreen Window.
30
+ //! All windows larger than this size will automatically be shrunk to fit.
31
+ unsigned availableWidth();
32
+
33
+ //! Returns the maximum height (in 'points') that is available for a non-fullscreen Window.
34
+ //! All windows larger than this size will automatically be shrunk to fit.
35
+ unsigned availableHeight();
36
+
37
+ //! Convenient all-in-one class that serves as the foundation of a standard
38
+ //! Gosu application. Manages initialization of all of Gosu's core components
39
+ //! and provides timing functionality.
40
+ //! Note that you should really only use one instance of this class at the same time.
41
+ //! This may or may not change later.
42
+ class Window
43
+ {
44
+ struct Impl;
45
+ const GOSU_UNIQUE_PTR<Impl> pimpl;
46
+ #if defined(GOSU_CPP11_ENABLED)
47
+ Window(Window&&) = delete;
48
+ Window& operator=(Window&&) = delete;
49
+ Window(const Window&) = delete;
50
+ Window& operator=(const Window&) = delete;
51
+ #endif
52
+
53
+ public:
54
+ //! Constructs a Window.
55
+ //! \param width Width of the window in points; that is, pixels on a normal display, and 'virtual pixels' on a
56
+ //! high-resolution display.
57
+ //! \param height See width.
58
+ //! \param updateInterval Interval in milliseconds between two calls
59
+ //! to the update member function.
60
+ Window(unsigned width, unsigned height, bool fullscreen,
61
+ double updateInterval = 16.666666);
62
+ virtual ~Window();
63
+
64
+ std::wstring caption() const;
65
+ void setCaption(const std::wstring& caption);
66
+
67
+ double updateInterval() const;
68
+
69
+ //! Enters a modal loop where the Window is visible on screen and
70
+ //! receives calls to draw, update etc.
71
+ void show();
72
+ //! Closes the window if it is currently shown.
73
+ void close();
74
+
75
+ //! Called every updateInterval milliseconds while the window is being
76
+ //! shown. Your application's main game logic goes here.
77
+ virtual void update() {}
78
+ //! Called after every update and when the OS wants the window to
79
+ //! repaint itself. Your application's rendering code goes here.
80
+ virtual void draw() {}
81
+
82
+ //! Gives the game a chance to say no to being redrawn.
83
+ //! This is not a definitive answer. The operating system can still force
84
+ //! the window to redraw itself.
85
+ //! By default, the window is redrawn all the time.
86
+ virtual bool needsRedraw() const { return true; }
87
+
88
+ //! If this function returns true, the system arrow cursor is drawn while
89
+ //! over the window.
90
+ virtual bool needsCursor() const { return false; }
91
+
92
+ //! This function is called when the window loses focus on some platforms.
93
+ //! Most importantly, it is called on the iPhone or iPad when the user
94
+ //! locks the screen.
95
+ virtual void loseFocus() {}
96
+
97
+ //! This function is called when the operating system's memory is low.
98
+ //! So far, it is only called in iOS applications.
99
+ virtual void releaseMemory() {}
100
+
101
+ //! Called before update when the user pressed a button while the
102
+ //! window had the focus.
103
+ virtual void buttonDown(Gosu::Button) {}
104
+ //! Same as buttonDown. Called then the user released a button.
105
+ virtual void buttonUp(Gosu::Button) {}
106
+
107
+ // Ignore when SWIG is wrapping this class for Ruby/Gosu.
108
+ #ifndef SWIG
109
+
110
+ const Graphics& graphics() const;
111
+ Graphics& graphics();
112
+
113
+ const Input& input() const;
114
+ Input& input();
115
+
116
+ #ifdef GOSU_IS_UNIX
117
+ // Context for creating shared contexts.
118
+ // Only on Unices (so far).
119
+ typedef std::tr1::shared_ptr<std::tr1::function<void()> > SharedContext;
120
+ SharedContext createSharedContext();
121
+ #endif
122
+
123
+ #ifdef GOSU_IS_IPHONE
124
+ void* rootViewController() const;
125
+ // iPhone-only callbacks for touch events.
126
+ // Note that it does not hurt to override them even if you compile
127
+ // for another platform; if you don't specify "virtual" the code
128
+ // should even be stripped away cleanly.
129
+ virtual void touchBegan(Touch touch) {}
130
+ virtual void touchMoved(Touch touch) {}
131
+ virtual void touchEnded(Touch touch) {}
132
+ #endif
133
+
134
+ const Audio& audio() const;
135
+ Audio& audio();
136
+
137
+ #endif
138
+ };
139
+ }
140
+
141
+ #ifdef GOSU_IS_IPHONE
142
+ Gosu::Window& windowInstance();
143
+ #endif
144
+
145
+ #endif
@@ -0,0 +1,275 @@
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 https://github.com/jlnr/gosu/wiki/Ruby-Chipmunk-Integration 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 method for converting from radians to a Vec2 vector.
23
+ class Numeric
24
+ def radians_to_vec2
25
+ CP::Vec2.new(Math::cos(self), Math::sin(self))
26
+ end
27
+ end
28
+
29
+ # Layering of sprites
30
+ module ZOrder
31
+ Background, Stars, Player, UI = *0..3
32
+ end
33
+
34
+ # This game will have one Player in the form of a ship
35
+ class Player
36
+ attr_reader :shape
37
+
38
+ def initialize(window, shape)
39
+ @image = Gosu::Image.new(window, "media/Starfighter.bmp", false)
40
+ @shape = shape
41
+ @shape.body.p = CP::Vec2.new(0.0, 0.0) # position
42
+ @shape.body.v = CP::Vec2.new(0.0, 0.0) # velocity
43
+
44
+ # Keep in mind that down the screen is positive y, which means that PI/2 radians,
45
+ # which you might consider the top in the traditional Trig unit circle sense is actually
46
+ # the bottom; thus 3PI/2 is the top
47
+ @shape.body.a = (3*Math::PI/2.0) # angle in radians; faces towards top of screen
48
+ end
49
+
50
+ # Directly set the position of our Player
51
+ def warp(vect)
52
+ @shape.body.p = vect
53
+ end
54
+
55
+ # Apply negative Torque; Chipmunk will do the rest
56
+ # SUBSTEPS is used as a divisor to keep turning rate constant
57
+ # even if the number of steps per update are adjusted
58
+ def turn_left
59
+ @shape.body.t -= 400.0/SUBSTEPS
60
+ end
61
+
62
+ # Apply positive Torque; Chipmunk will do the rest
63
+ # SUBSTEPS is used as a divisor to keep turning rate constant
64
+ # even if the number of steps per update are adjusted
65
+ def turn_right
66
+ @shape.body.t += 400.0/SUBSTEPS
67
+ end
68
+
69
+ # Apply forward force; Chipmunk will do the rest
70
+ # SUBSTEPS is used as a divisor to keep acceleration rate constant
71
+ # even if the number of steps per update are adjusted
72
+ # Here we must convert the angle (facing) of the body into
73
+ # forward momentum by creating a vector in the direction of the facing
74
+ # and with a magnitude representing the force we want to apply
75
+ def accelerate
76
+ @shape.body.apply_force((@shape.body.a.radians_to_vec2 * (3000.0/SUBSTEPS)), CP::Vec2.new(0.0, 0.0))
77
+ end
78
+
79
+ # Apply even more forward force
80
+ # See accelerate for more details
81
+ def boost
82
+ @shape.body.apply_force((@shape.body.a.radians_to_vec2 * (3000.0)), CP::Vec2.new(0.0, 0.0))
83
+ end
84
+
85
+ # Apply reverse force
86
+ # See accelerate for more details
87
+ def reverse
88
+ @shape.body.apply_force(-(@shape.body.a.radians_to_vec2 * (1000.0/SUBSTEPS)), CP::Vec2.new(0.0, 0.0))
89
+ end
90
+
91
+ # Wrap to the other side of the screen when we fly off the edge
92
+ def validate_position
93
+ l_position = CP::Vec2.new(@shape.body.p.x % SCREEN_WIDTH, @shape.body.p.y % SCREEN_HEIGHT)
94
+ @shape.body.p = l_position
95
+ end
96
+
97
+ def draw
98
+ @image.draw_rot(@shape.body.p.x, @shape.body.p.y, ZOrder::Player, @shape.body.a.radians_to_gosu)
99
+ end
100
+ end
101
+
102
+ # See how simple our Star is?
103
+ # Of course... it just sits around and looks good...
104
+ class Star
105
+ attr_reader :shape
106
+
107
+ def initialize(animation, shape)
108
+ @animation = animation
109
+ @color = Gosu::Color.new(0xff000000)
110
+ @color.red = rand(255 - 40) + 40
111
+ @color.green = rand(255 - 40) + 40
112
+ @color.blue = rand(255 - 40) + 40
113
+ @shape = shape
114
+ @shape.body.p = CP::Vec2.new(rand * SCREEN_WIDTH, rand * SCREEN_HEIGHT) # position
115
+ @shape.body.v = CP::Vec2.new(0.0, 0.0) # velocity
116
+ @shape.body.a = (3*Math::PI/2.0) # angle in radians; faces towards top of screen
117
+ end
118
+
119
+ def draw
120
+ img = @animation[Gosu::milliseconds / 100 % @animation.size];
121
+ img.draw(@shape.body.p.x - img.width / 2.0, @shape.body.p.y - img.height / 2.0, ZOrder::Stars, 1, 1, @color, :add)
122
+ end
123
+ end
124
+
125
+ # The Gosu::Window is always the "environment" of our game
126
+ # It also provides the pulse of our game
127
+ class GameWindow < Gosu::Window
128
+ def initialize
129
+ super(SCREEN_WIDTH, SCREEN_HEIGHT, false, 16)
130
+ self.caption = "Gosu & Chipmunk Integration Demo"
131
+ @background_image = Gosu::Image.new(self, "media/Space.png", true)
132
+
133
+ # Put the beep here, as it is the environment now that determines collision
134
+ @beep = Gosu::Sample.new(self, "media/Beep.wav")
135
+
136
+ # Put the score here, as it is the environment that tracks this now
137
+ @score = 0
138
+ @font = Gosu::Font.new(self, Gosu::default_font_name, 20)
139
+
140
+ # Time increment over which to apply a physics "step" ("delta t")
141
+ @dt = (1.0/60.0)
142
+
143
+ # Create our Space and set its damping
144
+ # A damping of 0.8 causes the ship bleed off its force and torque over time
145
+ # This is not realistic behavior in a vacuum of space, but it gives the game
146
+ # the feel I'd like in this situation
147
+ @space = CP::Space.new
148
+ @space.damping = 0.8
149
+
150
+ # Create the Body for the Player
151
+ body = CP::Body.new(10.0, 150.0)
152
+
153
+ # In order to create a shape, we must first define it
154
+ # Chipmunk defines 3 types of Shapes: Segments, Circles and Polys
155
+ # We'll use s simple, 4 sided Poly for our Player (ship)
156
+ # You need to define the vectors so that the "top" of the Shape is towards 0 radians (the right)
157
+ 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)]
158
+ shape = CP::Shape::Poly.new(body, shape_array, CP::Vec2.new(0,0))
159
+
160
+ # The collision_type of a shape allows us to set up special collision behavior
161
+ # based on these types. The actual value for the collision_type is arbitrary
162
+ # and, as long as it is consistent, will work for us; of course, it helps to have it make sense
163
+ shape.collision_type = :ship
164
+
165
+ @space.add_body(body)
166
+ @space.add_shape(shape)
167
+
168
+ @player = Player.new(self, shape)
169
+ @player.warp(CP::Vec2.new(320, 240)) # move to the center of the window
170
+
171
+ @star_anim = Gosu::Image::load_tiles(self, "media/Star.png", 25, 25, false)
172
+ @stars = Array.new
173
+
174
+ # Here we define what is supposed to happen when a Player (ship) collides with a Star
175
+ # I create a @remove_shapes array because we cannot remove either Shapes or Bodies
176
+ # from Space within a collision closure, rather, we have to wait till the closure
177
+ # is through executing, then we can remove the Shapes and Bodies
178
+ # In this case, the Shapes and the Bodies they own are removed in the Gosu::Window.update phase
179
+ # by iterating over the @remove_shapes array
180
+ # Also note that both Shapes involved in the collision are passed into the closure
181
+ # in the same order that their collision_types are defined in the add_collision_func call
182
+ @remove_shapes = []
183
+ @space.add_collision_func(:ship, :star) do |ship_shape, star_shape|
184
+ @score += 10
185
+ @beep.play
186
+ @remove_shapes << star_shape
187
+ end
188
+
189
+ # Here we tell Space that we don't want one star bumping into another
190
+ # The reason we need to do this is because when the Player hits a Star,
191
+ # the Star will travel until it is removed in the update cycle below
192
+ # which means it may collide and therefore push other Stars
193
+ # To see the effect, remove this line and play the game, every once in a while
194
+ # you'll see a Star moving
195
+ @space.add_collision_func(:star, :star, &nil)
196
+ end
197
+
198
+ def update
199
+ # Step the physics environment SUBSTEPS times each update
200
+ SUBSTEPS.times do
201
+ # This iterator makes an assumption of one Shape per Star making it safe to remove
202
+ # each Shape's Body as it comes up
203
+ # If our Stars had multiple Shapes, as would be required if we were to meticulously
204
+ # define their true boundaries, we couldn't do this as we would remove the Body
205
+ # multiple times
206
+ # We would probably solve this by creating a separate @remove_bodies array to remove the Bodies
207
+ # of the Stars that were gathered by the Player
208
+ @remove_shapes.each do |shape|
209
+ @stars.delete_if { |star| star.shape == shape }
210
+ @space.remove_body(shape.body)
211
+ @space.remove_shape(shape)
212
+ end
213
+ @remove_shapes.clear # clear out the shapes for next pass
214
+
215
+ # When a force or torque is set on a Body, it is cumulative
216
+ # This means that the force you applied last SUBSTEP will compound with the
217
+ # force applied this SUBSTEP; which is probably not the behavior you want
218
+ # We reset the forces on the Player each SUBSTEP for this reason
219
+ @player.shape.body.reset_forces
220
+
221
+ # Wrap around the screen to the other side
222
+ @player.validate_position
223
+
224
+ # Check keyboard
225
+ if button_down? Gosu::KbLeft
226
+ @player.turn_left
227
+ end
228
+ if button_down? Gosu::KbRight
229
+ @player.turn_right
230
+ end
231
+
232
+ if button_down? Gosu::KbUp
233
+ if ( (button_down? Gosu::KbRightShift) || (button_down? Gosu::KbLeftShift) )
234
+ @player.boost
235
+ else
236
+ @player.accelerate
237
+ end
238
+ elsif button_down? Gosu::KbDown
239
+ @player.reverse
240
+ end
241
+
242
+ # Perform the step over @dt period of time
243
+ # For best performance @dt should remain consistent for the game
244
+ @space.step(@dt)
245
+ end
246
+
247
+ # Each update (not SUBSTEP) we see if we need to add more Stars
248
+ if rand(100) < 4 and @stars.size < 25 then
249
+ body = CP::Body.new(0.0001, 0.0001)
250
+ shape = CP::Shape::Circle.new(body, 25/2, CP::Vec2.new(0.0, 0.0))
251
+ shape.collision_type = :star
252
+
253
+ @space.add_body(body)
254
+ @space.add_shape(shape)
255
+
256
+ @stars.push(Star.new(@star_anim, shape))
257
+ end
258
+ end
259
+
260
+ def draw
261
+ @background_image.draw(0, 0, ZOrder::Background)
262
+ @player.draw
263
+ @stars.each { |star| star.draw }
264
+ @font.draw("Score: #{@score}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffffff00)
265
+ end
266
+
267
+ def button_down(id)
268
+ if id == Gosu::KbEscape
269
+ close
270
+ end
271
+ end
272
+ end
273
+
274
+ window = GameWindow.new
275
+ window.show
@@ -0,0 +1,223 @@
1
+ # Basically, the tutorial game taken to a jump'n'run perspective.
2
+
3
+ # Shows how to
4
+ # * implement jumping/gravity
5
+ # * implement scrolling using Window#translate
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
+ require 'rubygems'
29
+ require 'gosu'
30
+ include Gosu
31
+
32
+ module Tiles
33
+ Grass = 0
34
+ Earth = 1
35
+ end
36
+
37
+ class CollectibleGem
38
+ attr_reader :x, :y
39
+
40
+ def initialize(image, x, y)
41
+ @image = image
42
+ @x, @y = x, y
43
+ end
44
+
45
+ def draw
46
+ # Draw, slowly rotating
47
+ @image.draw_rot(@x, @y, 0, 25 * Math.sin(milliseconds / 133.7))
48
+ end
49
+ end
50
+
51
+ # Player class.
52
+ class CptnRuby
53
+ attr_reader :x, :y
54
+
55
+ def initialize(window, x, y)
56
+ @x, @y = x, y
57
+ @dir = :left
58
+ @vy = 0 # Vertical velocity
59
+ @map = window.map
60
+ # Load all animation frames
61
+ @standing, @walk1, @walk2, @jump =
62
+ *Image.load_tiles(window, "media/CptnRuby.png", 50, 50, false)
63
+ # This always points to the frame that is currently drawn.
64
+ # This is set in update, and used in draw.
65
+ @cur_image = @standing
66
+ end
67
+
68
+ def draw
69
+ # Flip vertically when facing to the left.
70
+ if @dir == :left then
71
+ offs_x = -25
72
+ factor = 1.0
73
+ else
74
+ offs_x = 25
75
+ factor = -1.0
76
+ end
77
+ @cur_image.draw(@x + offs_x, @y - 49, 0, factor, 1.0)
78
+ end
79
+
80
+ # Could the object be placed at x + offs_x/y + offs_y without being stuck?
81
+ def would_fit(offs_x, offs_y)
82
+ # Check at the center/top and center/bottom for map collisions
83
+ not @map.solid?(@x + offs_x, @y + offs_y) and
84
+ not @map.solid?(@x + offs_x, @y + offs_y - 45)
85
+ end
86
+
87
+ def update(move_x)
88
+ # Select image depending on action
89
+ if (move_x == 0)
90
+ @cur_image = @standing
91
+ else
92
+ @cur_image = (milliseconds / 175 % 2 == 0) ? @walk1 : @walk2
93
+ end
94
+ if (@vy < 0)
95
+ @cur_image = @jump
96
+ end
97
+
98
+ # Directional walking, horizontal movement
99
+ if move_x > 0 then
100
+ @dir = :right
101
+ move_x.times { if would_fit(1, 0) then @x += 1 end }
102
+ end
103
+ if move_x < 0 then
104
+ @dir = :left
105
+ (-move_x).times { if would_fit(-1, 0) then @x -= 1 end }
106
+ end
107
+
108
+ # Acceleration/gravity
109
+ # By adding 1 each frame, and (ideally) adding vy to y, the player's
110
+ # jumping curve will be the parabole we want it to be.
111
+ @vy += 1
112
+ # Vertical movement
113
+ if @vy > 0 then
114
+ @vy.times { if would_fit(0, 1) then @y += 1 else @vy = 0 end }
115
+ end
116
+ if @vy < 0 then
117
+ (-@vy).times { if would_fit(0, -1) then @y -= 1 else @vy = 0 end }
118
+ end
119
+ end
120
+
121
+ def try_to_jump
122
+ if @map.solid?(@x, @y + 1) then
123
+ @vy = -20
124
+ end
125
+ end
126
+
127
+ def collect_gems(gems)
128
+ # Same as in the tutorial game.
129
+ gems.reject! do |c|
130
+ (c.x - @x).abs < 50 and (c.y - @y).abs < 50
131
+ end
132
+ end
133
+ end
134
+
135
+ # Map class holds and draws tiles and gems.
136
+ class Map
137
+ attr_reader :width, :height, :gems
138
+
139
+ def initialize(window, filename)
140
+ # Load 60x60 tiles, 5px overlap in all four directions.
141
+ @tileset = Image.load_tiles(window, "media/CptnRuby Tileset.png", 60, 60, true)
142
+
143
+ gem_img = Image.new(window, "media/CptnRuby Gem.png", false)
144
+ @gems = []
145
+
146
+ lines = File.readlines(filename).map { |line| line.chomp }
147
+ @height = lines.size
148
+ @width = lines[0].size
149
+ @tiles = Array.new(@width) do |x|
150
+ Array.new(@height) do |y|
151
+ case lines[y][x, 1]
152
+ when '"'
153
+ Tiles::Grass
154
+ when '#'
155
+ Tiles::Earth
156
+ when 'x'
157
+ @gems.push(CollectibleGem.new(gem_img, x * 50 + 25, y * 50 + 25))
158
+ nil
159
+ else
160
+ nil
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ def draw
167
+ # Very primitive drawing function:
168
+ # Draws all the tiles, some off-screen, some on-screen.
169
+ @height.times do |y|
170
+ @width.times do |x|
171
+ tile = @tiles[x][y]
172
+ if tile
173
+ # Draw the tile with an offset (tile images have some overlap)
174
+ # Scrolling is implemented here just as in the game objects.
175
+ @tileset[tile].draw(x * 50 - 5, y * 50 - 5, 0)
176
+ end
177
+ end
178
+ end
179
+ @gems.each { |c| c.draw }
180
+ end
181
+
182
+ # Solid at a given pixel position?
183
+ def solid?(x, y)
184
+ y < 0 || @tiles[x / 50][y / 50]
185
+ end
186
+ end
187
+
188
+ class Game < Window
189
+ attr_reader :map
190
+
191
+ def initialize
192
+ super(640, 480, false)
193
+ self.caption = "Cptn. Ruby"
194
+ @sky = Image.new(self, "media/Space.png", true)
195
+ @map = Map.new(self, "media/CptnRuby Map.txt")
196
+ @cptn = CptnRuby.new(self, 400, 100)
197
+ # The scrolling position is stored as top left corner of the screen.
198
+ @camera_x = @camera_y = 0
199
+ end
200
+ def update
201
+ move_x = 0
202
+ move_x -= 5 if button_down? KbLeft
203
+ move_x += 5 if button_down? KbRight
204
+ @cptn.update(move_x)
205
+ @cptn.collect_gems(@map.gems)
206
+ # Scrolling follows player
207
+ @camera_x = [[@cptn.x - 320, 0].max, @map.width * 50 - 640].min
208
+ @camera_y = [[@cptn.y - 240, 0].max, @map.height * 50 - 480].min
209
+ end
210
+ def draw
211
+ @sky.draw 0, 0, 0
212
+ translate(-@camera_x, -@camera_y) do
213
+ @map.draw
214
+ @cptn.draw
215
+ end
216
+ end
217
+ def button_down(id)
218
+ if id == KbUp then @cptn.try_to_jump end
219
+ if id == KbEscape then close end
220
+ end
221
+ end
222
+
223
+ Game.new.show