Add camera zoom

This commit is contained in:
2026-05-03 15:20:14 +02:00
parent 9e711d436c
commit fee4e0c3ba
5 changed files with 99 additions and 4 deletions

View File

@@ -1,6 +1,7 @@
## Scrolls the camera when the mouse cursor reaches the screen edge. ## Scrolls the camera when the mouse cursor reaches the screen edge.
## Attach to a Camera2D node. Requires an OrthographicCamera2D or ViewportContainer ## Attach to a Camera2D node. Requires an OrthographicCamera2D or ViewportContainer
## to determine screen bounds at runtime. ## to determine screen bounds at runtime.
@tool
extends Camera2D extends Camera2D
## Speed at which the camera scrolls (pixels per second). ## Speed at which the camera scrolls (pixels per second).
@@ -18,6 +19,15 @@ extends Camera2D
## Maximum bounds when clamping (in world space). ## Maximum bounds when clamping (in world space).
@export var clamp_max: Vector2 = Vector2.ZERO @export var clamp_max: Vector2 = Vector2.ZERO
## Zoom sensitivity per mouse wheel tick.
@export var zoom_sensitivity: float = 0.1
## Minimum zoom level.
@export var zoom_min: float = 0.25
## Maximum zoom level.
@export var zoom_max: float = 3.0
var _screen_rect: Rect2 var _screen_rect: Rect2
@@ -29,6 +39,10 @@ func _ready() -> void:
func _process(delta: float) -> void: func _process(delta: float) -> void:
if Engine.is_editor_hint():
queue_redraw()
return
if is_zero_approx(scroll_speed): if is_zero_approx(scroll_speed):
return return
@@ -58,11 +72,46 @@ func _get_scroll_direction(mouse_pos: Vector2) -> Vector2:
return direction.normalized() return direction.normalized()
func _draw() -> void:
if not Engine.is_editor_hint():
return
if not clamp_enabled:
return
var rect_size: Vector2 = clamp_max - clamp_min
draw_rect(Rect2(clamp_min, rect_size), Color(1, 0.4, 0, 0.35))
draw_rect(Rect2(clamp_min, rect_size), Color(1, 0.6, 0.2, 0.8), false, 2.0)
func _clamp_position() -> void: func _clamp_position() -> void:
position.x = clampf(position.x, clamp_min.x, clamp_max.x) position.x = clampf(position.x, clamp_min.x, clamp_max.x)
position.y = clampf(position.y, clamp_min.y, clamp_max.y) position.y = clampf(position.y, clamp_min.y, clamp_max.y)
func _unhandled_input(event: InputEvent) -> void:
if Engine.is_editor_hint():
return
if event is InputEventMouseButton:
var mouse_event: InputEventMouseButton = event
if mouse_event.button_index == MOUSE_BUTTON_WHEEL_UP:
_zoom(zoom_sensitivity, mouse_event.position)
elif mouse_event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
_zoom(-zoom_sensitivity, mouse_event.position)
func _zoom(change: float, mouse_pos: Vector2) -> void:
var new_zoom_value: float = clampf(zoom.x + change, zoom_min, zoom_max)
if is_equal_approx(new_zoom_value, zoom.x):
return
var viewport_center: Vector2 = get_viewport_rect().size / 2.0
var offset: Vector2 = mouse_pos - viewport_center
var world_point: Vector2 = position + offset / zoom
zoom = Vector2(new_zoom_value, new_zoom_value)
position = world_point - offset / zoom
func _update_screen_rect() -> void: func _update_screen_rect() -> void:
var viewport: Viewport = get_viewport() var viewport: Viewport = get_viewport()
if viewport != null: if viewport != null:

View File

