准备工作#
窗口尺寸设置为 1920 * 1080 ,加入一些输入映射,例如WASD对应"ui_xx",设置空格或者你喜欢的按键作为"ui_recovery"
新建Node2d、Sprite2D和Carmea2d
取消Sprite2D的居中,并把Carmea2d的锚点模式设置为Fixed Top Left
导入一张图片作为Sprite2D的纹理,我这里使用Azgaar的奇幻地图生成器随机生成了一张
运行图片左上角对齐屏幕左上角即可。
给创建的
Node2d节点添加
GDScript脚本,写入以下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
| @onready var camera = $Camera2D
@onready var sprite = $Sprite2D
var CamScale = Vector2(1.0, 1.0)
var CamPos = Vector2(0.0, 0.0)
func _ready() -> void:
pass
func _input(event: InputEvent) -> void:
pass
func _process(delta: float) -> void:
pass
|
其中CamScale保存变换后的缩放倍率,CamPos保存变换后的锚点位置
按照鼠标位置缩放#
设置的相机锚点为左上角,即(0,0),那么直接对相机的zoom参数改变会以(0,0)为中心缩放。那么如果要实现按照鼠标为中心缩放则需要在缩放的同时进行平移。那么如何进行平移呢?
代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| var scale_delta = 0.05
var mouse_position
func _input(event: InputEvent) -> void:
mouse_position = get_viewport().get_mouse_position()
if event is InputEventMouseButton:
var BeforeScale = CamScale
#滚轮缩放
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
CamScale.x = exp(log(CamScale.x) + scale_delta)
CamScale.y = exp(log(CamScale.y) + scale_delta)
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
CamScale.x = exp(log(CamScale.x) - scale_delta)
CamScale.y = exp(log(CamScale.y) - scale_delta)
var pscale = CamScale / BeforeScale
if pscale != Vector2(1, 1):
#按照鼠标位置缩放
var delta_mouse_posion = (pscale * (position + mouse_position) - mouse_position) / CamScale
CamPos += delta_mouse_posion
camera.zoom = CamScale
|
平移操作分为WASD平移和鼠标中键平移,两者逻辑稍有不同。
我们需要设置变量来保存平移一次的距离,在脚本中加入如下代码
WASD平移#
如果是在_input()里判断某键按下,似乎长按时只会相应一次。于是在_process里使用Input.get_vector来完成WASD的平移,此处的参考链接忘记了,欢迎评论区补充,我会及时补上。代码如下所示
1
2
3
4
5
6
7
8
| var offsets: Vector2
func _process(delta: float) -> void:
# WASD平移
var input_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
offsets = offset * input_direction / CamScale #当放大时,相机移动速度相较原图也是放大的,所以需要除以缩放倍数
CamPos += offsets
camera.position = CamPos
|
鼠标中键平移#
鼠标中键平移实际上就是按下鼠标中键时记录位置,没有松开中键时当前鼠标位置与记录位置做差作为平移偏移量,并更新记录位置,代码如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| var mouse_position
var isDrag = false
var startPos: Vector2
func _input(event: InputEvent) -> void:
mouse_position = get_viewport().get_mouse_position()
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_MIDDLE:
if event.is_pressed():
startPos = mouse_position
isDrag = true
else:
isDrag = false
# 鼠标中键拖动平移
if isDrag:
CamPos += (mouse_position - startPos) * CamScale
camera.position = CamPos
startPos = mouse_position
|
相机锚点限制#
有时我们需要设置平移限制来避免相机移动到地图之外去。
首先我们需要地图的大小,可以使用sprite.texture.get_size()获得,还需要获得当前视口大小,可以使用get_viewport_rect().end获得矩形的右下角的点。
当相机移动的最右(下)时,屏幕最右(下)侧可以看到地图的最右(下)侧。当缩放系数为1时,此时的式子为
$$相机位置 + 屏幕尺寸 = 图片尺寸$$
当缩放系数改变时,只需要在屏幕尺寸除以一个缩放系数即可。
代码如下,在每次改变缩放大小时需要更新右侧和下侧的锚点限制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| var leftSide
var topSide
var rightSide
var bottomSide
var limit_offset: Vector2
func _ready() -> void:
var temp = get_viewport_rect().end / CamScale
limit_offset = Vector2.ZERO
leftSide = - limit_offset.x
topSide = - limit_offset.y
# 缩放值改变时更新相机锚点限制
rightSide = maxf(0, sprite.texture.get_size().x - temp.x + limit_offset.x)
bottomSide = maxf(0, sprite.texture.get_size().y - temp.y + limit_offset.y)
......
func _process(delta: float) -> void:
......
## 相机锚点限制
CamPos.x = maxf(CamPos.x, 0)
CamPos.y = maxf(CamPos.y, 0)
CamPos.x = minf(CamPos.x, rightSide)
CamPos.y = minf(CamPos.y, bottomSide)
camera.position = CamPos
|
效果展示#
完整代码#
点击展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
| extends Node2D
@onready var camera = $Camera2D
@onready var sprite = $Sprite2D
var CamScale = Vector2(1.0, 1.0)
var CamPos = Vector2(0.0, 0.0)
var offset = 20
var scale_delta = 0.05
var offsets: Vector2
var mouse_position
var isDrag = false
var startPos: Vector2
var leftSide
var topSide
var rightSide
var bottomSide
var limit_offset: Vector2
func _ready() -> void:
var temp = get_viewport_rect().end / CamScale
limit_offset = Vector2.ZERO
leftSide = - limit_offset.x
topSide = - limit_offset.y
rightSide = maxf(0, sprite.texture.get_size().x - temp.x + limit_offset.x)
bottomSide = maxf(0, sprite.texture.get_size().y - temp.y + limit_offset.y)
func _input(event: InputEvent) -> void:
mouse_position = get_viewport().get_mouse_position()
# 恢复初始视口
if event.is_action_pressed("ui_recovery"):
CamPos = Vector2(0.0, 0.0)
CamScale = Vector2(1.0, 1.0)
camera.zoom = CamScale
# 缩放值改变时更新相机锚点限制
var temp = get_viewport_rect().end / CamScale
rightSide = maxf(0, sprite.texture.get_size().x - temp.x + limit_offset.x)
bottomSide = maxf(0, sprite.texture.get_size().y - temp.y + limit_offset.y)
if event is InputEventMouseButton:
var BeforeScale = CamScale
#滚轮缩放
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
CamScale.x = exp(log(CamScale.x) + scale_delta)
CamScale.y = exp(log(CamScale.y) + scale_delta)
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
CamScale.x = exp(log(CamScale.x) - scale_delta)
CamScale.y = exp(log(CamScale.y) - scale_delta)
# 鼠标中键拖动平移
if event.button_index == MOUSE_BUTTON_MIDDLE:
if event.is_pressed():
startPos = mouse_position
isDrag = true
else:
isDrag = false
var pscale = CamScale / BeforeScale
if pscale != Vector2(1, 1):
#按照鼠标位置缩放
var delta_mouse_posion = (pscale * (position + mouse_position) - mouse_position) / CamScale
CamPos += delta_mouse_posion
camera.zoom = CamScale
# 缩放值改变时更新相机锚点限制
var temp = get_viewport_rect().end / CamScale
rightSide = maxf(0, sprite.texture.get_size().x - temp.x + limit_offset.x)
bottomSide = maxf(0, sprite.texture.get_size().y - temp.y + limit_offset.y)
# 鼠标中键拖动平移
if isDrag:
CamPos += (mouse_position - startPos) * CamScale
startPos = mouse_position
func _process(delta: float) -> void:
# WASD平移
var input_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
offsets = offset * input_direction / CamScale
CamPos += offsets
# 相机锚点限制
CamPos.x = maxf(CamPos.x, 0)
CamPos.y = maxf(CamPos.y, 0)
CamPos.x = minf(CamPos.x, rightSide)
CamPos.y = minf(CamPos.y, bottomSide)
camera.position = CamPos
|
参考链接#