Viewport 和画布变换

前言

本文简要介绍了从本地绘制节点内容开始到这些内容绘制到屏幕上为止的 2D 变换过程,对引擎非常底层的细节进行了讨论。

画布变换

正如前面教程 画布层 中提到的那样,每个 CanvasItem 节点(要记得 Node2D 和基于 Control 的节点都使用 CanvasItem 作为它们的公共根)将驻留在 Canvas Layer 中。每个 Canvas Layer 都有一个变换(平移、旋转、缩放等),可以作为 Transform2D 进行访问。

在前面的教程中也有介绍,节点默认是在 0 层上绘制,即内置的画布。如果要把节点放在不同的层中,可以使用 CanvasLayer 节点。

全局画布变换

Viewport 还具有全局画布变换(也是 Transform2D)。这是一个影响所有单独的画布层的主变换。通常这种变换用得不多。它被用在了 Godot 的编辑器中的 CanvasItem 编辑器中。

拉伸变换

最后,Viewport 有拉伸变换,用于调整大小或拉伸屏幕。此变换在内部使用(见 多分辨率),但也可以在每个 Viewport 上手动设置。

MainLoop._input_event() 回调中接收的输入事件上应用了拉伸变换,但没有应用上面那些变换。为了方便将 InputEvent 坐标转换为本地 CanvasItem 坐标,引擎添加了 CanvasItem.make_input_local() 函数。

变换顺序

要使CanvasItem的本地属性中的坐标成为实际屏幕坐标, 必须应用以下一系列变换:

../../_images/viewport_transforms2.png

变换函数

通过以下函数可以获取各个转换:

类型

变换

CanvasItem(画布项)

CanvasItem.get_global_transform()

CanvasLayer(画布层)

CanvasItem.get_canvas_transform()

CanvasLayer(画布层)+GlobalCanvas(全局画布)+Stretch(拉伸)

CanvasItem.get_viewport_transform()

那么最后, 要将CanvasItem的本地坐标转换为屏幕坐标, 只需按以下顺序相乘:

GDScriptC#

  1. var screen_coord = get_viewport_transform() * (get_global_transform() * local_pos)
  1. var screenCord = (GetViewportTransform() * GetGlobalTransform()).Xform(localPos);

但请记住, 通常情况最好不要使用屏幕坐标. 推荐的方法是, 仅仅使用画布坐标( CanvasItem.get_global_transform() ), 以保证自动分辨率调整能正常工作.

提供自定义输入事件

通常需要将自定义输入事件提供给场景树. 有了上述知识, 要正确地做到这一点, 必须通过以下方式完成:

GDScriptC#

  1. var local_pos = Vector2(10, 20) # local to Control/Node2D
  2. var ie = InputEventMouseButton.new()
  3. ie.button_index = BUTTON_LEFT
  4. ie.position = get_viewport_transform() * (get_global_transform() * local_pos)
  5. get_tree().input_event(ie)
  1. var localPos = new Vector2(10,20); // local to Control/Node2D
  2. var ie = new InputEventMouseButton();
  3. ie.ButtonIndex = (int)ButtonList.Left;
  4. ie.Position = (GetViewportTransform() * GetGlobalTransform()).Xform(localPos);
  5. GetTree().InputEvent(ie);