通过制作2D平台游戏学习Godot 4 — 第10部分:游戏相机
到目前为止,和《大金刚》一样,我们的游戏使用默认相机,它可以捕捉整个游戏地图。这意味着我们可以一次性看到所有东西。每款2D平台游戏都有不同类型的相机。有些相机会跟随玩家,有些则会在屏幕上滚动。
你最终为游戏选择的相机类型取决于你试图为玩家群体打造的体验。快速移动并捕捉小区域的相机适用于你试图营造紧张感的游戏或关卡,而移动较慢、捕捉较大区域的相机则能营造更轻松的游戏体验。相机的大小、焦点和速度取决于你的最终目标。
由于我们的游戏灵感源自《大金刚》,所以无需改变相机设置,但为了让游戏独具特色,我们将通过三种不同方式在Godot中创建相机。
在本部分你将学到什么:
- 如何使用Camera2D节点。
- 如何为相机的偏移值设置动画。
- 如何估算和定义相机的限制。
固定摄像机
使用固定摄像头时,摄像头会捕捉整个屏幕或游戏地图的大部分区域。然后可以看到玩家在这个区域内移动。在玩家到达屏幕边缘或该区域尽头之前,摄像头会线性跟随玩家。在我们的游戏中,我们捕捉的是整个游戏地图(即区域)。
一个例子是《大金刚》:
这款游戏已经捕捉到了我们主场景的整个窗口,因此我们无需在场景中添加摄像机来作为固定摄像机。
玩家固定视角相机
使用固定于玩家的相机时,相机会被设置为跟随玩家的中心位置。这意味着相机的相对位置取决于玩家的位置。
一个例子是《洛克人》:
要为我们的角色添加一个固定视角相机,我们需要在“角色”场景中添加一个Camera2D节点。该节点会强制屏幕(当前层)跟随“角色”场景滚动。与手动更改基于CanvasItem的节点位置相比,这样编写可滚动场景的程序会更轻松(也更快)。
如果你现在运行场景,并且你的玩家在地图上四处跑动,相机应该聚焦在他们身上。
我们可以通过在检查器面板中更改相机的缩放值,将相机“移近”玩家。
如果你现在运行场景,摄像机应该会拉近到玩家身上!
如果你在地图上四处跑动,你可能已经注意到相机捕捉到了游戏边界之外的灰色背景。我们不希望相机捕捉这些区域,相反,我们希望相机在到达地图的外部边界时停止。要做到这一点,我们需要更改Camera2D节点的限制。这将以像素为单位限制左右上下的滚动范围。你可以在检查器面板中“限制”下方更改限制。
要获取这些限制的值,你可以在主场景中从左右标尺上拖动,以获取游戏边界边缘的坐标值。
左极限
右极限
上限
下限
这就是我最终得到的结果:
现在,如果你运行场景,相机应该跟随玩家,并在到达游戏边界的外缘时停止!
偏移固定相机
有时我们希望相机仅在超出特定范围后才跟随玩家,而不是始终锁定玩家。这样在相机重新聚焦滚动之前,能给玩家一些空间,使我们能够看到前方的事物。我们可以通过更改相机的偏移量值来实现这一点。这是相机的相对偏移量,能让我们环顾四周。
一个例子是《超级马里奥兄弟》:
如果你已经有了一个Camera2D节点,在检查器面板中取消选择“启用”属性来禁用它。
添加一个新的Camera2D节点。我们还希望根据玩家的方向,让相机缓慢地向左或向右移动,所以还要添加一个AnimationPlayer节点。
在“动画”面板中创建两个新动画,分别命名为“move_left”和“move_right”。
对于这两者,添加一个新的属性轨迹动画。
将其连接到你的Camera2D(即你的新相机,而非已禁用的相机)。
选择偏移属性,因为我们希望相机所处位置不以玩家为中心。
将动画时长调整为你希望相机移动(转场)的速度。我这里采用 0.5 的测试值。然后在动画的起始和结束位置插入关键帧。
现在,在向左移动关键帧的第一个值(0)处,我们需要更改偏移值。我希望相机向右偏移100像素,向上偏移 -50像素。
然后在向左移动关键帧的第二个值(0.5)处,我们需要将偏移值更改为向右移动动画的起始值。我希望相机向左偏移100像素,向上偏移50像素。
在你的向右移动关键帧的第一个值(0)处,我们需要更改偏移值。我希望相机向右偏移100像素,向上偏移50像素。
然后在向右移动关键帧的第二个值(0.5)处,我们需要将偏移值更改为向左移动动画的起始值。我希望相机向左偏移100像素,向上偏移 -50像素。
我还希望相机稍微拉近一些,所以将“缩放”值更改为更大的值,比如 “2”(这是可选的)。由于相机的限制取决于相机的偏移量,所以我们必须在代码中更改相机的限制范围,因为相机在各个方向上的位置会有所不同。
我们还希望相机能够平滑过渡,因此请启用相机的“位置平滑”属性,该属性将以 position_smoothing_speed 的速度将相机平滑地移动到目标位置。你可以根据希望相机过渡或跟随玩家的速度来更改此速度。
我们还希望动画中的过渡捕捉更加平滑。我们可以通过更改动画中的插值模式来实现这一点。插值告诉Godot如何计算关键帧之间的帧值。
支持以下插值模式:
- 最近值:设置最接近的关键帧值。
- 线性:根据两个关键帧之间的线性函数计算来设置值
- 三次方:根据两个关键帧之间的三次函数计算来设置值
我们希望将向左移动和向右移动动画的插值模式从线性改为三次样条。
在我们的玩家脚本中,我们需要创建新的变量来记录玩家当前和上一个方向。我们将使用这些方向来决定是否播放向左移动或向右移动动画。
### Player.gd
extends CharacterBody2D
#player movement variables
@export var speed = 100
@export var gravity = 200
@export var jump_height = -110
# Keep track of the last direction (1 for right, -1 for left, 0 for none)
var last_direction = 0
# Check the direction of the player's movement
var current_direction = 0
如果我们玩家的当前方向为1,那么他们将面向右侧;如果为 -1,则面向左侧。游戏开始时,我们的玩家将面向右侧,因此在游戏开始时,我们必须将当前方向设置为面向右侧。
### Player.gd
#older code
func _ready():
current_direction = -1
现在,我们需要在游戏循环中根据玩家的x轴速度值不断更新这个current_direction
变量。在2D游戏中,当速度的x轴分量为正时,通常表示向右移动,反之则表示向左移动。我们将在process()
函数中更新这个变量。
### Player.gd
#older code
func _process(delta):
if velocity.x > 0: # Moving right
current_direction = 1
elif velocity.x < 0: # Moving left
current_direction = -1
如果我们当前的方向current_direction
与上一个方向不同,我们将根据方向值(1或 -1)播放动画。我们还需要将上一个方向更新为与当前方向相同。这样可以避免重复过渡,因此如果我们面向右侧并且按下向右输入,move_right
动画在我们改变方向之前不会再次播放。
### Player.gd
#older code
func _process(delta):
if velocity.x > 0: # Moving right
current_direction = 1
elif velocity.x < 0: # Moving left
current_direction = -1
# If the direction has changed, play the appropriate animation
if current_direction != last_direction:
if current_direction == 1:
# Play the right animation
$AnimationPlayer.play("move_right")
elif current_direction == -1:
# Play the left animation
$AnimationPlayer.play("move_left")
# Update the last_direction variable
last_direction = current_direction
我们还需要根据方向设置边界。当你向左和向右时,需要针对每个边界(上、下、左、右)进行测试。
### Player.gd
#older code
func _process(delta):
if velocity.x > 0: # Moving right
current_direction = 1
elif velocity.x < 0: # Moving left
current_direction = -1
# If the direction has changed, play the appropriate animation
if current_direction != last_direction:
if current_direction == 1:
# Play the right animation
$AnimationPlayer.play("move_right")
#limits
$Camera2D.limit_left = -110
$Camera2D.limit_bottom = 705
$Camera2D.limit_top = 40
$Camera2D.limit_right = 1068
elif current_direction == -1:
# Play the left animation
$AnimationPlayer.play("move_left")
#limits
$Camera2D.limit_left = 90
$Camera2D.limit_bottom = 705
$Camera2D.limit_top = 40
$Camera2D.limit_right = 1268
# Update the last_direction variable
last_direction = current_direction
你的代码应该像这样。
现在,如果你运行场景,相机应该跟随玩家,改变方向,并且在到达游戏边界的外边缘时停止!如果过渡仍然过于生硬,那么将动画时长设置得更长一点,以实现更平滑的过渡。
恭喜你创建了游戏相机(如果你选择了不固定在主场景的相机)!在下一部分,我们将设置次要敌人,它们会在平台关卡中向我们投掷和生成箱子。
现在是保存项目并为其制作备份的好时机,这样如果出现任何破坏游戏的错误,你就可以恢复到这一步。在继续学习本系列之前,回顾一下你所学的内容。准备好后,我们下一部分见!