# 【摄像机适应地图】

可以让摄像机动态适应地图大小,并且可进行移动摄像机。

以下是摄像机挂载的代码。

extends Camera2D

@export var tilemap: TileMap

# 加速度
var acceleration = 200

# 速度
var velocity:Vector2 = Vector2.ZERO

# 停止衰减
var release_falloff = 10.0

# 最大速度
var max_speed = 30.0

func _ready():
    set_anchor_mode(Camera2D.ANCHOR_MODE_FIXED_TOP_LEFT)
    var zoom_vector = get_camera_zoom_to_tilemap()
    set_zoom(zoom_vector)
    apply_camera_limits()

func _process(_delta):
    var input_vector = Input.get_vector("camera_move_left","camera_move_right","camera_move_up","camera_move_down")
    calculate_velocity(input_vector)
    update_global_position()
    pass

func apply_camera_limits():
    var tilemap_info = get_tilemap_info()
    var level_side = Vector2i(tilemap_info.tile_size * tilemap_info.size)
    set_limit(SIDE_LEFT,0)
    set_limit(SIDE_TOP,0)
    set_limit(SIDE_RIGHT,level_side.x)
    set_limit(SIDE_BOTTOM,level_side.y)

func update_global_position():
    var delta = get_process_delta_time()
    global_position += lerp(
        velocity,
        Vector2.ZERO,
        pow(2,-32 * delta)
    )

    # 处理摄像机位置超出地图的情况
    var zoomed_viewport_size = get_viewport_to_zoom_scale()
    var left_limit = get_limit(SIDE_LEFT)
    var right_limit = get_limit(SIDE_RIGHT) - zoomed_viewport_size.x
    var top_limit = get_limit(SIDE_TOP)
    var bottom_limit = get_limit(SIDE_BOTTOM) - zoomed_viewport_size.y
    global_position.x = clamp(global_position.x,left_limit,right_limit)
    global_position.y = clamp(global_position.y,top_limit,bottom_limit)

func get_viewport_to_zoom_scale():
    var zoom_vector = get_zoom()
    var zoomed_viewport_size = Vector2i(
        get_viewport().size[0] / zoom_vector.x,
        get_viewport().size[1] / zoom_vector.y
    )
    return zoomed_viewport_size

func calculate_velocity(direction):
    var delta = get_process_delta_time()
    velocity += direction * acceleration * delta
    if direction.x == 0:
        # velocity.x = lerp(velocity.x,0.0,release_falloff * delta) 线性
        velocity.x = lerp(0.0,velocity.x,pow(2,-release_falloff * delta)) # 曲线
    if direction.y == 0:
        # velocity.y = lerp(velocity.y,0.0,release_falloff * delta) 线性
        velocity.y = lerp(0.0,velocity.y,pow(2,-release_falloff * delta)) # 曲线
    velocity.x = clamp(
        velocity.x,
        -max_speed,
        max_speed
    )
    velocity.y = clamp(
        velocity.y,
        -max_speed,
        max_speed
    )

func get_camera_zoom_to_tilemap():
    var viewport_size = get_viewport().size # [x , y]
    var tilemap_info = get_tilemap_info()
    var level_size = Vector2i(tilemap_info.tile_size * tilemap_info.size)
    var viewport_aspect = viewport_size[0] / viewport_size[1]
    var level_aspect = float(level_size.x) / level_size.y
    var new_zoom = 1.0
    if level_aspect > viewport_aspect:
        new_zoom =  viewport_size[1] / level_size.y
    else:
        new_zoom = viewport_size[0] / level_size.x
        
    # 可以限制缩放
    new_zoom = clamp(new_zoom,2,3.5)
    return Vector2(new_zoom,new_zoom)

func get_tilemap_info():
    var tile_size = tilemap.get_tileset().tile_size
    var tilemap_rect = tilemap.get_used_rect()
    var tilemap_size = Vector2i(
        tilemap_rect.end.x - tilemap_rect.position.x,
        tilemap_rect.end.y - tilemap_rect.position.y
    )
    return {"size":tilemap_size, "tile_size":tile_size}