I’m working on a top down 2d game where the player can move the camera using the WASD keys. I want to limit this camera movement to the limits of the camera.
As you can hopefully see in this GIF, the camera doesn’t show areas of the map that the tilemap does not cover. So this works just fine. The problem is that the position of the camera can exceed these limits. This means when you keep moving the camera out of limits it can take much longer to move the camera in the opposite direction. In the GIF, keep pressing S to move the camera down but for a while nothing happens.
How can I fix this behaviour? Is there some way to access the camera rect in world coordinates?
class_name GameCamera
extends Camera2D
@export var lerp_smoothing := 10.0
@export var camera_speed := 25
@export var world_tile_map: TileMap:
get():
if not world_tile_map.is_node_ready():
await world_tile_map.ready
return world_tile_map
func _ready() -> void:
var world_rect := world_tile_map.get_used_rect()
limit_left = world_rect.position.x * world_tile_map.tile_set.tile_size.x
limit_top = world_rect.position.y * world_tile_map.tile_set.tile_size.y
limit_right = world_rect.end.x * world_tile_map.tile_set.tile_size.x
limit_bottom = world_rect.end.y * world_tile_map.tile_set.tile_size.y
func _process(delta: float) -> void:
var movement_vector := Input.get_vector("move_left", "move_right", "move_up", "move_down")
if not movement_vector == Vector2.ZERO:
var target_position := global_position + movement_vector * camera_speed
global_position = global_position.lerp(target_position, 1.0 - exp(-delta * lerp_smoothing))
The problem is you’re moving the global position away from your limit position (remember, the camera global position is different from the target positon, what control what you actually see on this camera), so to solve that your need to check if the next position still inside the tilemap rect
extends Camera2D
@export var lerp_smoothing := 10.0
@export var camera_speed := 25
var world_rect: Rect2i
@export var world_tile_map: TileMap:
get():
if not world_tile_map.is_node_ready():
await world_tile_map.ready
return world_tile_map
func _ready() -> void:
world_rect = world_tile_map.get_used_rect()
var x_axis_size = ProjectSettings.get_setting("display/window/size/viewport_width")
var y_axis_size = ProjectSettings.get_setting("display/window/size/viewport_height")
world_rect = world_rect.grow_individual(-x_axis_size/2, -y_axis_size/2, -x_axis_size/2, -y_axis_size/2)
func _process(delta: float) -> void:
var movement_vector := Input.get_vector("move_left", "move_right", "move_up", "move_down")
if not movement_vector == Vector2.ZERO:
var target_position := global_position + movement_vector * camera_speed
if world_rect.has_point(target_position):
global_position = global_position.lerp(target_position, 1.0 - exp(-delta * lerp_smoothing))
E 0:00:01:0252 game_camera.gd:30 @ _process(): Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.
<C++ Source> ./core/math/rect2i.h:131 @ has_point()
game_camera.gd:30 @ _process()
There works just fine (in my case i’m creating a Rect2i variable at postion 0, 0 with size of 5000x5000 to test). Your camera has a custom zoom value? That code expects the camera zoom as Vector2(1, 1).