Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Viewport 和畫布變換

前言

本頁介紹了節點從其內容在本機被繪製一直到被繪製到畫面上的 2D 變換過程。本概覽也討論了有關引擎非常低階的細節。

本教學的目標是介紹如何為提供給 Input 的輸入事件在正確的坐標系中確定位置。

關於所有坐標系以及 2D 變換的詳盡描述見 2D 坐標系與 2D 變換

Canvas 變換

與之前的教學,ref:doc_canvas_layers,提到的一樣,所有 CanvasItem 節點 (請記得,基於 Node2D 與 Control 的節點都使用 CanvasItem 作為共同的基礎型別) 會被保持放在 Canvas Layer 中。所有的 Canvas Layer 都有可作為 Transform2D 存取的變換 (平移、旋轉、縮放…等)。

另外,在之前的教學中也有提到,節點預設會在內建 Canvas 的 Layer 0 中繪製。若要將節點放在不同的圖層中,可使用 CanvasLayer 節點。

全域 Canvas 變換

Viewport 也有一個全域 Canvas 變換 (該變換也是 Transform2D)。全域 Canvas 為主變換,會影響所有單獨的 Canvas Layer 變換。一般來說,這種變換並不常用,但在 Godot 編輯器中的 CanvasItem 編輯器中有用到。

拉伸變換

最後,Viewport 還有 拉伸變換 (Stretch Transform) ,該變換時用來縮放或拉伸螢幕的。這個變換時在內部使用的 (與 多解析度 中說明的一樣),但也可以手動在各個 Viewport 上設定。

有接受 MainLoop._input_event() 回呼的輸入事件也會受到該變換加成,但並不屬於上述變換。若要將 InputEvent 座標轉換為本機 CanvasItem 座標,我們提供了方便的 CanvasItem.make_input_local() 函式。

Canvas 變換

根視口是一個 Window。為了能夠像 多解析度 中一樣將*視窗*的內容進行縮放和移動,每個 Window 都包含了*視窗變換*。例如在 Viewport 使用固定長寬比顯示時,負責*視窗*邊緣的黑框。

變換順序

要將 CanvasItem 本機屬性內的座標轉換為實際螢幕上的座標,必須要套用下列一系列的變換:

../../_images/viewport_transforms3.webp

變換函式

上圖顯示了一些可用的變換函式。所有變換都是從右向左的,這意味著將一個變換與一個座標相乘會得到一個更靠左的坐標系,將一個變換的 affine inverse 相乘會得到一個更靠右的坐標系:

# Called from a CanvasItem.
canvas_pos = get_global_transform() * local_pos
local_pos = get_global_transform().affine_inverse() * canvas_pos

最後,要將 CanvasItem 本機座標轉換為螢幕座標,只需要以下列順序相乘:

var screen_coord = get_viewport().get_screen_transform() * get_global_transform_with_canvas() * local_pos

請記得,我們通常不會使用到螢幕座標。建議的做法是只要處理 Canvas 座標就好了 (CanvasItem.get_global_transform()),使用 Canvas 座標就可以自動處理好不同的螢幕解析度縮放。

處理自定輸入事件

很多情況下我們會想將自定輸入事件提供給場景樹。有了上述知識,若要正確將自定輸入事件提供給場景樹,則必須要通過下列方法:

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)