minigl 2.2.3 → 2.2.4

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.
data/lib/minigl/map.rb CHANGED
@@ -1,256 +1,261 @@
1
- require_relative 'global'
2
-
3
- module MiniGL
4
- # This class provides easy control of a tile map, i.e., a map consisting of
5
- # a grid of equally sized tiles. It also provides viewport control, through
6
- # its camera property and methods.
7
- class Map
8
- SQRT_2_DIV_2 = Math.sqrt(2) / 2 # :nodoc:
9
- MINUS_PI_DIV_4 = -Math::PI / 4 # :nodoc:
10
-
11
- # A Vector where x is the tile width and y is the tile height.
12
- attr_reader :tile_size
13
-
14
- # A Vector where x is the horizontal tile count and y the vertical count.
15
- attr_reader :size
16
-
17
- # A Rectangle representing the region of the map that is currently
18
- # visible.
19
- attr_reader :cam
20
-
21
- # Creates a new map.
22
- #
23
- # Parameters:
24
- # [t_w] The width of the tiles.
25
- # [t_h] The height of the tiles.
26
- # [t_x_count] The horizontal count of tiles in the map.
27
- # [t_y_count] The vertical count of tiles in the map.
28
- # [scr_w] Width of the viewport for the map.
29
- # [scr_h] Height of the viewport for the map.
30
- # [isometric] Whether to use a isometric map. By default, an ortogonal map
31
- # is used.
32
- # [limit_cam] Whether the camera should respect the bounds of the map
33
- # (i.e., when given coordinates that would imply regions
34
- # outside the map to appear in the screen, the camera would
35
- # move to the nearest position where only the map shows up
36
- # in the screen).
37
- def initialize(t_w, t_h, t_x_count, t_y_count, scr_w = 800, scr_h = 600, isometric = false, limit_cam = true)
38
- @tile_size = Vector.new t_w, t_h
39
- @size = Vector.new t_x_count, t_y_count
40
- @cam = Rectangle.new 0, 0, scr_w, scr_h
41
- @limit_cam = limit_cam
42
- @isometric = isometric
43
- if isometric
44
- initialize_isometric
45
- elsif limit_cam
46
- @max_x = t_x_count * t_w - scr_w
47
- @max_y = t_y_count * t_h - scr_h
48
- end
49
- set_camera 0, 0
50
- end
51
-
52
- # Returns a Vector with the total size of the map, in pixels (x for the
53
- # width and y for the height).
54
- def get_absolute_size
55
- return Vector.new(@tile_size.x * @size.x, @tile_size.y * @size.y) unless @isometric
56
- avg = (@size.x + @size.y) * 0.5
57
- Vector.new (avg * @tile_size.x).to_i, (avg * @tile_size.y).to_i
58
- end
59
-
60
- # Returns a Vector with the coordinates of the center of the map.
61
- def get_center
62
- abs_size = get_absolute_size
63
- Vector.new(abs_size.x * 0.5, abs_size.y * 0.5)
64
- end
65
-
66
- # Returns the position in the screen corresponding to the given tile
67
- # indices.
68
- #
69
- # Parameters:
70
- # [map_x] The index of the tile in the horizontal direction. It must be in
71
- # the interval <code>0..t_x_count</code>.
72
- # [map_y] The index of the tile in the vertical direction. It must be in
73
- # the interval <code>0..t_y_count</code>.
74
- def get_screen_pos(map_x, map_y)
75
- return Vector.new(map_x * @tile_size.x - @cam.x, map_y * @tile_size.y - @cam.y) unless @isometric
76
- Vector.new ((map_x - map_y - 1) * @tile_size.x * 0.5) - @cam.x + @x_offset,
77
- ((map_x + map_y) * @tile_size.y * 0.5) - @cam.y
78
- end
79
-
80
- # Returns the tile in the map that corresponds to the given position in
81
- # the screen, as a Vector, where x is the horizontal index and y the
82
- # vertical index.
83
- #
84
- # Parameters:
85
- # [scr_x] The x-coordinate in the screen.
86
- # [scr_y] The y-coordinate in the screen.
87
- def get_map_pos(scr_x, scr_y)
88
- return Vector.new((scr_x + @cam.x) / @tile_size.x, (scr_y + @cam.y) / @tile_size.y) unless @isometric
89
-
90
- # Obtém a posição transformada para as coordenadas isométricas
91
- v = get_isometric_position scr_x, scr_y
92
-
93
- # Depois divide pelo tamanho do quadrado para achar a posição da matriz
94
- Vector.new((v.x * @inverse_square_size).to_i, (v.y * @inverse_square_size).to_i)
95
- end
96
-
97
- # Verifies whether a tile is inside the map.
98
- #
99
- # Parameters:
100
- # [v] A Vector representing the tile, with x as the horizontal index and
101
- # y as the vertical index.
102
- def is_in_map(v)
103
- v.x >= 0 && v.y >= 0 && v.x < @size.x && v.y < @size.y
104
- end
105
-
106
- # Sets the top left corner of the viewport to the given position of the
107
- # map. Note that this is not the position in the screen.
108
- #
109
- # Parameters:
110
- # [cam_x] The x-coordinate inside the map, in pixels (not a tile index).
111
- # [cam_y] The y-coordinate inside the map, in pixels (not a tile index).
112
- def set_camera(cam_x, cam_y)
113
- @cam.x = cam_x
114
- @cam.y = cam_y
115
- set_bounds
116
- end
117
-
118
- # Moves the viewport by the given amount of pixels.
119
- #
120
- # Parameters:
121
- # [x] The amount of pixels to move horizontally. Negative values will
122
- # cause the camera to move to the left.
123
- # [y] The amount of pixels to move vertically. Negative values will cause
124
- # the camera to move up.
125
- def move_camera(x, y)
126
- @cam.x += x
127
- @cam.y += y
128
- set_bounds
129
- end
130
-
131
- # Iterates through the currently visible tiles, providing the horizontal
132
- # tile index, the vertical tile index, the x-coordinate (in pixels) and
133
- # the y-coordinate (in pixels), of each tile, in that order, to a given
134
- # block of code.
135
- #
136
- # Example:
137
- #
138
- # map.foreach do |i, j, x, y|
139
- # draw_tile tiles[i][j], x, y
140
- # end
141
- def foreach
142
- for j in @min_vis_y..@max_vis_y
143
- for i in @min_vis_x..@max_vis_x
144
- pos = get_screen_pos i, j
145
- yield i, j, pos.x, pos.y
146
- end
147
- end
148
- end
149
-
150
- private
151
-
152
- def set_bounds
153
- if @limit_cam
154
- if @isometric
155
- v1 = get_isometric_position(0, 0)
156
- v2 = get_isometric_position(@cam.w - 1, 0)
157
- v3 = get_isometric_position(@cam.w - 1, @cam.h - 1)
158
- v4 = get_isometric_position(0, @cam.h - 1)
159
- if v1.x < -@max_offset
160
- offset = -(v1.x + @max_offset)
161
- @cam.x += offset * SQRT_2_DIV_2
162
- @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio
163
- v1.x = -@max_offset
164
- end
165
- if v2.y < -@max_offset
166
- offset = -(v2.y + @max_offset)
167
- @cam.x -= offset * SQRT_2_DIV_2
168
- @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio
169
- v2.y = -@max_offset
170
- end
171
- if v3.x > @iso_abs_size.x + @max_offset
172
- offset = v3.x - @iso_abs_size.x - @max_offset
173
- @cam.x -= offset * SQRT_2_DIV_2
174
- @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio
175
- v3.x = @iso_abs_size.x + @max_offset
176
- end
177
- if v4.y > @iso_abs_size.y + @max_offset
178
- offset = v4.y - @iso_abs_size.y - @max_offset
179
- @cam.x += offset * SQRT_2_DIV_2
180
- @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio
181
- v4.y = @iso_abs_size.y + @max_offset
182
- end
183
- else
184
- @cam.x = @max_x if @cam.x > @max_x
185
- @cam.x = 0 if @cam.x < 0
186
- @cam.y = @max_y if @cam.y > @max_y
187
- @cam.y = 0 if @cam.y < 0
188
- end
189
- end
190
-
191
- @cam.x = @cam.x.round
192
- @cam.y = @cam.y.round
193
- if @isometric
194
- @min_vis_x = get_map_pos(0, 0).x
195
- @min_vis_y = get_map_pos(@cam.w - 1, 0).y
196
- @max_vis_x = get_map_pos(@cam.w - 1, @cam.h - 1).x
197
- @max_vis_y = get_map_pos(0, @cam.h - 1).y
198
- else
199
- @min_vis_x = @cam.x / @tile_size.x
200
- @min_vis_y = @cam.y / @tile_size.y
201
- @max_vis_x = (@cam.x + @cam.w - 1) / @tile_size.x
202
- @max_vis_y = (@cam.y + @cam.h - 1) / @tile_size.y
203
- end
204
-
205
- if @min_vis_y < 0; @min_vis_y = 0
206
- elsif @min_vis_y > @size.y - 1; @min_vis_y = @size.y - 1; end
207
-
208
- if @max_vis_y < 0; @max_vis_y = 0
209
- elsif @max_vis_y > @size.y - 1; @max_vis_y = @size.y - 1; end
210
-
211
- if @min_vis_x < 0; @min_vis_x = 0
212
- elsif @min_vis_x > @size.x - 1; @min_vis_x = @size.x - 1; end
213
-
214
- if @max_vis_x < 0; @max_vis_x = 0
215
- elsif @max_vis_x > @size.x - 1; @max_vis_x = @size.x - 1; end
216
- end
217
-
218
- def initialize_isometric
219
- @x_offset = (@size.y * 0.5 * @tile_size.x).round
220
- @tile_ratio = @tile_size.x.to_f / @tile_size.y
221
- square_size = @tile_size.x * SQRT_2_DIV_2
222
- @inverse_square_size = 1 / square_size
223
- @iso_abs_size = Vector.new(square_size * @size.x, square_size * @size.y)
224
- a = (@size.x + @size.y) * 0.5 * @tile_size.x
225
- @isometric_offset_x = (a - square_size * @size.x) * 0.5
226
- @isometric_offset_y = (a - square_size * @size.y) * 0.5
227
- if @limit_cam
228
- actual_cam_h = @cam.h * @tile_ratio
229
- @max_offset = actual_cam_h < @cam.w ? actual_cam_h : @cam.w
230
- @max_offset *= SQRT_2_DIV_2
231
- end
232
- end
233
-
234
- def get_isometric_position(scr_x, scr_y)
235
- # Escreve a posição em relação a origem (no centro do mapa)
236
- center = get_center
237
- position = Vector.new scr_x + @cam.x - center.x, scr_y + @cam.y - center.y
238
-
239
- # Multiplica por tile_ratio para obter tiles quadrados
240
- position.y *= @tile_ratio
241
-
242
- # O centro do mapa também é deslocado
243
- center.y *= @tile_ratio
244
-
245
- # Rotaciona o vetor posição -45°
246
- position.rotate! MINUS_PI_DIV_4
247
-
248
- # Retorna a referência da posição para o canto da tela
249
- position += center
250
-
251
- # O mapa quadrado está centralizado no centro do losango, precisa retornar ao canto da tela
252
- position.x -= @isometric_offset_x; position.y -= @isometric_offset_y
253
- position
254
- end
255
- end
256
- end
1
+ require_relative 'global'
2
+
3
+ module MiniGL
4
+ # This class provides easy control of a tile map, i.e., a map consisting of
5
+ # a grid of equally sized tiles. It also provides viewport control, through
6
+ # its camera property and methods.
7
+ # When working with +GameObject+, +Sprite+ and +Effect+ inside a +Map+, you
8
+ # should provide the map instance as parameter to the +draw+ method of these;
9
+ # when using Gosu's +Image+, you'll need to manually subtract the coordinates
10
+ # of the camera from the position of the image, like this:
11
+ # <code>img.draw(x - map.cam.x, y - map.cam.y, z)</code>
12
+ class Map
13
+ SQRT_2_DIV_2 = Math.sqrt(2) / 2 # :nodoc:
14
+ MINUS_PI_DIV_4 = -Math::PI / 4 # :nodoc:
15
+
16
+ # A Vector where x is the tile width and y is the tile height.
17
+ attr_reader :tile_size
18
+
19
+ # A Vector where x is the horizontal tile count and y the vertical count.
20
+ attr_reader :size
21
+
22
+ # A Rectangle representing the region of the map that is currently
23
+ # visible.
24
+ attr_reader :cam
25
+
26
+ # Creates a new map.
27
+ #
28
+ # Parameters:
29
+ # [t_w] The width of the tiles.
30
+ # [t_h] The height of the tiles.
31
+ # [t_x_count] The horizontal count of tiles in the map.
32
+ # [t_y_count] The vertical count of tiles in the map.
33
+ # [scr_w] Width of the viewport for the map.
34
+ # [scr_h] Height of the viewport for the map.
35
+ # [isometric] Whether to use a isometric map. By default, an ortogonal map
36
+ # is used.
37
+ # [limit_cam] Whether the camera should respect the bounds of the map
38
+ # (i.e., when given coordinates that would imply regions
39
+ # outside the map to appear in the screen, the camera would
40
+ # move to the nearest position where only the map shows up
41
+ # in the screen).
42
+ def initialize(t_w, t_h, t_x_count, t_y_count, scr_w = 800, scr_h = 600, isometric = false, limit_cam = true)
43
+ @tile_size = Vector.new t_w, t_h
44
+ @size = Vector.new t_x_count, t_y_count
45
+ @cam = Rectangle.new 0, 0, scr_w, scr_h
46
+ @limit_cam = limit_cam
47
+ @isometric = isometric
48
+ if isometric
49
+ initialize_isometric
50
+ elsif limit_cam
51
+ @max_x = t_x_count * t_w - scr_w
52
+ @max_y = t_y_count * t_h - scr_h
53
+ end
54
+ set_camera 0, 0
55
+ end
56
+
57
+ # Returns a Vector with the total size of the map, in pixels (x for the
58
+ # width and y for the height).
59
+ def get_absolute_size
60
+ return Vector.new(@tile_size.x * @size.x, @tile_size.y * @size.y) unless @isometric
61
+ avg = (@size.x + @size.y) * 0.5
62
+ Vector.new (avg * @tile_size.x).to_i, (avg * @tile_size.y).to_i
63
+ end
64
+
65
+ # Returns a Vector with the coordinates of the center of the map.
66
+ def get_center
67
+ abs_size = get_absolute_size
68
+ Vector.new(abs_size.x * 0.5, abs_size.y * 0.5)
69
+ end
70
+
71
+ # Returns the position in the screen corresponding to the given tile
72
+ # indices.
73
+ #
74
+ # Parameters:
75
+ # [map_x] The index of the tile in the horizontal direction. It must be in
76
+ # the interval <code>0..t_x_count</code>.
77
+ # [map_y] The index of the tile in the vertical direction. It must be in
78
+ # the interval <code>0..t_y_count</code>.
79
+ def get_screen_pos(map_x, map_y)
80
+ return Vector.new(map_x * @tile_size.x - @cam.x, map_y * @tile_size.y - @cam.y) unless @isometric
81
+ Vector.new ((map_x - map_y - 1) * @tile_size.x * 0.5) - @cam.x + @x_offset,
82
+ ((map_x + map_y) * @tile_size.y * 0.5) - @cam.y
83
+ end
84
+
85
+ # Returns the tile in the map that corresponds to the given position in
86
+ # the screen, as a Vector, where x is the horizontal index and y the
87
+ # vertical index.
88
+ #
89
+ # Parameters:
90
+ # [scr_x] The x-coordinate in the screen.
91
+ # [scr_y] The y-coordinate in the screen.
92
+ def get_map_pos(scr_x, scr_y)
93
+ return Vector.new((scr_x + @cam.x) / @tile_size.x, (scr_y + @cam.y) / @tile_size.y) unless @isometric
94
+
95
+ # Gets the position transformed to isometric coordinates
96
+ v = get_isometric_position scr_x, scr_y
97
+
98
+ # divides by the square size to find the position in the matrix
99
+ Vector.new((v.x * @inverse_square_size).to_i, (v.y * @inverse_square_size).to_i)
100
+ end
101
+
102
+ # Verifies whether a tile is inside the map.
103
+ #
104
+ # Parameters:
105
+ # [v] A Vector representing the tile, with x as the horizontal index and
106
+ # y as the vertical index.
107
+ def is_in_map(v)
108
+ v.x >= 0 && v.y >= 0 && v.x < @size.x && v.y < @size.y
109
+ end
110
+
111
+ # Sets the top left corner of the viewport to the given position of the
112
+ # map. Note that this is not the position in the screen.
113
+ #
114
+ # Parameters:
115
+ # [cam_x] The x-coordinate inside the map, in pixels (not a tile index).
116
+ # [cam_y] The y-coordinate inside the map, in pixels (not a tile index).
117
+ def set_camera(cam_x, cam_y)
118
+ @cam.x = cam_x
119
+ @cam.y = cam_y
120
+ set_bounds
121
+ end
122
+
123
+ # Moves the viewport by the given amount of pixels.
124
+ #
125
+ # Parameters:
126
+ # [x] The amount of pixels to move horizontally. Negative values will
127
+ # cause the camera to move to the left.
128
+ # [y] The amount of pixels to move vertically. Negative values will cause
129
+ # the camera to move up.
130
+ def move_camera(x, y)
131
+ @cam.x += x
132
+ @cam.y += y
133
+ set_bounds
134
+ end
135
+
136
+ # Iterates through the currently visible tiles, providing the horizontal
137
+ # tile index, the vertical tile index, the x-coordinate (in pixels) and
138
+ # the y-coordinate (in pixels), of each tile, in that order, to a given
139
+ # block of code.
140
+ #
141
+ # Example:
142
+ #
143
+ # map.foreach do |i, j, x, y|
144
+ # draw_tile tiles[i][j], x, y
145
+ # end
146
+ def foreach
147
+ for j in @min_vis_y..@max_vis_y
148
+ for i in @min_vis_x..@max_vis_x
149
+ pos = get_screen_pos i, j
150
+ yield i, j, pos.x, pos.y
151
+ end
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def set_bounds
158
+ if @limit_cam
159
+ if @isometric
160
+ v1 = get_isometric_position(0, 0)
161
+ v2 = get_isometric_position(@cam.w - 1, 0)
162
+ v3 = get_isometric_position(@cam.w - 1, @cam.h - 1)
163
+ v4 = get_isometric_position(0, @cam.h - 1)
164
+ if v1.x < -@max_offset
165
+ offset = -(v1.x + @max_offset)
166
+ @cam.x += offset * SQRT_2_DIV_2
167
+ @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio
168
+ v1.x = -@max_offset
169
+ end
170
+ if v2.y < -@max_offset
171
+ offset = -(v2.y + @max_offset)
172
+ @cam.x -= offset * SQRT_2_DIV_2
173
+ @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio
174
+ v2.y = -@max_offset
175
+ end
176
+ if v3.x > @iso_abs_size.x + @max_offset
177
+ offset = v3.x - @iso_abs_size.x - @max_offset
178
+ @cam.x -= offset * SQRT_2_DIV_2
179
+ @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio
180
+ v3.x = @iso_abs_size.x + @max_offset
181
+ end
182
+ if v4.y > @iso_abs_size.y + @max_offset
183
+ offset = v4.y - @iso_abs_size.y - @max_offset
184
+ @cam.x += offset * SQRT_2_DIV_2
185
+ @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio
186
+ v4.y = @iso_abs_size.y + @max_offset
187
+ end
188
+ else
189
+ @cam.x = @max_x if @cam.x > @max_x
190
+ @cam.x = 0 if @cam.x < 0
191
+ @cam.y = @max_y if @cam.y > @max_y
192
+ @cam.y = 0 if @cam.y < 0
193
+ end
194
+ end
195
+
196
+ @cam.x = @cam.x.round
197
+ @cam.y = @cam.y.round
198
+ if @isometric
199
+ @min_vis_x = get_map_pos(0, 0).x
200
+ @min_vis_y = get_map_pos(@cam.w - 1, 0).y
201
+ @max_vis_x = get_map_pos(@cam.w - 1, @cam.h - 1).x
202
+ @max_vis_y = get_map_pos(0, @cam.h - 1).y
203
+ else
204
+ @min_vis_x = @cam.x / @tile_size.x
205
+ @min_vis_y = @cam.y / @tile_size.y
206
+ @max_vis_x = (@cam.x + @cam.w - 1) / @tile_size.x
207
+ @max_vis_y = (@cam.y + @cam.h - 1) / @tile_size.y
208
+ end
209
+
210
+ if @min_vis_y < 0; @min_vis_y = 0
211
+ elsif @min_vis_y > @size.y - 1; @min_vis_y = @size.y - 1; end
212
+
213
+ if @max_vis_y < 0; @max_vis_y = 0
214
+ elsif @max_vis_y > @size.y - 1; @max_vis_y = @size.y - 1; end
215
+
216
+ if @min_vis_x < 0; @min_vis_x = 0
217
+ elsif @min_vis_x > @size.x - 1; @min_vis_x = @size.x - 1; end
218
+
219
+ if @max_vis_x < 0; @max_vis_x = 0
220
+ elsif @max_vis_x > @size.x - 1; @max_vis_x = @size.x - 1; end
221
+ end
222
+
223
+ def initialize_isometric
224
+ @x_offset = (@size.y * 0.5 * @tile_size.x).round
225
+ @tile_ratio = @tile_size.x.to_f / @tile_size.y
226
+ square_size = @tile_size.x * SQRT_2_DIV_2
227
+ @inverse_square_size = 1 / square_size
228
+ @iso_abs_size = Vector.new(square_size * @size.x, square_size * @size.y)
229
+ a = (@size.x + @size.y) * 0.5 * @tile_size.x
230
+ @isometric_offset_x = (a - square_size * @size.x) * 0.5
231
+ @isometric_offset_y = (a - square_size * @size.y) * 0.5
232
+ if @limit_cam
233
+ actual_cam_h = @cam.h * @tile_ratio
234
+ @max_offset = actual_cam_h < @cam.w ? actual_cam_h : @cam.w
235
+ @max_offset *= SQRT_2_DIV_2
236
+ end
237
+ end
238
+
239
+ def get_isometric_position(scr_x, scr_y)
240
+ # Gets the position relative to the center of the map
241
+ center = get_center
242
+ position = Vector.new scr_x + @cam.x - center.x, scr_y + @cam.y - center.y
243
+
244
+ # Multiplies by tile_ratio to get square tiles
245
+ position.y *= @tile_ratio
246
+
247
+ # Moves the center of the map accordingly
248
+ center.y *= @tile_ratio
249
+
250
+ # Rotates the position -45 degrees
251
+ position.rotate! MINUS_PI_DIV_4
252
+
253
+ # Returns the reference to the center of the map
254
+ position += center
255
+
256
+ # Returns to the corner of the screen
257
+ position.x -= @isometric_offset_x; position.y -= @isometric_offset_y
258
+ position
259
+ end
260
+ end
261
+ end