Godot 学习 04——Viewport 和 Collision

关于视图,碰撞的二三事。这里学的内容不多……

关于视图

https://docs.godotengine.org/en/stable/tutorials/2d/2d_transforms.html#canvas-transform

Viewport,CanvasItem,CanvasLayer

Viewport 就是进行绘制的地方,CanvasItem 即是要绘制的东西,CanvasLayer 则用于分层绘制。

Viewport 可以嵌套,但这里只考虑单 Viewport 的情况。实际上,游戏的操作系统窗口就是 Viewport,它负责绘制它的直接和间接的 CanvasItem 子节点(当然,这不是说 CanvasItem 就无法超出 Viewport 的范围了,实际上,总是超出的,比如卷轴动作游戏,肯定左边右边会超出一大堆,只是转换镜头)。

Viewport 有一个 canvas_transform 属性,它是一个 Transform2D,用来对所有 CanvasItem 进行转换。如果要做镜头抖动,缩放等效果,通常操作这个属性会更加方便。

但我们不想所有东西都被这个变换影响——比如我想做一个视差的背景(远景变化慢,近景变化快以突出立体感),然后这时候抖动,我希望只是近景抖动;比如缩放的时候,我不希望 UI 也跟着缩放;比如做个场景切换之间的动画,不想 UI 也被这个动画影响……

这时候就引入了 CanvasLayer——CanvasLayer 自己持有自己的变换,因此互相之间的变换不会被干涉。

CanvasLayer 几乎也可以认为是绘图的根本——它的子节点依赖 CanvasLayer 的 transform,而 CanvasLayer 的 transform 不依赖 viewport 的 canvas_transform。不像普通的节点会依赖父节点的 transform,CanvasLayer 自己是“fixed”的。这里或者也可以说,Viewport 也内涵一个 CanvasLayer。

一般来说,绘制顺序是根据节点在 Scene 树中出现的顺序。但 CanvasLayer 和 Viewport 会设置自己下面的 CanvasItem 的绘制层级,使用数字表示,数字越大的越靠前。Viewport 的层级默认是 0,多个 CanvasLayer 可以有相同的绘制层级,这时候它们下面的元素的绘制顺序是不确定的。

同一个层级中,则可以通过 z-index 控制顺序;但绘制层级是比 z-index 更优先的

Camera

Viewport 可以和一个 Camera 绑定,以方便进行镜头平移操作。CanvasLayer 不能和镜头绑定,没意义。镜头也不会改变 CanvasLayer 的显示。所以这里不要把镜头当作特别重要的东西,实际上镜头完全是可以不要的,始终使用 canvas_transform 也是可以的。

2D 的镜头使用 Camera2D。它继承 Node2D,其中 position 属性可用,rotation 属性需要配置 Ignore Rotation 为 false(以支持滚转),Camera2D 自己有 offset 和 zoom 属性。显然 offset 可以很方便做动画——平常我一直用 position,然后角色被打了我把 offset 摇一摇

也有所谓的 ParallaxBackground,一个 Viewport 要有一个 ParallaxBackground,它会和 Camera2D 协同作用来提供视差背景。

各种变换

https://docs.godotengine.org/en/stable/tutorials/2d/2d_transforms.html

下面不考虑 Subviewport 和 Window 等玩意儿,因为文档没提,我现在也没能力自己去研究。

首先我们知道,CanvasItem 是被绘制的对象,所以从它开始研究。CanvasItem 虽然是虚类,但是规定了方法 get_transform,因此认为是可以变换的,可以从它开始。

CanvasItem 有自己的变换,而必定直接或间接属于一个 Canvas Layer(这里认为 Viewport 也是 CanvasLayer),而 Canvas Layer 有自己的变换。

Viewport 处 Canvas Transform(Viewport 作为 Canvas Layer 时持有的变换)之外,也有一个变换,称为 Global Canvas Transform,它会应用给所有 Canvas Layer,这个主要是在 Godot 内部使用。

然后是一个 Stretch transform——顾名思义,拉伸,根据长宽比配置去放大缩小内容。

