Minimapa

Una Camera2D alejada en un SubViewport que comparte el World2D del juego. El HUD está fuera de ese mundo, por eso no aparece en el mapa.

El editor de arriba está ejecutando este proyecto. Cambia una línea y se recarga.

Clases de Godot usadas

SubViewportCamera2DWorld2D

El código

scripts/main.gd
extends Node

const MAP: Vector2 = Vector2(2600, 1600)
const SPEED: float = 400.0
const MINIMAP_SIZE: Vector2i = Vector2i(320, 240)

var stage: SubViewport
var player: Node2D
var minimap_camera: Camera2D
var route: PackedVector2Array = PackedVector2Array()
var target: int = 1
var manual: bool = false


func _ready() -> void:
	_add_background()

	var view: Vector2 = get_viewport().get_visible_rect().size

	var container: SubViewportContainer = SubViewportContainer.new()
	container.stretch = true
	add_child(container)
	container.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

	stage = SubViewport.new()
	stage.size = Vector2i(view)
	stage.render_target_update_mode = SubViewport.UPDATE_ALWAYS
	container.add_child(stage)

	_add_world()

	route = PackedVector2Array([Vector2(420, 400), Vector2(2180, 420), Vector2(2160, 1200), Vector2(1300, 800), Vector2(440, 1180)])

	player = _add_player()
	player.position = route[0]

	var camera: Camera2D = Camera2D.new()
	camera.position_smoothing_enabled = true
	camera.position_smoothing_speed = 5.0
	camera.limit_left = 0
	camera.limit_top = 0
	camera.limit_right = int(MAP.x)
	camera.limit_bottom = int(MAP.y)
	player.add_child(camera)
	camera.make_current()

	var minimap: SubViewport = SubViewport.new()
	minimap.size = MINIMAP_SIZE
	minimap.render_target_update_mode = SubViewport.UPDATE_ALWAYS
	add_child(minimap)
	# a Viewport is born with its own World2D, so sharing one has to be explicit
	minimap.world_2d = stage.world_2d

	minimap_camera = Camera2D.new()
	minimap_camera.zoom = Vector2(0.26, 0.26)
	minimap_camera.position = player.position
	minimap.add_child(minimap_camera)
	minimap_camera.make_current()

	_add_hud(minimap)

	print('the corner panel is a second Camera2D on the same World2D, wasd drives the square')


func _process(delta: float) -> void:
	var drive: Vector2 = Vector2(
		float(Input.is_key_pressed(KEY_D)) - float(Input.is_key_pressed(KEY_A)),
		float(Input.is_key_pressed(KEY_S)) - float(Input.is_key_pressed(KEY_W))
	)

	if drive != Vector2.ZERO:
		manual = true

	if manual:
		player.position += drive.normalized() * SPEED * delta
		player.position = player.position.clamp(Vector2(80, 80), MAP - Vector2(80, 80))
	else:
		var goal: Vector2 = route[target]
		player.position = player.position.move_toward(goal, SPEED * delta)

		if player.position.distance_to(goal) < 4.0:
			target = (target + 1) % route.size()

	minimap_camera.position = player.position


