UGUI中Canvas Scaler的功能說明

在工作中用到Canvas Scaler組件時酌予,只使用到了UIScaleMode為ScaleWithScreenSize選項的情況磺箕。今天把這塊的源碼仔細看了一下,并且參考了這篇GAD的文章抛虫,終于弄懂了一些松靡,下面分享一下自己的學習心得杭抠。

tips:建議讀者也閱讀一下上文提到的文章旨剥,因為這篇文章沒有分析過程削祈。

CanvasScaler是一個控制Canvas縮放的組件,共分為以下四種模式:

  1. Constant Pixel Size 固定像素大小望几,此時使用ScaleFactor值直接控制Canvas的Scale值肛搬。
  2. Scale With Screen Size 按照屏幕大小進行縮放弱睦,會根據(jù)Resolution大小與當前屏幕大小以及三種縮放策略控制Canvas的Scale值罢艾。
  3. Constant Physical Size 固定物理大小,此時根據(jù)設(shè)置的物理單位直接控制Canvas的Scale值笛洛。
  4. World 當Canvas的RenderMode為WorldSpace時會顯示這種模式夏志,此時CanvasScaler不能控制Canvas的縮放。

在詳細說明四種模式之前苛让,我想先提出一個概念:UGUI單位(或簡稱UI單位)沟蔑。UI單位指的是你在設(shè)置UI組件大小時,所使用的單位狱杰。
例如下圖中瘦材,我會把960和640的單位稱為UI單位,我認為有這樣一個名詞對于理解接下來的內(nèi)容有很大幫助仿畸。

有了上面的定義之后食棕,我們可以得到:當UI組件的根Canvas的Scale為1時,1UI單位的大小為1像素颁湖。

Const Pixel Size 固定像素大小

在這種模式下宣蠕,我們的UI組件在不同大小的屏幕上例隆,會占用相同數(shù)量的像素甥捺。當ScaleFactor的值為x時,一個(100,100)的Image镀层,在任何屏幕上镰禾,都會占用100x*100x 個像素,即1UI單位等于x個像素唱逢。
在采用這種模式的情況下吴侦,同樣的UI在手機上會比在電腦上小很多,因為手機屏幕的像素密度要遠遠大于電腦(即手機屏幕的DPI大于電腦屏幕)

Constant Physical Size 固定物理大小

在這種模式下坞古,我們的UI組件在不同大小的屏幕上备韧,會占用相同大小的物理長度。假如我們設(shè)置PhysicalUnit的值為Inches痪枫,則我們的Canvas的Scale值會被設(shè)置為96(我電腦屏幕的DPI為96)织堂,此時1UI單位等于96個像素(即1英寸)。
舉例來說奶陈,當你有一張你手掌的圖片易阳,你希望在任意的設(shè)備上,這張手掌圖片都可以和你的手掌完全重合吃粒,可以采用這種模式潦俺。

這種模式下的兩個參數(shù):Fallback Screen DPI和DefaultSpriteDPI,是Unity無法取得屏幕的DPI值時的默認值和Sprite DPI默認值。當無法取得當前設(shè)備屏幕時事示,使用FallbackScreenDPI早像,而Sprite DPI是描述你使用圖片的DPI,對于這個值我現(xiàn)在不能很好的理解肖爵,目前我認為這個值與FallbackScreenDPI設(shè)置為相同的比較好扎酷,如果有錯誤的話還希望告知。
實現(xiàn)代碼如下:

        protected virtual void HandleConstantPhysicalSize()
        {
            float currentDpi = Screen.dpi;
            float dpi = (currentDpi == 0 ? m_FallbackScreenDPI : currentDpi);
            float targetDPI = 1;
            switch (m_PhysicalUnit)
            {
                case Unit.Centimeters: targetDPI = 2.54f; break;
                case Unit.Millimeters: targetDPI = 25.4f; break;
                case Unit.Inches:      targetDPI =     1; break;
                case Unit.Points:      targetDPI =    72; break;
                case Unit.Picas:       targetDPI =     6; break;
            }

            SetScaleFactor(dpi / targetDPI);
            SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit * targetDPI / m_DefaultSpriteDPI);
        }

Scale With Screen Size 根據(jù)屏幕大小縮放

在這種模式下遏匆,Canvas的Scale值會根據(jù)屏幕的實際大小與預設(shè)屏幕大小的比例進行縮放法挨,縮放的策略分為三種:

  1. 根據(jù)長寬的Match值進行調(diào)整
  2. 使用長寬中最小的比例進行縮放
  3. 使用長寬中最大的比例進行縮放

