'Screen Space - Overlay'なCanvasでのRectTransform (Unity uGUI)

# 前提
- この記事は、`RectTransform`に対する筆者の理解をまとめたものです。
    - これまでずっと直感的に使用していたので、改めて研究してみたものです。
    - 網羅的ではなく、また、独自の解釈を含みます。
- [RectTransform (公式スクリプトリファレンス)](https://docs.unity3d.com/ja/current/ScriptReference/RectTransform.html)
- [CanvasScaler (公式スクリプトリファレンス)](https://docs.unity3d.com/ja/2018.4/ScriptReference/UI.CanvasScaler.html)

###### 検証環境
- Unity 2019.4.x (LTS)

# 概念

![スクリーンショット 2020-12-07 123044.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365845/f754fa5e-c3b0-ffe5-4eb4-9aa87e1c532f.png)

- 青色の`Targrt`オブジェクトに着目します。
    - `RectTransform`は、`Transform`の派生クラスです。
        - `MonoBehavior`を継承したクラスが`RectTransform`を持つなら、`(RectTransform) transform`あるいは`transform as RectTransform`で、`RectTransform`にアクセスできます。
            - `RectTransform`がない場合、キャストは例外を発生し、`as`は`null`を返します。
            - 例外が発生しなければキャストの方が高速です。
    - ワールド座標系
        - `Screen Space - Overlay`な`Canvas`でのワールド座標系は、スクリーン座標系に一致します。
    - ローカル座標系
        - `Transform`は、親のローカル座標系で配置されます。
        - `RectTransform.rect`は自身のローカル座標系で示されます。
        - `RectTransform`のメンバーはローカル座標系に依存しているので、スケールの影響を受けます。
            - `rect`や`sizeDelta`などからワールド／スクリーン座標系の実サイズを得るためには、`lossyScale`を適用する必要があります。
    - 正規化座標系
        - 基準とする矩形の左下を`(0.0,0.0)`、右上を`(1.0,1.0)`とする座標系です。
        - 負数や`1.0`を超える値も有効で、矩形の外が表現できます。
    - `parent`
        - 赤色の矩形は、ヒエラルキーで親になる`Parent`オブジェクトの`rect`です。
        - 個々のオブジェクトは、親の影響下で、自身のローカルな系を持ちます。
    - `anchor`
        - 図中の、白い楔形が指し示す4点です。
            - 4点が全て一致して1点となる場合や、2点ずつ一致して2点となる場合もあります。
        - オブジェクトは、これらの箇所で親に癒着していて、親の`rect`の変化に追従します。
        - 親の`rect`を基準として正規化された座標系で、左下`anchorMin`と右上`anchorMax`の位置が規定されます。
    - `pivot`
        - 図中の、青いドーナッツ型の中心点です。
        - 自身のローカル座標の原点で、オブジェクトの位置を代表します。さらに、回転軸でもあります。
        - 自身の`rect`を基準として正規化された座標系で位置が規定されます。
    - `rect`
        - 図中の、青丸の角と白線で示されている矩形領域です。
        - オブジェクトの領域を表します。
        - `anchorMin`から左下角へのオフセット`offsetMin`と、`anchorMax`から右上角へのオフセット`offsetMax`として規定されます。
    - `scale`
        - 親に対する相対的な拡大率です。
        - ワールドに対しては、ヒエラルキーのルートから積算されたスケールを持つことになります。
    - `rotation`
        - 親に対する相対的な回転角です。
        - ワールドに対しては、ヒエラルキーのルートから加算された回転角を持つことになります。

# 変数
RectTransform|意味|規格|型
|:--|:--|:--|:-:|
anchoredPosition|pivotの位置$^{※}$|anchorからの変位|Vector2
anchoredPosition3D|pivotの位置$^{※}$|anchorからの変位|Vector3
pivot|pivotの位置|rectの正規化座標系|Vector2
anchorMax|anchorの右上位置|親のrectの正規化座標系|Vector2
anchorMin|anchorの左下位置|親のrectの正規化座標系|Vector2
offsetMax|rect右上角の位置|anchorMaxからの変位|Vector2
offsetMin|rect左下角の位置|anchorMinからの変位|Vector2
rect|rectの位置とサイズ|自身のローカル座標系|Rect
sizeDelta|rectのサイズ|anchorとのサイズ差|Vector2
**Transform**から継承|**説明**|**規格**|**型**
localEulerAngles|回転角度|親のローカル座標系|float
localRotation|回転四元数|親のローカル座標系|Quaternion
localPosition|pivotの位置|親のローカル座標系|Vector3
localScale|拡大率|親のローカル座標系|Vector3
eulerAngles|回転角度|ワールド座標系|float
rotation|回転四元数|ワールド座標系|Quaternion
position|pivotの位置|ワールド座標系|Vector3
lossyScale|拡大率|ワールド座標系、読み取り専用|Vector3

#### ※anchorからの変位によるpivotの位置
- `anchoredPosition`は、`anchorMin`と`anchorMax`が一致しない(つまり`anchor`が一点でない)場合は、`pivot`の位置を示せなくなる可能性があります。
    - 例えば、親が`rect=(0,0,200,200)`で、対象が`anchorMin=(0.5,0.5)`、`anchorMax=(1,1)`,`sizeDelta=(0,0)`のとき、`pivot`を何処に移動しても`anchoredPosition`は`(0,0)`のまま変化しません。(ただし、計算精度による微少変動を除きます。)

# Public 関数
RectTransform|説明
|:--|:--|
ForceUpdateRectTransforms|強制的に内部データを再計算します。
GetLocalCorners|rectの頂点座標を自身のローカル座標系で取得します。
GetWorldCorners|rectの頂点座標を自身のワールド座標系で取得します。
SetInsetAndSizeFromParentEdge|親のrectの一辺からの距離とサイズでrectの一軸を設定します。
SetSizeWithCurrentAnchors|rectの一軸のサイズを設定します。

#### GetLocalCorners
`public void GetLocalCorners (Vector3 [4] corners);`

- 四頂点を格納可能な配列を渡して矩形の座標を得ます。

#### SetInsetAndSizeFromParentEdge
`public void SetInsetAndSizeFromParentEdge (RectTransform.Edge edge, float inset, float size);`

- 一回のコールで、矩形の一軸の領域を設定します。
    - `pivot`の値を満たすように、領域が展開されます。
    - 矩形を定めるためには、軸を変えて二回コールしなければなりません。
    - 同じ軸に属する対辺で二回目をコールすると上書きされます。
- `edge`
    - `RectTransform.Edge.Bottom`、`Left`、`Right`、`Top`
    - 親の矩形の四辺から一つを選択します。
- `inset`
    - 領域の開始ポイントです。
- `size`
    - `inset + size`が、領域の終了ポイントです。

#### SetSizeWithCurrentAnchors
`public void SetSizeWithCurrentAnchors (RectTransform.Axis axis, float size);`

- 一回のコールで、矩形の一軸のサイズを設定します。
    - `pivot`の値を満たすように、領域が展開されます。
    - 矩形を定めるためには、軸を変えて二回コールしなければなりません。
- `axis`
    - `RectTransform.Axis.Horizontal`、`Vertical`
    - 軸を選びます。
- `size`
    - 軸の領域サイズです。

# エディタの挙動
- ツールバー
    - `Pivot`/`Center`
        - 選択によって、Sceneウインドウで回転させる際の中心軸が変わります。
        - `pivot`が`rect`中央にないときに`Center`を選んでシーン･ウインドウで回転させると、`rotation`以外のメンバーも変化します。
- インスペクター
    - `anchorMin`と`anchorMax`が一点で一致する場合は、`(posX,Width)`と`(posY,Height)`を編集可能です。
    - `anchorMin`と`anchorMax`が一致しない軸においては、`(Left,Right)`あるいは`(Top,Bottom)`が編集可能です。
    - `Right`は`anchorMax.x`から、`Top`は`anchorMax.y`からの、負のオフセットです。
    - `Left`は`anchorMin.x`から、`Bottom`は`anchorMin.y`からの、正のオフセットです。

# CanvasScaler
- `CanvasScaler`や`Canvas`と共にアタッチされている`RectTransform`の`localScale`は、`Canvas.scaleFactor`と同じ値を示します。
    - 子孫の`RectTransform`はその影響下にあり、その`lossyScale`は、`Canvas`まで順に親を辿って`localScale`を積算したものに一致します。
- つまり、`CanvasScaler`によるスケーリングは、`RectTransform.localScale`を制御しているだけなので、単なる`RectTransform`の挙動として捉えることができます。