func _add_world() -> void:
	var outside: Polygon2D = _make_rect(MAP + Vector2(2400, 2400), Color('#12141a'))
	outside.position = MAP * 0.5
	stage.add_child(outside)

	var ground: Polygon2D = _make_rect(MAP, Color('#12141a').lerp(Color('#2a2f3d'), 0.5))
	ground.position = MAP * 0.5
	stage.add_child(ground)

	for x in range(200, int(MAP.x), 200):
		_add_grid_line(Vector2(x, 0), Vector2(x, MAP.y))

	for y in range(200, int(MAP.y), 200):
		_add_grid_line(Vector2(0, y), Vector2(MAP.x, y))

	for wall in [
		{ at = Vector2(MAP.x * 0.5, 45), size = Vector2(MAP.x, 90) },
		{ at = Vector2(MAP.x * 0.5, MAP.y - 45), size = Vector2(MAP.x, 90) },
		{ at = Vector2(45, MAP.y * 0.5), size = Vector2(90, MAP.y) },
		{ at = Vector2(MAP.x - 45, MAP.y * 0.5), size = Vector2(90, MAP.y) },
		{ at = Vector2(900, 760), size = Vector2(560, 90) },
		{ at = Vector2(1700, 480), size = Vector2(90, 620) },
		{ at = Vector2(1820, 1160), size = Vector2(620, 90) },
		{ at = Vector2(700, 1300), size = Vector2(90, 520) },
		{ at = Vector2(1180, 1420), size = Vector2(700, 90) },
		{ at = Vector2(2280, 800), size = Vector2(90, 480) },
		{ at = Vector2(420, 700), size = Vector2(90, 340) },
	]:
		var block: Polygon2D = _make_rect(wall.size, Color('#8da5f3').lerp(Color('#12141a'), 0.45))
		block.position = wall.at
		stage.add_child(block)

	var rng: RandomNumberGenerator = RandomNumberGenerator.new()
	rng.seed = 7

	for i in 26:
		var crate: Polygon2D = _make_rect(Vector2(90, 90), Color('#f3d48d').lerp(Color('#12141a'), 0.35))
		crate.position = Vector2(rng.randf_range(200, MAP.x - 200), rng.randf_range(200, MAP.y - 200))
		crate.rotation = rng.randf_range(-0.4, 0.4)
		stage.add_child(crate)


func _add_player() -> Node2D:
	var body: Node2D = Node2D.new()
	body.add_child(_make_rect(Vector2(80, 80), Color('#8eef97')))
	body.add_child(_make_rect(Vector2(30, 30), Color('#fc7f7f')))
	stage.add_child(body)

	return body


func _add_grid_line(from: Vector2, to: Vector2) -> void:
	var line: Line2D = Line2D.new()
	line.points = PackedVector2Array([from, to])
	line.width = 3.0
	line.default_color = Color('#2a2f3d')
	stage.add_child(line)


func _make_rect(size: Vector2, color: Color) -> Polygon2D:
	var rect: Polygon2D = Polygon2D.new()
	rect.polygon = PackedVector2Array([-size * 0.5, Vector2(size.x, -size.y) * 0.5, size * 0.5, Vector2(-size.x, size.y) * 0.5])
	rect.color = color

	return rect


func _add_hud(minimap: SubViewport) -> void:
	var hud: CanvasLayer = CanvasLayer.new()
	add_child(hud)

	var view: Vector2 = get_viewport().get_visible_rect().size
	var origin: Vector2 = Vector2(view.x - MINIMAP_SIZE.x - 34, 34)

	var frame: ColorRect = ColorRect.new()
	frame.color = Color('#8da5f3')
	frame.position = origin - Vector2(4, 4)
	frame.size = Vector2(MINIMAP_SIZE) + Vector2(8, 8)
	hud.add_child(frame)

	var picture: TextureRect = TextureRect.new()
	picture.texture = minimap.get_texture()
	picture.position = origin
	picture.size = Vector2(MINIMAP_SIZE)
	hud.add_child(picture)

	var label: Label = Label.new()
	label.text = 'minimap  zoom 0.26'
	label.add_theme_font_size_override('font_size', 20)
	label.add_theme_color_override('font_color', Color('#8da5f3'))
	label.position = origin + Vector2(0, MINIMAP_SIZE.y + 10)
	hud.add_child(label)

	var title: Label = Label.new()
	title.text = 'the HUD is outside the shared world, so it stays out of the minimap'
	title.add_theme_font_size_override('font_size', 20)
	title.add_theme_color_override('font_color', Color('#8eef97'))
	title.position = Vector2(40, 40)
	hud.add_child(title)


func _add_background() -> void:
	var layer: CanvasLayer = CanvasLayer.new()
	layer.layer = -1
	add_child(layer)

	var background: ColorRect = ColorRect.new()
	background.color = Color('#12141a')
	layer.add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

Más ejemplos de Cámara