gosu-examples 1.0.4 → 1.0.7
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.
- checksums.yaml +5 -5
- data/README.md +1 -1
- data/bin/gosu-examples +20 -22
- data/examples/chipmunk_and_rmagick.rb +25 -28
- data/examples/chipmunk_integration.rb +45 -48
- data/examples/cptn_ruby.rb +23 -26
- data/examples/opengl_integration.rb +61 -62
- data/examples/rmagick_integration.rb +87 -88
- data/examples/text_input.rb +157 -0
- data/examples/tutorial.rb +23 -26
- data/examples/welcome.rb +20 -23
- data/lib/gosu-examples/example.rb +34 -13
- data/lib/gosu-examples/sidebar.rb +16 -16
- metadata +10 -36
- data/examples/media/BrokenPNG.png +0 -0
- data/examples/media/Cursor.png +0 -0
- data/examples/media/JingleBells.mp3 +0 -0
- data/examples/media/JingleBells.ogg +0 -0
- data/examples/media/Loop.wav +0 -0
- data/examples/media/Sample.wav +0 -0
- data/examples/media/SquareTexture.png +0 -0
- data/examples/media/Wallpaper.png +0 -0
- data/examples/media/WallpaperXXL.png +0 -0
- data/examples/media/WhiteAlpha.png +0 -0
- data/examples/media/audio_formats/aiff_32bit_float.aiff +0 -0
- data/examples/media/audio_formats/au_16bit_pcm.au +0 -0
- data/examples/media/audio_formats/caf_be_16bit_44khz.caf +0 -0
- data/examples/media/audio_formats/caf_le_16bit_44khz.caf +0 -0
- data/examples/media/audio_formats/caf_le_8bit_44khz.caf +0 -0
- data/examples/media/audio_formats/general_midi.mid +0 -0
- data/examples/media/audio_formats/impulse_tracker.it +0 -0
- data/examples/media/audio_formats/mp3_128k_stereo.mp3 +0 -0
- data/examples/media/audio_formats/mp3_avg_96kbit_jointstereo.mp3 +0 -0
- data/examples/media/audio_formats/ogg_vorbis.ogg +0 -0
- data/examples/media/audio_formats/wav_16bit_pcm.wav +0 -0
- data/examples/media/audio_formats/wav_32bit_pcm.wav +0 -0
- data/examples/media/audio_formats/wav_4bit_ms_adpcm.wav +0 -0
- data/examples/media/image_formats/test.jpg +0 -0
- data/examples/media/image_formats/test.psd +0 -0
- data/examples/media/vera.ttf +0 -0
@@ -1,13 +1,12 @@
|
|
1
|
-
# Encoding: UTF-8
|
2
|
-
|
3
1
|
# The tutorial game over a landscape rendered with OpenGL.
|
4
2
|
# Basically shows how arbitrary OpenGL calls can be put into
|
5
3
|
# the block given to Window#gl, and that Gosu Images can be
|
6
4
|
# used as textures using the gl_tex_info call.
|
7
5
|
|
8
|
-
require
|
9
|
-
require
|
10
|
-
|
6
|
+
require "gosu"
|
7
|
+
require "opengl"
|
8
|
+
|
9
|
+
OpenGL.load_lib
|
11
10
|
|
12
11
|
WIDTH, HEIGHT = 640, 480
|
13
12
|
|
@@ -25,11 +24,11 @@ class GLBackground
|
|
25
24
|
SCROLLS_PER_STEP = 50
|
26
25
|
|
27
26
|
def initialize
|
28
|
-
@image = Gosu::Image.new("media/earth.png", :
|
27
|
+
@image = Gosu::Image.new("media/earth.png", tileable: true)
|
29
28
|
@scrolls = 0
|
30
29
|
@height_map = Array.new(POINTS_Y) { Array.new(POINTS_X) { rand } }
|
31
30
|
end
|
32
|
-
|
31
|
+
|
33
32
|
def scroll
|
34
33
|
@scrolls += 1
|
35
34
|
if @scrolls == SCROLLS_PER_STEP
|
@@ -38,22 +37,22 @@ class GLBackground
|
|
38
37
|
@height_map.push Array.new(POINTS_X) { rand }
|
39
38
|
end
|
40
39
|
end
|
41
|
-
|
40
|
+
|
42
41
|
def draw(z)
|
43
42
|
# gl will execute the given block in a clean OpenGL environment, then reset
|
44
43
|
# everything so Gosu's rendering can take place again.
|
45
44
|
Gosu.gl(z) { exec_gl }
|
46
45
|
end
|
47
|
-
|
46
|
+
|
48
47
|
private
|
49
|
-
|
50
|
-
include
|
51
|
-
|
48
|
+
|
49
|
+
include OpenGL
|
50
|
+
|
52
51
|
def exec_gl
|
53
52
|
glClearColor(0.0, 0.2, 0.5, 1.0)
|
54
53
|
glClearDepth(0)
|
55
54
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
56
|
-
|
55
|
+
|
57
56
|
# Get the name of the OpenGL texture the Image resides on, and the
|
58
57
|
# u/v coordinates of the rect it occupies.
|
59
58
|
# gl_tex_info can return nil if the image was too large to fit onto
|
@@ -62,7 +61,7 @@ class GLBackground
|
|
62
61
|
return unless info
|
63
62
|
|
64
63
|
# Pretty straightforward OpenGL code.
|
65
|
-
|
64
|
+
|
66
65
|
glDepthFunc(GL_GEQUAL)
|
67
66
|
glEnable(GL_DEPTH_TEST)
|
68
67
|
glEnable(GL_BLEND)
|
@@ -73,35 +72,35 @@ class GLBackground
|
|
73
72
|
|
74
73
|
glMatrixMode(GL_MODELVIEW)
|
75
74
|
glLoadIdentity
|
76
|
-
|
77
|
-
|
75
|
+
glTranslatef(0, 0, -4)
|
76
|
+
|
78
77
|
glEnable(GL_TEXTURE_2D)
|
79
78
|
glBindTexture(GL_TEXTURE_2D, info.tex_name)
|
80
|
-
|
79
|
+
|
81
80
|
offs_y = 1.0 * @scrolls / SCROLLS_PER_STEP
|
82
|
-
|
81
|
+
|
83
82
|
0.upto(POINTS_Y - 2) do |y|
|
84
83
|
0.upto(POINTS_X - 2) do |x|
|
85
84
|
glBegin(GL_TRIANGLE_STRIP)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
85
|
+
z = @height_map[y][x]
|
86
|
+
glColor4d(1, 1, 1, z)
|
87
|
+
glTexCoord2d(info.left, info.top)
|
88
|
+
glVertex3d(-0.5 + (x - 0.0) / (POINTS_X - 1), -0.5 + (y - offs_y - 0.0) / (POINTS_Y - 2), z)
|
89
|
+
|
90
|
+
z = @height_map[y + 1][x]
|
91
|
+
glColor4d(1, 1, 1, z)
|
92
|
+
glTexCoord2d(info.left, info.bottom)
|
93
|
+
glVertex3d(-0.5 + (x - 0.0) / (POINTS_X - 1), -0.5 + (y - offs_y + 1.0) / (POINTS_Y - 2), z)
|
94
|
+
|
95
|
+
z = @height_map[y][x + 1]
|
96
|
+
glColor4d(1, 1, 1, z)
|
97
|
+
glTexCoord2d(info.right, info.top)
|
98
|
+
glVertex3d(-0.5 + (x + 1.0) / (POINTS_X - 1), -0.5 + (y - offs_y - 0.0) / (POINTS_Y - 2), z)
|
99
|
+
|
100
|
+
z = @height_map[y + 1][x + 1]
|
101
|
+
glColor4d(1, 1, 1, z)
|
102
|
+
glTexCoord2d(info.right, info.bottom)
|
103
|
+
glVertex3d(-0.5 + (x + 1.0) / (POINTS_X - 1), -0.5 + (y - offs_y + 1.0) / (POINTS_Y - 2), z)
|
105
104
|
glEnd
|
106
105
|
end
|
107
106
|
end
|
@@ -111,7 +110,7 @@ end
|
|
111
110
|
# Roughly adapted from the tutorial game. Always faces north.
|
112
111
|
class Player
|
113
112
|
Speed = 7
|
114
|
-
|
113
|
+
|
115
114
|
attr_reader :score
|
116
115
|
|
117
116
|
def initialize(x, y)
|
@@ -124,23 +123,23 @@ class Player
|
|
124
123
|
def move_left
|
125
124
|
@x = [@x - Speed, 0].max
|
126
125
|
end
|
127
|
-
|
126
|
+
|
128
127
|
def move_right
|
129
128
|
@x = [@x + Speed, WIDTH].min
|
130
129
|
end
|
131
|
-
|
130
|
+
|
132
131
|
def accelerate
|
133
132
|
@y = [@y - Speed, 50].max
|
134
133
|
end
|
135
|
-
|
134
|
+
|
136
135
|
def brake
|
137
136
|
@y = [@y + Speed, HEIGHT].min
|
138
137
|
end
|
139
|
-
|
138
|
+
|
140
139
|
def draw
|
141
140
|
@image.draw(@x - @image.width / 2, @y - @image.height / 2, ZOrder::Player)
|
142
141
|
end
|
143
|
-
|
142
|
+
|
144
143
|
def collect_stars(stars)
|
145
144
|
stars.reject! do |star|
|
146
145
|
if Gosu.distance(@x, @y, star.x, star.y) < 35
|
@@ -158,7 +157,7 @@ end
|
|
158
157
|
# for extra rotation coolness!
|
159
158
|
class Star
|
160
159
|
attr_reader :x, :y
|
161
|
-
|
160
|
+
|
162
161
|
def initialize(animation)
|
163
162
|
@animation = animation
|
164
163
|
@color = Gosu::Color.new(0xff_000000)
|
@@ -169,11 +168,11 @@ class Star
|
|
169
168
|
@y = 0
|
170
169
|
end
|
171
170
|
|
172
|
-
def draw
|
173
|
-
img = @animation[Gosu.milliseconds / 100 % @animation.size]
|
171
|
+
def draw
|
172
|
+
img = @animation[Gosu.milliseconds / 100 % @animation.size]
|
174
173
|
img.draw_rot(@x, @y, ZOrder::Stars, @y, 0.5, 0.5, 1, 1, @color, :add)
|
175
174
|
end
|
176
|
-
|
175
|
+
|
177
176
|
def update
|
178
177
|
# Move towards bottom of screen
|
179
178
|
@y += 3
|
@@ -185,40 +184,40 @@ end
|
|
185
184
|
class OpenGLIntegration < (Example rescue Gosu::Window)
|
186
185
|
def initialize
|
187
186
|
super WIDTH, HEIGHT
|
188
|
-
|
187
|
+
|
189
188
|
self.caption = "OpenGL Integration"
|
190
|
-
|
189
|
+
|
191
190
|
@gl_background = GLBackground.new
|
192
|
-
|
191
|
+
|
193
192
|
@player = Player.new(400, 500)
|
194
|
-
|
193
|
+
|
195
194
|
@star_anim = Gosu::Image::load_tiles("media/star.png", 25, 25)
|
196
195
|
@stars = Array.new
|
197
|
-
|
196
|
+
|
198
197
|
@font = Gosu::Font.new(20)
|
199
198
|
end
|
200
|
-
|
199
|
+
|
201
200
|
def update
|
202
|
-
@player.move_left
|
201
|
+
@player.move_left if Gosu.button_down? Gosu::KB_LEFT or Gosu.button_down? Gosu::GP_LEFT
|
203
202
|
@player.move_right if Gosu.button_down? Gosu::KB_RIGHT or Gosu.button_down? Gosu::GP_RIGHT
|
204
|
-
@player.accelerate if Gosu.button_down? Gosu::KB_UP
|
205
|
-
@player.brake
|
206
|
-
|
203
|
+
@player.accelerate if Gosu.button_down? Gosu::KB_UP or Gosu.button_down? Gosu::GP_UP
|
204
|
+
@player.brake if Gosu.button_down? Gosu::KB_DOWN or Gosu.button_down? Gosu::GP_DOWN
|
205
|
+
|
207
206
|
@player.collect_stars(@stars)
|
208
|
-
|
207
|
+
|
209
208
|
@stars.reject! { |star| !star.update }
|
210
|
-
|
209
|
+
|
211
210
|
@gl_background.scroll
|
212
|
-
|
211
|
+
|
213
212
|
@stars.push(Star.new(@star_anim)) if rand(20) == 0
|
214
213
|
end
|
215
214
|
|
216
215
|
def draw
|
217
216
|
@player.draw
|
218
217
|
@stars.each { |star| star.draw }
|
219
|
-
@font.
|
218
|
+
@font.draw_text("Score: #{@player.score}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xff_ffff00)
|
220
219
|
@gl_background.draw(ZOrder::Background)
|
221
220
|
end
|
222
221
|
end
|
223
222
|
|
224
|
-
OpenGLIntegration.new.show if __FILE__ == $0
|
223
|
+
OpenGLIntegration.new.show if __FILE__ == $0
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# A (too) simple Gorilla-style shooter for two players.
|
1
|
+
# A simple Gorilla-style shooter for two players.
|
4
2
|
# Shows how Gosu and RMagick can be used together to generate a map, implement
|
5
3
|
# a dynamic landscape and generally look great.
|
6
4
|
# Also shows a very minimal, yet effective way of designing a game's object system.
|
@@ -13,13 +11,12 @@
|
|
13
11
|
# * The look of dead soldiers is, err, by accident. Soldier.png needs to be
|
14
12
|
# designed in a less obfuscated way :)
|
15
13
|
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require 'rmagick'
|
14
|
+
require "gosu"
|
15
|
+
require "rmagick"
|
19
16
|
|
20
17
|
WIDTH, HEIGHT = 640, 480
|
21
18
|
|
22
|
-
NULL_PIXEL = Magick::Pixel.from_color(
|
19
|
+
NULL_PIXEL = Magick::Pixel.from_color("none")
|
23
20
|
|
24
21
|
# The class for this game's map.
|
25
22
|
# Design:
|
@@ -35,16 +32,16 @@ class Map
|
|
35
32
|
# Loading SVG files isn't possible with Gosu, so say wow!
|
36
33
|
# (Seems to take a while though)
|
37
34
|
sky = Magick::Image.read("media/landscape.svg").first
|
38
|
-
@sky = Gosu::Image.new(sky, :
|
39
|
-
|
35
|
+
@sky = Gosu::Image.new(sky, tileable: true)
|
36
|
+
|
40
37
|
# Create the map an stores the RMagick image in @image
|
41
38
|
create_rmagick_map
|
42
|
-
|
39
|
+
|
43
40
|
# Copy the RMagick Image to a Gosu Image (still unchanged)
|
44
|
-
@gosu_image = Gosu::Image.new(@image, :
|
41
|
+
@gosu_image = Gosu::Image.new(@image, tileable: true)
|
45
42
|
end
|
46
|
-
|
47
|
-
def solid?
|
43
|
+
|
44
|
+
def solid?(x, y)
|
48
45
|
# Map is open at the top.
|
49
46
|
return false if y < 0
|
50
47
|
# Map is closed on all other sides.
|
@@ -52,7 +49,7 @@ class Map
|
|
52
49
|
# Inside of the map, determine solidity from the map image.
|
53
50
|
@image.pixel_color(x, y) != NULL_PIXEL
|
54
51
|
end
|
55
|
-
|
52
|
+
|
56
53
|
def draw
|
57
54
|
# Sky background.
|
58
55
|
@sky.draw 0, 0, 0
|
@@ -64,43 +61,43 @@ class Map
|
|
64
61
|
RADIUS = 25
|
65
62
|
# Radius of a crater, Shadow included.
|
66
63
|
SH_RADIUS = 45
|
67
|
-
|
64
|
+
|
68
65
|
# Create the crater image (basically a circle shape that is used to erase
|
69
66
|
# parts of the map) and the crater shadow image.
|
70
67
|
CRATER_IMAGE = begin
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
68
|
+
crater = Magick::Image.new(2 * RADIUS, 2 * RADIUS) { self.background_color = "none" }
|
69
|
+
gc = Magick::Draw.new
|
70
|
+
gc.fill("black").circle(RADIUS, RADIUS, RADIUS, 0)
|
71
|
+
gc.draw crater
|
72
|
+
crater
|
73
|
+
end
|
77
74
|
CRATER_SHADOW = CRATER_IMAGE.shadow(0, 0, (SH_RADIUS - RADIUS) / 2, 1)
|
78
|
-
|
79
|
-
def blast
|
75
|
+
|
76
|
+
def blast(x, y)
|
80
77
|
# Draw the shadow (twice for more intensity), then erase a circle from the map.
|
81
78
|
@image.composite! CRATER_SHADOW, x - SH_RADIUS, y - SH_RADIUS, Magick::AtopCompositeOp
|
82
79
|
@image.composite! CRATER_SHADOW, x - SH_RADIUS, y - SH_RADIUS, Magick::AtopCompositeOp
|
83
|
-
@image.composite! CRATER_IMAGE,
|
84
|
-
|
80
|
+
@image.composite! CRATER_IMAGE, x - RADIUS, y - RADIUS, Magick::DstOutCompositeOp
|
81
|
+
|
85
82
|
# Isolate the affected portion of the RMagick image.
|
86
83
|
dirty_portion = @image.crop(x - SH_RADIUS, y - SH_RADIUS, SH_RADIUS * 2, SH_RADIUS * 2)
|
87
84
|
# Overwrite this part of the Gosu image. If the crater begins outside of the map, still
|
88
85
|
# just update the inner part.
|
89
86
|
@gosu_image.insert dirty_portion, [x - SH_RADIUS, 0].max, [y - SH_RADIUS, 0].max
|
90
87
|
end
|
91
|
-
|
88
|
+
|
92
89
|
private
|
93
|
-
|
90
|
+
|
94
91
|
def create_rmagick_map
|
95
92
|
# This is the one large RMagick image that represents the map.
|
96
|
-
@image = Magick::Image.new(WIDTH, HEIGHT) { self.background_color =
|
97
|
-
|
93
|
+
@image = Magick::Image.new(WIDTH, HEIGHT) { self.background_color = "none" }
|
94
|
+
|
98
95
|
# Set up a Draw object that fills with an earth texture.
|
99
|
-
earth = Magick::Image.read(
|
96
|
+
earth = Magick::Image.read("media/earth.png").first.resize(1.5)
|
100
97
|
gc = Magick::Draw.new
|
101
|
-
gc.pattern(
|
102
|
-
gc.fill(
|
103
|
-
gc.stroke(
|
98
|
+
gc.pattern("earth", 0, 0, earth.columns, earth.rows) { gc.composite(0, 0, 0, 0, earth) }
|
99
|
+
gc.fill("earth")
|
100
|
+
gc.stroke("#603000").stroke_width(1.5)
|
104
101
|
# Draw a smooth bezier island onto the map!
|
105
102
|
polypoints = [0, HEIGHT]
|
106
103
|
0.upto(8) do |x|
|
@@ -109,20 +106,20 @@ class Map
|
|
109
106
|
polypoints += [WIDTH, HEIGHT]
|
110
107
|
gc.bezier(*polypoints)
|
111
108
|
gc.draw(@image)
|
112
|
-
|
109
|
+
|
113
110
|
# Create a bright-dark gradient fill, an image from it and change the map's
|
114
111
|
# brightness with it.
|
115
|
-
fill = Magick::GradientFill.new(0, HEIGHT * 0.4, WIDTH, HEIGHT * 0.4,
|
112
|
+
fill = Magick::GradientFill.new(0, HEIGHT * 0.4, WIDTH, HEIGHT * 0.4, "#fff", "#666")
|
116
113
|
gradient = Magick::Image.new(WIDTH, HEIGHT, fill)
|
117
114
|
gradient = @image.composite(gradient, 0, 0, Magick::InCompositeOp)
|
118
115
|
@image.composite!(gradient, 0, 0, Magick::MultiplyCompositeOp)
|
119
116
|
|
120
117
|
# Finally, place the star in the middle of the map, just onto the ground.
|
121
|
-
star = Magick::Image.read(
|
118
|
+
star = Magick::Image.read("media/large_star.png").first
|
122
119
|
star_y = 0
|
123
120
|
star_y += 20 until solid?(WIDTH / 2, star_y)
|
124
121
|
@image.composite!(star, (WIDTH - star.columns) / 2, star_y - star.rows * 0.85,
|
125
|
-
|
122
|
+
Magick::DstOverCompositeOp)
|
126
123
|
end
|
127
124
|
end
|
128
125
|
|
@@ -132,29 +129,29 @@ end
|
|
132
129
|
# draw: Draws the object (obviously)
|
133
130
|
# update: Moves the object etc., returns false if the object is to be deleted
|
134
131
|
# hit_by?(missile): Returns true if an object is hit by the missile, causing
|
135
|
-
# it to explode on this object.
|
132
|
+
# it to explode on this object.
|
136
133
|
|
137
134
|
class Player
|
138
135
|
# Magic numbers considered harmful! This is the height of the
|
139
136
|
# player as used for collision detection.
|
140
137
|
HEIGHT = 14
|
141
|
-
|
138
|
+
|
142
139
|
attr_reader :x, :y, :dead
|
143
|
-
|
140
|
+
|
144
141
|
def initialize(window, x, y, color)
|
145
142
|
# Only load the images once for all instances of this class.
|
146
143
|
@@images ||= Gosu::Image.load_tiles("media/soldier.png", 40, 50)
|
147
|
-
|
144
|
+
|
148
145
|
@window, @x, @y, @color = window, x, y, color
|
149
146
|
@vy = 0
|
150
|
-
|
147
|
+
|
151
148
|
# -1: left, +1: right
|
152
149
|
@dir = -1
|
153
150
|
|
154
151
|
# Aiming angle.
|
155
152
|
@angle = 90
|
156
153
|
end
|
157
|
-
|
154
|
+
|
158
155
|
def draw
|
159
156
|
if dead
|
160
157
|
# Poor, broken soldier.
|
@@ -169,7 +166,7 @@ class Player
|
|
169
166
|
# No: Stand around (boring).
|
170
167
|
frame = 0
|
171
168
|
end
|
172
|
-
|
169
|
+
|
173
170
|
# Draw feet, then chest.
|
174
171
|
@@images[frame].draw(x - 10 * @dir, y - 20, 0, @dir * 0.5, 0.5, @color)
|
175
172
|
angle = @angle
|
@@ -177,14 +174,14 @@ class Player
|
|
177
174
|
@@images[2].draw_rot(x, y - 5, 0, angle, 1, 0.5, 0.5, @dir * 0.5, @color)
|
178
175
|
end
|
179
176
|
end
|
180
|
-
|
177
|
+
|
181
178
|
def update
|
182
179
|
# First, assume that no walking happened this frame.
|
183
180
|
@show_walk_anim = false
|
184
|
-
|
181
|
+
|
185
182
|
# Gravity.
|
186
183
|
@vy += 1
|
187
|
-
|
184
|
+
|
188
185
|
if @vy > 1
|
189
186
|
# Move upwards until hitting something.
|
190
187
|
@vy.times do
|
@@ -206,19 +203,19 @@ class Player
|
|
206
203
|
end
|
207
204
|
end
|
208
205
|
end
|
209
|
-
|
206
|
+
|
210
207
|
# Soldiers are never deleted (they may die, but that is a different thing).
|
211
208
|
true
|
212
209
|
end
|
213
|
-
|
210
|
+
|
214
211
|
def aim_up
|
215
212
|
@angle -= 2 unless @angle < 10
|
216
213
|
end
|
217
|
-
|
214
|
+
|
218
215
|
def aim_down
|
219
216
|
@angle += 2 unless @angle > 170
|
220
217
|
end
|
221
|
-
|
218
|
+
|
222
219
|
def try_walk(dir)
|
223
220
|
@show_walk_anim = true
|
224
221
|
@dir = dir
|
@@ -226,27 +223,27 @@ class Player
|
|
226
223
|
2.times { @y -= 1 unless @window.map.solid?(x, y - HEIGHT - 1) }
|
227
224
|
# Now move into the desired direction.
|
228
225
|
@x += dir unless @window.map.solid?(x + dir, y) or
|
229
|
-
@window.map.solid?(x + dir, y - HEIGHT)
|
226
|
+
@window.map.solid?(x + dir, y - HEIGHT)
|
230
227
|
# To make up for unnecessary movement upwards, sink downward again.
|
231
228
|
2.times { @y += 1 unless @window.map.solid?(x, y + 1) }
|
232
229
|
end
|
233
|
-
|
230
|
+
|
234
231
|
def try_jump
|
235
232
|
@vy = -12 if @window.map.solid?(x, y + 1)
|
236
233
|
end
|
237
|
-
|
234
|
+
|
238
235
|
def shoot
|
239
236
|
@window.objects << Missile.new(@window, x + 10 * @dir, y - 10, @angle * @dir)
|
240
237
|
end
|
241
|
-
|
242
|
-
def hit_by?
|
238
|
+
|
239
|
+
def hit_by?(missile)
|
243
240
|
if Gosu.distance(missile.x, missile.y, x, y) < 30
|
244
241
|
# Was hit :(
|
245
242
|
@dead = true
|
246
243
|
return true
|
247
244
|
else
|
248
245
|
return false
|
249
|
-
end
|
246
|
+
end
|
250
247
|
end
|
251
248
|
end
|
252
249
|
|
@@ -257,14 +254,14 @@ class Missile
|
|
257
254
|
|
258
255
|
# All missile instances use the same sound.
|
259
256
|
EXPLOSION = Gosu::Sample.new("media/explosion.wav")
|
260
|
-
|
257
|
+
|
261
258
|
def initialize(window, x, y, angle)
|
262
259
|
# Horizontal/vertical velocity.
|
263
260
|
@vx, @vy = Gosu.offset_x(angle, 20).to_i, Gosu.offset_y(angle, 20).to_i
|
264
|
-
|
261
|
+
|
265
262
|
@window, @x, @y = window, x + @vx, y + @vy
|
266
263
|
end
|
267
|
-
|
264
|
+
|
268
265
|
def update
|
269
266
|
# Movement, gravity
|
270
267
|
@x += @vx
|
@@ -282,12 +279,12 @@ class Missile
|
|
282
279
|
return true
|
283
280
|
end
|
284
281
|
end
|
285
|
-
|
282
|
+
|
286
283
|
def draw
|
287
284
|
# Just draw a small rectangle.
|
288
|
-
Gosu.draw_rect x-2, y-2, 4, 4, 0xff_800000
|
285
|
+
Gosu.draw_rect x - 2, y - 2, 4, 4, 0xff_800000
|
289
286
|
end
|
290
|
-
|
287
|
+
|
291
288
|
def hit_by?(missile)
|
292
289
|
# Missiles can't be hit by other missiles!
|
293
290
|
false
|
@@ -299,25 +296,25 @@ end
|
|
299
296
|
class Particle
|
300
297
|
def initialize(window, x, y)
|
301
298
|
# All Particle instances use the same image
|
302
|
-
@@image ||= Gosu::Image.new(
|
303
|
-
|
299
|
+
@@image ||= Gosu::Image.new("media/smoke.png")
|
300
|
+
|
304
301
|
@x, @y = x, y
|
305
302
|
@color = Gosu::Color.new(255, 255, 255, 255)
|
306
303
|
end
|
307
|
-
|
304
|
+
|
308
305
|
def update
|
309
306
|
@y -= 5
|
310
307
|
@x = @x - 1 + rand(3)
|
311
308
|
@color.alpha -= 5
|
312
|
-
|
309
|
+
|
313
310
|
# Remove if faded completely.
|
314
311
|
@color.alpha > 0
|
315
312
|
end
|
316
|
-
|
313
|
+
|
317
314
|
def draw
|
318
315
|
@@image.draw(@x - 25, @y - 25, 0, 1, 1, @color)
|
319
316
|
end
|
320
|
-
|
317
|
+
|
321
318
|
def hit_by?(missile)
|
322
319
|
# Smoke can't be hit!
|
323
320
|
false
|
@@ -329,10 +326,10 @@ end
|
|
329
326
|
|
330
327
|
class RMagickIntegration < (Example rescue Gosu::Window)
|
331
328
|
attr_reader :map, :objects
|
332
|
-
|
329
|
+
|
333
330
|
def initialize
|
334
331
|
super WIDTH, HEIGHT
|
335
|
-
|
332
|
+
|
336
333
|
self.caption = "RMagick Integration Demo"
|
337
334
|
|
338
335
|
# Texts to display in the appropriate situations.
|
@@ -340,65 +337,67 @@ class RMagickIntegration < (Example rescue Gosu::Window)
|
|
340
337
|
@player_won_messages = []
|
341
338
|
2.times do |plr|
|
342
339
|
@player_instructions << Gosu::Image.from_text(
|
343
|
-
"It is the #{
|
340
|
+
"It is the #{plr == 0 ? "green" : "red"} toy soldier's turn.\n" +
|
344
341
|
"(Arrow keys to walk and aim, Return to jump, Space to shoot)",
|
345
|
-
30, :
|
342
|
+
30, width: width, align: :center,
|
343
|
+
)
|
346
344
|
@player_won_messages << Gosu::Image.from_text(
|
347
|
-
"The #{
|
348
|
-
30, :
|
345
|
+
"The #{plr == 0 ? "green" : "red"} toy soldier has won!",
|
346
|
+
30, width: width, align: :center,
|
347
|
+
)
|
349
348
|
end
|
350
349
|
|
351
350
|
# Create everything!
|
352
351
|
@map = Map.new
|
353
352
|
@players = [Player.new(self, 100, 40, 0xff_308000), Player.new(self, WIDTH - 100, 40, 0xff_803000)]
|
354
353
|
@objects = @players.dup
|
355
|
-
|
354
|
+
|
356
355
|
# Let any player start.
|
357
356
|
@current_player = rand(2)
|
358
357
|
# Currently not waiting for a missile to hit something.
|
359
358
|
@waiting = false
|
360
359
|
end
|
361
|
-
|
360
|
+
|
362
361
|
def draw
|
363
362
|
# Draw the main game.
|
364
363
|
@map.draw
|
365
364
|
@objects.each { |o| o.draw }
|
366
|
-
|
365
|
+
|
367
366
|
# If any text should be displayed, draw it - and add a nice black border around it
|
368
367
|
# by drawing it four times, with a little offset in each direction.
|
369
|
-
|
368
|
+
|
370
369
|
cur_text = @player_instructions[@current_player] if not @waiting
|
371
370
|
cur_text = @player_won_messages[1 - @current_player] if @players[@current_player].dead
|
372
|
-
|
371
|
+
|
373
372
|
if cur_text
|
374
373
|
x, y = 0, 30
|
375
374
|
cur_text.draw(x - 1, y, 0, 1, 1, 0xff_000000)
|
376
375
|
cur_text.draw(x + 1, y, 0, 1, 1, 0xff_000000)
|
377
376
|
cur_text.draw(x, y - 1, 0, 1, 1, 0xff_000000)
|
378
377
|
cur_text.draw(x, y + 1, 0, 1, 1, 0xff_000000)
|
379
|
-
cur_text.draw(x,
|
378
|
+
cur_text.draw(x, y, 0, 1, 1, 0xff_ffffff)
|
380
379
|
end
|
381
380
|
end
|
382
|
-
|
381
|
+
|
383
382
|
def update
|
384
383
|
# if waiting for the next player's turn, continue to do so until the missile has
|
385
384
|
# hit something.
|
386
385
|
@waiting &&= !@objects.grep(Missile).empty?
|
387
|
-
|
388
|
-
# Remove all objects whose update method returns false.
|
386
|
+
|
387
|
+
# Remove all objects whose update method returns false.
|
389
388
|
@objects.reject! { |o| o.update == false }
|
390
389
|
|
391
390
|
# If it's a player's turn, forward controls.
|
392
391
|
if not @waiting and not @players[@current_player].dead
|
393
392
|
player = @players[@current_player]
|
394
|
-
player.aim_up
|
395
|
-
player.aim_down
|
393
|
+
player.aim_up if Gosu.button_down? Gosu::KB_UP
|
394
|
+
player.aim_down if Gosu.button_down? Gosu::KB_DOWN
|
396
395
|
player.try_walk(-1) if Gosu.button_down? Gosu::KB_LEFT
|
397
396
|
player.try_walk(+1) if Gosu.button_down? Gosu::KB_RIGHT
|
398
|
-
player.try_jump
|
397
|
+
player.try_jump if Gosu.button_down? Gosu::KB_RETURN
|
399
398
|
end
|
400
399
|
end
|
401
|
-
|
400
|
+
|
402
401
|
def button_down(id)
|
403
402
|
if id == Gosu::KB_SPACE and not @waiting and not @players[@current_player].dead
|
404
403
|
# Shoot! This is handled in button_down because holding space shouldn't auto-fire.
|