视口变换与画布变换
前言
本文简要介绍了从本地绘制节点内容开始到这些内容绘制到屏幕上为止的 2D 变换过程,对引擎非常底层的细节进行了讨论。
本教程的目标是介绍如何为提供给 Input 的输入事件在正确的坐标系中确定位置。
关于所有坐标系以及 2D 变换的详尽描述见 2D 坐标系与 2D 变换。
画布变换
正如前面教程 画布层 中提到的那样,每个 CanvasItem 节点(要记得 Node2D 和基于 Control 的节点都使用 CanvasItem 作为它们的公共根)将驻留在 Canvas Layer 中。每个 Canvas Layer 都有一个变换(平移、旋转、缩放等),可以作为 Transform2D 进行访问。
在前面的教程中也有介绍,节点默认是在 0 层上绘制,即内置的画布。如果要把节点放在不同的层中,可以使用 CanvasLayer 节点。
全局画布变换
Viewport 还具有全局画布变换(也是一个 Transform2D)。这是一个能够影响所有画布层的主变换。一般而言,主要用于 Godot 的 CanvasItem 编辑器。
拉伸变换
最后,Viewport 有拉伸变换,用于调整大小或拉伸屏幕。此变换在内部使用(见 多分辨率),但也可以在每个 Viewport 上手动设置。
输入事件会与这个变换相乘,但还缺少前面说的那些。为了方便将 InputEvent 的坐标转换到 CanvasItem 局部坐标,添加了 CanvasItem.make_input_local() 函数。
窗口变换
根视口是一个 Window。为了能够像 多分辨率 中一样将窗口的内容进行缩放和移动,每个 Window 都包含了窗口变换。例如在 Viewport 使用固定长宽比显示时,负责窗口边缘的黑框。
变换顺序
要将 CanvasItem 本地坐标转换为实际屏幕坐标,必须应用以下变换链:
变换函数
上图显示了一些可用的变换函数。所有变换都是从右向左的,这意味着将一个变换与一个坐标相乘会得到一个更靠左的坐标系,将一个变换的 affine inverse 相乘会得到一个更靠右的坐标系:
GDScriptC#
# Called from a CanvasItem.
canvas_pos = get_global_transform() * local_pos
local_pos = get_global_transform().affine_inverse() * canvas_pos
// Called from a CanvasItem.
canvasPos = GetGlobalTransform() * localPos;
localPos = GetGlobalTransform().AffineInverse() * canvasPos;
那么最后, 要将CanvasItem的本地坐标转换为屏幕坐标, 只需按以下顺序相乘:
GDScriptC#
var screen_coord = get_viewport().get_screen_transform() * get_global_transform_with_canvas() * local_pos
var screenCoord = GetViewport().GetScreenTransform() * GetGlobalTransformWithCanvas() * localPos;
但请记住, 通常情况最好不要使用屏幕坐标. 推荐的方法是, 仅仅使用画布坐标( CanvasItem.get_global_transform()
), 以保证自动分辨率调整能正常工作.
提供自定义输入事件
通常需要将自定义输入事件提供给场景树。要正确地做到这一点,必须通过以下方式完成:
GDScriptC#
var local_pos = Vector2(10, 20) # Local to Control/Node2D.
var ie = InputEventMouseButton.new()
ie.button_index = MOUSE_BUTTON_LEFT
ie.position = get_viewport().get_screen_transform() * get_global_transform_with_canvas() * local_pos
Input.parse_input_event(ie)
var localPos = new Vector2(10,20); // Local to Control/Node2D.
var ie = new InputEventMouseButton()
{
ButtonIndex = MouseButton.Left,
Position = GetViewport().GetScreenTransform() * GetGlobalTransformWithCanvas() * localPos,
};
Input.ParseInputEvent(ie);