搬运翻译自3D等距相机
嘿!我们正在制作一个3D等距游戏演示版本,我想在此分享一些我们目前所使用的摄像技巧!
3D等距相机
等距游戏最初是在2D中“伪装”3D的一种方法。然而,如今它可以基于2D或3D实现有趣美学或游戏体验。我将专注于3D实现(比如《纪念碑谷》)。
等距相机通常遵循45-45规则。他们应该以45度的角度俯视玩家,环境也应该以45度的角度倾斜。

此外,我们将相机的投影模式更改为正交Orthogonal
。这附带一些重要的注意事项。为了实现“放大与缩小”,您必须更改摄像头尺寸,而不是更改摄像头距离。目前,我们使用的相机尺寸为25。相机距离将影响投影,但是您必须使用它来了解它的工作原理。
为了更好地实现这一点,我们创建了一个由空间节点(摄像机目标)和附加摄像机组成的cameraRig
场景。为了轻松地维持45度不变量,相机将在_ready()
函数中适当地移动。
look_at_from_position((Vector3.UP + Vector3.BACK) * camera_distance,
get_parent().translation, Vector3.UP)
正如u/mad_hmpf所提到的那样,真正的等距摄像机的角度为35.26°,2d可能为60°。为了得到这个,只需简单Vector3.BACK
与sqrt(2)
相乘,如果你想在不改变距离的情况下改变角度,可以考虑标准化Vector3.UP + Vector3.BACK
。
跟随玩家
现在,我们需要这个相机来跟随玩家。为了做到这一点,我们将脚本附加到CameraRig
场景中,以便四处移动目标。一个简单的实现将只是将摄影师附加到玩家,或保持其平移相等。
translation = player.translation
然而,这可能导致不稳定和糟糕的镜头运动。
看下面的不稳定镜头运动视频
为了解决此问题,我们将使相机lerp插值
朝向玩家位置,如下所示:
translation = lerp(translation, player.translation, speed_factor * delta)
这个lerp
是与帧无关的,所以较慢的时间步长或较低的帧速率不会影响它。但是speed_factor速度系数
应该是多少呢?我们使用dead_zone_radius死区半径
来定义它,这是玩家与摄像机之间的最大距离。当与玩家的最大速度相结合时,我们可以通过简单地将玩家速度除以我们的死区半径来计算速度系数。这为我们提供了更流畅的摄像机,甚至对于传送也是如此。
平滑的相机运动示例看下面视频。
通过将相机位置和玩家位置解耦,我们还可以限制相机移动不超出界限等。不脱离界限,您只需要定义相机可以在每个关卡移动的区域,并且让相机尽可能靠近玩家,同时仍留在该区域。您甚至可以利用碰撞使摄像头沿着该区域的墙壁滑动(而不是必须手动处理)。但是,由于我们还没有开发完整的关卡,因此我们尚未实施该系统。
相机抖动
本部分的大部分内容都来自本次GDC演讲 Math for Game Programmers: Juicing Your Cameras With Math
b站的搬运 游戏程序员的数学:用数学让你的相机更多汁
对该视频的简单概括使用数学使你游戏中的相机更有范儿
对于相机抖动系统,我们先来说说我们到底要抖动什么。 为了晃动相机,我们将偏移某些值。 最初,您可能只想从字面上摇动相机位置。 虽然这有帮助,但它在 3D 中可能是一种平淡无奇的效果,因为即使平移抖动,物体也不会移动太多。 所以我们也将旋转相机,以移动更远的东西。
我们将为相机抖动定义一个介于0和1之间的创伤值trauma 。这将因受到伤害等事情而增加,并将随着时间的推移逐渐减少。然而,我们的摇晃实际上不会与创伤成正比,而是与创伤值的二次方成正比。这就为玩家创造了一个更明显的大创伤和小创伤值之间的差异。
我们最初可能只是想在每一帧为摄像机挑选随机的偏移量。虽然这也行得通,但我们的游戏还涉及到一个减缓时间的机制。因此,我们更愿意让摄像机的晃动随着时间的推移而减慢。
这意味着我们不能简单地选择一个随机值。相反,我们将使用Godot的OpenSimplexNoise
类来创建一个连续的噪音。我们可以用各种方式配置它,但我选择了4个八度octaves和一个0.25的周期period 。为了得到每个偏移量的不同噪声,我们不需要创建5个OpenSimplexNoise类,而是直接生成2D噪声,对每个偏移量采取不同的y值。代码如下。
h_offset = rng.get_noise_2d(time, 0) * t_sq * shake_factor
v_offset = rng.get_noise_2d(time, 1) * t_sq * shake_factor
rotate_x(rng.get_noise_2d(time, 2) * t_sq * shake_factor)
rotate_y(rng.get_noise_2d(time, 3) * t_sq * shake_factor)
rotate_z(rng.get_noise_2d(time, 4) * t_sq * shake_factor)
这是视频结果