具體的實現(xiàn)代碼如下:

        protected virtual void HandleScaleWithScreenSize()
        {
            Vector2 screenSize = new Vector2(Screen.width, Screen.height);

            float scaleFactor = 0;
            switch (m_ScreenMatchMode)
            {
                case ScreenMatchMode.MatchWidthOrHeight:
                {
                    // We take the log of the relative width and height before taking the average.
                    // Then we transform it back in the original space.
                    // the reason to transform in and out of logarithmic space is to have better behavior.
                    // If one axis has twice resolution and the other has half, it should even out if widthOrHeight value is at 0.5.
                    // In normal space the average would be (0.5 + 2) / 2 = 1.25
                    // In logarithmic space the average is (-1 + 1) / 2 = 0
                    float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
                    float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
                    float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
                    scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);
                    break;
                }
                case ScreenMatchMode.Expand:
                {
                    scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
                    break;
                }
                case ScreenMatchMode.Shrink:
                {
                    scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
                    break;
                }
            }

            SetScaleFactor(scaleFactor);
            SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
        }

World 世界模式

World模式下不能設(shè)置Canvas的Scale值,此時能設(shè)置的一個值為Dynamic Pixels Per Unit幅聘。這個值目前看來是控制Text在世界UI下時顯示的清晰度的凡纳,該值越大,相同Text顯示的越清晰帝蒿。應(yīng)該說Unity在這里的實現(xiàn)比較詭異的(在我的理解中不應(yīng)該放在Canvas Scaler里面)荐糜。


另外,所有的模式下都有一個可設(shè)置的值叫做Reference Pixels Per Unit葛超。這個值是用來描述在NativeSize下1UI單位暴氏,應(yīng)該對應(yīng)多少Sprite中的像素的。
例如 Reference Pixels Per Unit 設(shè)置為200 則一個100x100的圖片放入Image組件绣张,點擊SetNativeSize之后答渔,得到的長寬是(200,200)
具體代碼如下:

        public float pixelsPerUnit
        {
            get
            {
                float spritePixelsPerUnit = 100;
                if (sprite)
                    spritePixelsPerUnit = sprite.pixelsPerUnit;

                float referencePixelsPerUnit = 100;
                if (canvas)
                    referencePixelsPerUnit = canvas.referencePixelsPerUnit;

                return spritePixelsPerUnit / referencePixelsPerUnit;
            }
        }
        public override void SetNativeSize()
        {
            if (overrideSprite != null)
            {
                float w = overrideSprite.rect.width / pixelsPerUnit;
                float h = overrideSprite.rect.height / pixelsPerUnit;
                rectTransform.anchorMax = rectTransform.anchorMin;
                rectTransform.sizeDelta = new Vector2(w, h);
                SetAllDirty();
            }
        }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侥涵,隨后出現(xiàn)的幾起案子沼撕,更是在濱河造成了極大的恐慌,老刑警劉巖芜飘,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件务豺,死亡現(xiàn)場離奇詭異,居然都是意外死亡嗦明,警方通過查閱死者的電腦和手機笼沥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娶牌,“玉大人奔浅,你說我怎么就攤上這事∪瓜罚” “怎么了乘凸?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長累榜。 經(jīng)常有香客問我营勤,道長灵嫌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任葛作,我火速辦了婚禮寿羞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赂蠢。我一直安慰自己绪穆,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布虱岂。 她就那樣靜靜地躺著玖院,像睡著了一般。 火紅的嫁衣襯著肌膚如雪第岖。 梳的紋絲不亂的頭發(fā)上难菌,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音蔑滓,去河邊找鬼郊酒。 笑死,一個胖子當著我的面吹牛键袱,可吹牛的內(nèi)容都是我干的燎窘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蹄咖,長吁一口氣:“原來是場噩夢啊……” “哼褐健!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起比藻,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤铝量,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后银亲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡纽匙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年务蝠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烛缔。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡馏段,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出践瓷,到底是詐尸還是另有隱情院喜,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布晕翠,位于F島的核電站喷舀,受9級特大地震影響砍濒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硫麻,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一爸邢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拿愧,春花似錦杠河、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至柳洋,卻和暖如春陪白,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膳灶。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工咱士, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轧钓。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓序厉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親毕箍。 傳聞我的和親對象是個殘疾皇子弛房,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容