class MiniGL::Map
This class provides easy control of a tile map, i.e., a map consisting of a grid of equally sized tiles. It also provides viewport control, through its camera property and methods. When working with GameObject
, Sprite
and Effect
inside a Map
, you should provide the map instance as parameter to the draw
method of these; when using Gosu’s Image
, you’ll need to manually subtract the coordinates of the camera from the position of the image, like this: img.draw(x - map.cam.x, y - map.cam.y, z)
Attributes
A Rectangle
representing the region of the map that is currently visible.
A Vector
where x is the horizontal tile count and y the vertical count.
A Vector
where x is the tile width and y is the tile height.
Public Class Methods
Creates a new map.
Parameters:
- t_w
-
The width of the tiles.
- t_h
-
The height of the tiles.
- t_x_count
-
The horizontal count of tiles in the map.
- t_y_count
-
The vertical count of tiles in the map.
- scr_w
-
Width of the viewport for the map.
- scr_h
-
Height of the viewport for the map.
- isometric
-
Whether to use a isometric map. By default, an ortogonal map is used.
- limit_cam
-
Whether the camera should respect the bounds of the map (i.e., when given coordinates that would imply regions outside the map to appear in the screen, the camera would move to the nearest position where only the map shows up in the screen).
# File lib/minigl/map.rb, line 42 def initialize(t_w, t_h, t_x_count, t_y_count, scr_w = 800, scr_h = 600, isometric = false, limit_cam = true) @tile_size = Vector.new t_w, t_h @size = Vector.new t_x_count, t_y_count @cam = Rectangle.new 0, 0, scr_w, scr_h @limit_cam = limit_cam @isometric = isometric if isometric initialize_isometric elsif limit_cam @max_x = t_x_count * t_w - scr_w @max_y = t_y_count * t_h - scr_h end set_camera 0, 0 end
Public Instance Methods
Iterates through the currently visible tiles, providing the horizontal tile index, the vertical tile index, the x-coordinate (in pixels) and the y-coordinate (in pixels), of each tile, in that order, to a given block of code.
Example:
map.foreach do |i, j, x, y| draw_tile tiles[i][j], x, y end
# File lib/minigl/map.rb, line 146 def foreach for j in @min_vis_y..@max_vis_y for i in @min_vis_x..@max_vis_x pos = get_screen_pos i, j yield i, j, pos.x, pos.y end end end
Returns a Vector
with the total size of the map, in pixels (x for the width and y for the height).
# File lib/minigl/map.rb, line 59 def get_absolute_size return Vector.new(@tile_size.x * @size.x, @tile_size.y * @size.y) unless @isometric avg = (@size.x + @size.y) * 0.5 Vector.new (avg * @tile_size.x).to_i, (avg * @tile_size.y).to_i end
Returns a Vector
with the coordinates of the center of the map.
# File lib/minigl/map.rb, line 66 def get_center abs_size = get_absolute_size Vector.new(abs_size.x * 0.5, abs_size.y * 0.5) end
Returns the tile in the map that corresponds to the given position in the screen, as a Vector
, where x is the horizontal index and y the vertical index.
Parameters:
- scr_x
-
The x-coordinate in the screen.
- scr_y
-
The y-coordinate in the screen.
# File lib/minigl/map.rb, line 92 def get_map_pos(scr_x, scr_y) return Vector.new((scr_x + @cam.x) / @tile_size.x, (scr_y + @cam.y) / @tile_size.y) unless @isometric # Gets the position transformed to isometric coordinates v = get_isometric_position scr_x, scr_y # divides by the square size to find the position in the matrix Vector.new((v.x * @inverse_square_size).to_i, (v.y * @inverse_square_size).to_i) end
Returns the position in the screen corresponding to the given tile indices.
Parameters:
- map_x
-
The index of the tile in the horizontal direction. It must be in the interval
0..t_x_count
. - map_y
-
The index of the tile in the vertical direction. It must be in the interval
0..t_y_count
.
# File lib/minigl/map.rb, line 79 def get_screen_pos(map_x, map_y) return Vector.new(map_x * @tile_size.x - @cam.x, map_y * @tile_size.y - @cam.y) unless @isometric Vector.new ((map_x - map_y - 1) * @tile_size.x * 0.5) - @cam.x + @x_offset, ((map_x + map_y) * @tile_size.y * 0.5) - @cam.y end
Verifies whether a tile is inside the map.
Parameters:
- v
-
A
Vector
representing the tile, with x as the horizontal index and y as the vertical index.
# File lib/minigl/map.rb, line 107 def is_in_map(v) v.x >= 0 && v.y >= 0 && v.x < @size.x && v.y < @size.y end
Moves the viewport by the given amount of pixels.
Parameters:
- x
-
The amount of pixels to move horizontally. Negative values will cause the camera to move to the left.
- y
-
The amount of pixels to move vertically. Negative values will cause the camera to move up.
# File lib/minigl/map.rb, line 130 def move_camera(x, y) @cam.x += x @cam.y += y set_bounds end
Sets the top left corner of the viewport to the given position of the map. Note that this is not the position in the screen.
Parameters:
- cam_x
-
The x-coordinate inside the map, in pixels (not a tile index).
- cam_y
-
The y-coordinate inside the map, in pixels (not a tile index).
# File lib/minigl/map.rb, line 117 def set_camera(cam_x, cam_y) @cam.x = cam_x @cam.y = cam_y set_bounds end
Private Instance Methods
# File lib/minigl/map.rb, line 239 def get_isometric_position(scr_x, scr_y) # Gets the position relative to the center of the map center = get_center position = Vector.new scr_x + @cam.x - center.x, scr_y + @cam.y - center.y # Multiplies by tile_ratio to get square tiles position.y *= @tile_ratio # Moves the center of the map accordingly center.y *= @tile_ratio # Rotates the position -45 degrees position.rotate! MINUS_PI_DIV_4 # Returns the reference to the center of the map position += center # Returns to the corner of the screen position.x -= @isometric_offset_x; position.y -= @isometric_offset_y position end
# File lib/minigl/map.rb, line 223 def initialize_isometric @x_offset = (@size.y * 0.5 * @tile_size.x).round @tile_ratio = @tile_size.x.to_f / @tile_size.y square_size = @tile_size.x * SQRT_2_DIV_2 @inverse_square_size = 1 / square_size @iso_abs_size = Vector.new(square_size * @size.x, square_size * @size.y) a = (@size.x + @size.y) * 0.5 * @tile_size.x @isometric_offset_x = (a - square_size * @size.x) * 0.5 @isometric_offset_y = (a - square_size * @size.y) * 0.5 if @limit_cam actual_cam_h = @cam.h * @tile_ratio @max_offset = actual_cam_h < @cam.w ? actual_cam_h : @cam.w @max_offset *= SQRT_2_DIV_2 end end
# File lib/minigl/map.rb, line 157 def set_bounds if @limit_cam if @isometric v1 = get_isometric_position(0, 0) v2 = get_isometric_position(@cam.w - 1, 0) v3 = get_isometric_position(@cam.w - 1, @cam.h - 1) v4 = get_isometric_position(0, @cam.h - 1) if v1.x < -@max_offset offset = -(v1.x + @max_offset) @cam.x += offset * SQRT_2_DIV_2 @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio v1.x = -@max_offset end if v2.y < -@max_offset offset = -(v2.y + @max_offset) @cam.x -= offset * SQRT_2_DIV_2 @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio v2.y = -@max_offset end if v3.x > @iso_abs_size.x + @max_offset offset = v3.x - @iso_abs_size.x - @max_offset @cam.x -= offset * SQRT_2_DIV_2 @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio v3.x = @iso_abs_size.x + @max_offset end if v4.y > @iso_abs_size.y + @max_offset offset = v4.y - @iso_abs_size.y - @max_offset @cam.x += offset * SQRT_2_DIV_2 @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio v4.y = @iso_abs_size.y + @max_offset end else @cam.x = @max_x if @cam.x > @max_x @cam.x = 0 if @cam.x < 0 @cam.y = @max_y if @cam.y > @max_y @cam.y = 0 if @cam.y < 0 end end @cam.x = @cam.x.round @cam.y = @cam.y.round if @isometric @min_vis_x = get_map_pos(0, 0).x @min_vis_y = get_map_pos(@cam.w - 1, 0).y @max_vis_x = get_map_pos(@cam.w - 1, @cam.h - 1).x @max_vis_y = get_map_pos(0, @cam.h - 1).y else @min_vis_x = @cam.x / @tile_size.x @min_vis_y = @cam.y / @tile_size.y @max_vis_x = (@cam.x + @cam.w - 1) / @tile_size.x @max_vis_y = (@cam.y + @cam.h - 1) / @tile_size.y end if @min_vis_y < 0; @min_vis_y = 0 elsif @min_vis_y > @size.y - 1; @min_vis_y = @size.y - 1; end if @max_vis_y < 0; @max_vis_y = 0 elsif @max_vis_y > @size.y - 1; @max_vis_y = @size.y - 1; end if @min_vis_x < 0; @min_vis_x = 0 elsif @min_vis_x > @size.x - 1; @min_vis_x = @size.x - 1; end if @max_vis_x < 0; @max_vis_x = 0 elsif @max_vis_x > @size.x - 1; @max_vis_x = @size.x - 1; end end