最后是一个 Window transform——为了适应操作系统的窗口可以任意调整,这个变换可以在两侧或上下加黑边以保证长宽比。

根据上图,一个 CanvasItem 的本地坐标,称为 Item 坐标,根据这个 CanvasItem 自己的变换反向操作,就能得到它父 CanvasItem 的坐标;反复操作,直到父节点是 Canvas Layer,这时候的坐标就是 Canvas 坐标。这个坐标可能是最常用到的。

Canvas 自己也有一个 transform(对 canvasLayer 是 transform,对 viewport 是 canvas_transform),再反向操作,便得到了 Viewport 坐标。

使用这里的 Viewport 坐标基本上已经顶天了,所以不再研究了!再往上跑就跑到这个显示器上的坐标了

这里只需注意到,Canvas 的 transform 是直接相对于 Viewport 的,和 Canvas 之间的嵌套关系无关

物理

https://docs.godotengine.org/en/stable/tutorials/physics/physics_introduction.html

上面 CanvasLayer 的 transform 让人有个错觉,以为改变了视角就是改变了世界,这是错误的。Canvas 的 transform 只影响元素的视觉呈现,元素的物理坐标,即在World2D中的实际坐标,是没有被改变的,这个坐标是无视 Canvas 的 transform 的。

这里说的实际坐标,后面称为物理坐标吧,一般获取它是没啥意义的,因为 Godot 自己把细节都处理好了,使用者只需要绘制碰撞形状,然后使用相关信号去查看碰撞即可。

说到物理 Physic,实际上就是在说碰撞 Collision。即使是俯视角的游戏(没有重力),也会谈到所谓的物理。Godot 提供了多种碰撞对象以提供碰撞检测和响应:

  1. Area2D,它检测是否有碰撞物体进来和离开该区域,通常用在检查点要检查固定空间是否有东西进来等情况,它只检测,不作用其它物体;Area2D 也可以控制特定区域的物理属性。
  2. StaticBody2D,只影响其它物体,但不被影响(即只能通过代码控制),比如关卡的地面,物体等,但它能配置一个固定的线速度、角速度,所以移动平台也可以用它
  3. RigidBody2D,影响和被影响,就像现实中的物体;基于物理的敌人可以归到此类;要影响 RigidBody2D,不是去直接设置它的变换,而是给它施加力,然后让物理引擎处理剩下的
  4. CharacterBody2D,能够有碰撞检测,但不会被物理引擎操作,支持手动控制移动,主角的话就该用 CharacterBody2D;需要控制巡逻路线等的敌人也可用它。

StaticBody2D 和 RigidBody2D 可以通过 PhysicsMaterial 定义相关物理属性,如摩擦力和弹力等。

Godot 的物理处理的频率是固定的,默认配置是一秒 60 次,在物理 tick 中执行的代码称作 physic 处理,即_physic_process,它按固定频率执行,否则称为 idle 处理,即_process,它尽可能快(每一帧)地执行。

RigidBody2D 的诸多性质都是交给引擎计算的,如果直接设置它的属性,包括位置,线速度等,都可能会导致奇怪的情况;但它有一个回调_integrate_forces(),在其中修改它的属性是安全的。

……更具体的在实践中学,这里只知道有几种类型即可。

Collision Layer, Mask

有所谓的碰撞层和碰撞蒙版——碰撞体总是在特定碰撞层中,默认是层 1,碰撞体能通过蒙版配置要扫描哪些碰撞层上的碰撞体(不扫描的层就不会被它影响),默认是层 1

这就是说,一个碰撞体只能被它的蒙版的碰撞层上的碰撞体影响,注意这里只能配置它被谁影响,不能配置它影响谁。比如,物体 a 在层 1,物体 b 在层 2,如果 a 不配置 mask,b 配置 mask 为 1,则 a 会影响 b,而 b 不会影响 a;如果 a 配置 mask 为 2,b 配置 mask 为 1,则它们会互相影响。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 协议 ,转载请注明出处!