How Determine Camera Limits using Tilemap

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By aaschmitz
:warning: Old Version Published before Godot 3 was released.

What is the best way to calculate camera limits (stopping the scrolling) using a TileMap?

The camera limits is set using something like:

set_limit(MARGIN_TOP, 0)
set_limit(MARGIN_LEFT, 0)    
set_limit(MARGIN_RIGHT, calculated_limit_x)
set_limit(MARGIN_BOTTOM, calculated_limit_y)

The idea is that the camera stop scrolling horizontally when arriving in the last right tile and stop scrolling vertically when reaching the last bottom tile in the used map.

Is possible to do this by setting limits (x, y) manually on each map (exporting vars via GDScript), but it would be interesting to automate the process in order to determine the limits automatically :wink:

:bust_in_silhouette: Reply From: Aaron

I came up with a camera script that limits the camera to the current level’s size, including handling of the camera’s zoom.

If the viewport is smaller than the level either horizontally or vertically, the viewport is kept inside the level. If the viewport is bigger than the level, the level is centred.

extends Camera2D

export(Vector2) var world_size setget _world_size_changed

func _world_size_changed(size):
    world_size = size
    _sync_bounds()

func _ready():
    _sync_bounds()
    get_viewport().connect("size_changed", self, "_sync_bounds")

func _sync_bounds():
    var viewport = get_viewport()
    if viewport != null:
        var zoom = get_zoom()
        var scale = Vector2(1 / zoom.x, 1 / zoom.y)

        var resolution = viewport.get_rect().size

        if (world_size.x * scale.x) > resolution.width:
            _set_big_margins(world_size.x, resolution.width, scale.x, MARGIN_LEFT, MARGIN_RIGHT)
        else:
            _set_small_margins(world_size.x, resolution.width, scale.x, MARGIN_LEFT, MARGIN_RIGHT)

        if (world_size.y * scale.y) > resolution.height:
            _set_big_margins(world_size.y, resolution.height, scale.y, MARGIN_TOP, MARGIN_BOTTOM)
        else:
            _set_small_margins(world_size.y, resolution.height, scale.y, MARGIN_TOP, MARGIN_BOTTOM)

func _set_big_margins(world_size, screen_size, scale, margin_neg, margin_pos):
    set_limit(margin_neg, 0)

    var diff = (world_size * scale) - screen_size
    diff /= scale
    set_limit(margin_pos, ceil(screen_size + diff))

func _set_small_margins(world_size, screen_size, scale, margin_neg, margin_pos):
    var margin = (screen_size - (world_size * scale)) / 2

    set_limit(margin_neg, floor(-margin / scale))
    set_limit(margin_pos, ceil((margin / scale) - world_size))

TileMap itself doesn’t have a way to get the bounds though, so you’ll have to calculate that yourself.

I just opened an issue about getting the extents of a TileMap: Method to assess the extents of a tilemap (level size) · Issue #4390 · godotengine/godot · GitHub

Akien | 2016-04-21 09:08

:bust_in_silhouette: Reply From: tripod

as of godot 3.0 you can use get_used_rect():

func _ready() -> void:
  var cam:Camera2D = $Player/Camera2D
  var limits:Rect2 = $Terrain.get_used_rect()
  var size:Vector2 = $Terrain.get_cell_size()
  cam.limit_left = limits.position.x * size.x
  cam.limit_top = limits.position.y * size.y
  cam.limit_right = limits.end.x * size.x
  cam.limit_bottom = limits.end.y * size.y