gosu 0.8.6-x64-mingw32

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.
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