gosu-examples 1.0.4 → 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|