@@ -3,21 +3,19 @@ extends Node2D
## Emitted when production progress changes (0.0 to 1.0) ## Emitted when production progress changes (0.0 to 1.0)
signal production_progress_updated(progress: float) signal production_progress_updated(progress: float)
## Emitted when magic gold balance changes ## Emitted when magic gold balance changes
signal magic_gold_balance_updated(amount: BigNumber) signal magic_gold_balance_updated(amount: BigNumber)
## Emitted when a production cycle completes ## Emitted when a production cycle completes
signal production_completed(amount: BigNumber) signal production_completed(amount: BigNumber)
## Configuration data for this tower ## Configuration data for this tower
@export var data: AlchemyTowerData @export var data: AlchemyTowerData
## Available craft recipes ## Available craft recipes
@export var craft_recipes: AlchemyCraftCatalogue @export var craft_recipes: AlchemyCraftCatalogue
## Reference to game state ## Reference to game state
@onready var game_state: LevelGameState = find_parent("LevelGameState") @onready var game_state: LevelGameState = find_parent("LevelGameState")
@onready var _alchemy_currencies_panel: AlchemyCurrenciesPanel = $AlchemyCurrenciesPanel
## Current production state ## Current production state
var production_time_elapsed: float = 0.0 var production_time_elapsed: float = 0.0
@@ -194,3 +192,11 @@ func _on_craft_requested(recipe: Variant) -> void:
var success: bool = craft_item(recipe) var success: bool = craft_item(recipe)
if not success: if not success:
push_warning("AlchemyTower: Failed to craft %s - insufficient funds" % recipe.output_currency.display_name) push_warning("AlchemyTower: Failed to craft %s - insufficient funds" % recipe.output_currency.display_name)
func _on_area_2d_mouse_entered() -> void:
_alchemy_currencies_panel.visible = true
func _on_area_2d_mouse_exited() -> void:
_alchemy_currencies_panel.visible = false

View File

@@ -25,9 +25,13 @@ position = Vector2(0.5, 11)
shape = SubResource("RectangleShape2D_8pntr") shape = SubResource("RectangleShape2D_8pntr")
[node name="AlchemyCurrenciesPanel" parent="." unique_id=731368154 instance=ExtResource("2_8pntr")] [node name="AlchemyCurrenciesPanel" parent="." unique_id=731368154 instance=ExtResource("2_8pntr")]
visible = false
offset_left = 65.0 offset_left = 65.0
offset_top = -75.0 offset_top = -75.0
offset_right = 65.0 offset_right = 65.0
offset_bottom = -75.0 offset_bottom = -75.0
currency = ExtResource("7_b8p5n") currency = ExtResource("7_b8p5n")
craft_recipes = ExtResource("5_recipes") craft_recipes = ExtResource("5_recipes")
[connection signal="mouse_entered" from="Area2D" to="." method="_on_area_2d_mouse_entered"]
[connection signal="mouse_exited" from="Area2D" to="." method="_on_area_2d_mouse_exited"]

View File

@@ -90,5 +90,12 @@ position = Vector2(105, 920)
position = Vector2(1239, 775) position = Vector2(1239, 775)
[node name="Camera2D" type="Camera2D" parent="." unique_id=1494908331] [node name="Camera2D" type="Camera2D" parent="." unique_id=1494908331]
position = Vector2(5, 0)
offset = Vector2(960, 540) offset = Vector2(960, 540)
script = ExtResource("17_x77df") script = ExtResource("17_x77df")
clamp_enabled = true
clamp_min = Vector2(-1920, -1080)
clamp_max = Vector2(1920, 1080)
zoom_sensitivity = null
zoom_min = null
zoom_max = null

View File

@@ -6,8 +6,37 @@
[node name="EdgeScrollCamera" type="Node2D" unique_id=1977543223] [node name="EdgeScrollCamera" type="Node2D" unique_id=1977543223]
[node name="Camera2D" type="Camera2D" parent="." unique_id=455629291] [node name="Camera2D" type="Camera2D" parent="." unique_id=455629291]
offset = Vector2(960, 540)
script = ExtResource("1_08uhj") script = ExtResource("1_08uhj")
clamp_enabled = true
clamp_max = Vector2(3000, 2000)
[node name="Icon" type="Sprite2D" parent="." unique_id=629901180] [node name="Icon" type="Sprite2D" parent="." unique_id=629901180]
position = Vector2(469, 252) position = Vector2(988, 485)
texture = ExtResource("2_c5cli")
[node name="Icon2" type="Sprite2D" parent="." unique_id=919808260]
position = Vector2(1714, 130)
texture = ExtResource("2_c5cli")
[node name="Icon3" type="Sprite2D" parent="." unique_id=368045162]
position = Vector2(1750, 957)
texture = ExtResource("2_c5cli")
[node name="Icon6" type="Sprite2D" parent="." unique_id=1755994416]
position = Vector2(2641, 1655)
rotation = 0.9242716
texture = ExtResource("2_c5cli")
[node name="Icon7" type="Sprite2D" parent="." unique_id=460486605]
position = Vector2(895.00024, 1641)
rotation = 0.9242716
texture = ExtResource("2_c5cli")
[node name="Icon4" type="Sprite2D" parent="." unique_id=957351194]
position = Vector2(134, 962)
texture = ExtResource("2_c5cli")
[node name="Icon5" type="Sprite2D" parent="." unique_id=2077463270]
position = Vector2(137, 129)
texture = ExtResource("2_c5cli") texture = ExtResource("2_c5cli")