<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Cocos Creator 的 UI 多端適配之道

          共 4832字,需瀏覽 10分鐘

           ·

          2021-10-10 18:23

          前端同學通常都用媒體查詢或 rem 做多端適配,但是在 Cocos Creator 上 CSS 不復(fù)存在。本文從需求背景出發(fā),帶你領(lǐng)略 Cocos Creator 的多端適配之道。

          背景

          某一天接到了新需求,自己看了設(shè)計同學給的設(shè)計稿后瞬間感覺頭大,分析了下主要有以下難點:

          1. 題目背景需為同一張背景圖,在不同端上要顯示背景圖的不同區(qū)域

          2. 標題欄上的倒計時、題干與最小化按鈕的貼邊距離在各端各不相同

          3. 選項背景圖需根據(jù)選項長度自動拉伸,同時保證兩側(cè)圓角不被拉伸

          如果這種適配方案采用 CSS 實現(xiàn)的話,肯定少不了一大堆的媒體查詢,作為前端同學來說 CSS 實現(xiàn)肯定更為熟悉,但這也會導(dǎo)致樣式代碼冗長繁瑣,對開發(fā)者來說是一種折磨。業(yè)務(wù)中這幾年引進了 Cocos Creator 游戲引擎來實現(xiàn)新題型,曾經(jīng)我們那樣熟悉的 CSS 在 Cocos Creator 中將不復(fù)存在,這時在 Cocos?Creator 上我們要如何實現(xiàn)這種多端適配呢?


          分析

          針對這個需求,我們將適配的過程拆分成以下幾點:

          1. 多端適配背景圖

          2. 多端適配貼邊節(jié)點

          3. 選項背景圖九宮格切割


          多端適配背景圖

          1. 什么是設(shè)計分辨率和屏幕分辨率?

            在 Cocos Creator 上做多端適配需要先了解什么是設(shè)計分辨率和屏幕分辨率。根據(jù)官方文檔的介紹,設(shè)計分辨率?是內(nèi)容生產(chǎn)者在制作場景時使用的分辨率藍本,而?屏幕分辨率?是游戲在設(shè)備上運行時的實際屏幕顯示分辨率。在實際開發(fā)中,設(shè)計分辨率其實就是設(shè)計同學在設(shè)計稿中使用最多的尺寸,一般來說都是 iPhone 6 的 667*375,幾乎所有的設(shè)計稿都以這個尺寸來出圖,然后才會針對不同端(PC 、iPad、iPhoneX 等)來單獨出適配的設(shè)計稿圖。所以我們在 Cocos 中 canvas 的大小通常就設(shè)置成寬為 667,高為 375 的設(shè)計分辨率,在此分辨率上完成基本的功能開發(fā)。

          2. 設(shè)計分辨率和屏幕分辨率的關(guān)系?

            假設(shè)我們的設(shè)計分辨率與屏幕分辨率同為 667 x 375,這時候 canvas 不用縮放就可以完美適配屏幕;假設(shè)我們的設(shè)計分辨率為 667 x 375,而實際屏幕分辨率為1334 x 750,這個時候 canvas 就需要放大兩倍才能夠完美適配屏幕。canvas 進行縮放時,場景下所有的節(jié)點都能夠享受到基于設(shè)計分辨率的智能縮放。

          3. Fit Height 和 Fit Width

            上一點舉出的例子中,當設(shè)計分辨率為 667 x 375 且屏幕分辨率為 1334 x 750 時,場景需要放大兩倍才能夠完美適配屏幕,但這個的前提是設(shè)計分辨率和屏幕分辨率的寬高比一致。假設(shè)設(shè)計分辨率的寬高比與屏幕分辨率的寬高比不一致(這是在多端適配下常見的問題),這個時候該怎么辦呢?canvas 組件提供了 Fit Height 與 Fit Width 兩種適配模式來幫助我們解決。該怎么理解這兩種適配模式?

            我們以下面這個場景作為基礎(chǔ)場景,紫色框為我們的設(shè)計分辨率,藍色框為實際場景:

            先看看屏幕分辨率寬高比小于設(shè)計分辨率寬高比的情況(iPad 情況)。

            我們先設(shè)置為 Fit Height 模式看看效果,會發(fā)現(xiàn)設(shè)計分辨率的高度會自動撐滿屏幕的高度,而由于屏幕分辨率寬高比比設(shè)計分辨率小,所以屏幕兩邊也會被裁掉一部分背景圖。

            我們再設(shè)置為 Fit Width 模式看看效果,會發(fā)現(xiàn)設(shè)計分辨率的寬度會自動撐滿屏幕的寬度,而由于屏幕分辨率寬高比比設(shè)計分辨率小,所以屏幕上下會多顯示一部分背景圖。

            再看看屏幕分辨率寬高比大于設(shè)計分辨率寬高比的情況(iPhoneX 情況)

            我們先設(shè)置為 Fit Height 模式看看效果,會發(fā)現(xiàn)設(shè)計分辨率的高度會自動撐滿屏幕的高度,而由于屏幕分辨率寬高比比設(shè)計分辨率大,所以屏幕兩邊也會多顯示一部分背景圖。

            我們再設(shè)置為 Fit Width 模式看看效果,會發(fā)現(xiàn)設(shè)計分辨率的寬度會自動撐滿屏幕的寬度,而由于屏幕分辨率寬高比比設(shè)計分辨率大,所以屏幕上下會被裁掉一部分背景圖。

          4. 背景多端適配用什么模式?

            經(jīng)過上面 Fit Height 與 Fit Width 兩種模式的解釋,現(xiàn)在你能確定本文第一張圖中每個端使用哪種模式進行適配嗎?在屏幕分辨率寬高比小于設(shè)計分辨率寬高比(iPad 情況)時,我們希望在寬度一致的情況下在上下兩側(cè)展示更多的背景區(qū)域,這個時候就需要使用 Fit Width;在屏幕分辨率寬高比大于設(shè)計分辨率寬高比(iPhoneX 情況)時,我們希望在高度一致的情況下在左右兩側(cè)展示更多的背景區(qū)域,這個時候就需要使用 Fit Height。

            在代碼中我們可以通過獲取當前視圖大小來得到實際屏幕分辨率的寬高比,根據(jù)寬高比來決定是使用 Fit Height 模式還是 Fit Width 模式。

            exportfunction setCanvasScaleMode(canvas: cc.Canvas) {
            const standardRadio = 16 / 9; // 標準寬高比,差不多就是iPhone6的寬高比(橫屏),一般設(shè)計稿以此為標準 const screenSize = cc.view.getFrameSize();
            const currentRadio = screenSize.width / screenSize.height; // 寬高比 if (currentRadio <= standardRadio) {
            // 偏方形的屏幕,代表是iPad之類的。
            canvas.fitHeight = false;
            canvas.fitWidth = true;
            } else {
            // 偏長的屏幕,代表是ipx。太長了,高度顯得小,所以優(yōu)先適配高度
            canvas.fitWidth = false;
            canvas.fitHeight = true;
            }
            }


          5. 確定了模式之后我們還需要確保背景不會出現(xiàn)黑邊,因為當在 iPad 情況下使用 Fit Width 模式時,上下兩側(cè)會展示更多的背景區(qū)域,如果背景圖片沒有那么高的話上下兩側(cè)就會出現(xiàn)黑邊;同理當在 iPhoneX 情況下使用 Fit Height 模式時,左右兩側(cè)會展示更多的背景區(qū)域,如果背景圖片沒有那么寬的話左右兩側(cè)也會出現(xiàn)黑邊。這時我們需要設(shè)計同學提供的背景圖片時能夠覆蓋 iPad 的高度與 iPhoneX 的寬度,背景圖片應(yīng)大于設(shè)計分辨率,并在上下左右四個方向都預(yù)留一定的長度來保證背景適配時不會出現(xiàn)黑邊。

          6. 特殊情況

            細心的同學可能已經(jīng)發(fā)現(xiàn)了, PC 端與 iPhone7 端的寬高比其實是一樣的,按照我們上面的想法這兩端應(yīng)該顯示一樣的背景區(qū)域,同時由于 PC 端的寬高比 iPhone7 的寬高要大,而場景中的所有節(jié)點都能享受到基于設(shè)計分辨率的智能縮放,所以 PC 端的節(jié)點應(yīng)該比 iPhone7 的節(jié)點大。但是在第一張設(shè)計稿圖中,設(shè)計同學要求 PC 端要占據(jù)更多的背景區(qū)域,同時其中節(jié)點的大小也與 iPhone7 中節(jié)點的大小保持相同,以保證 PC 端題目顯示的美觀,這個時候我們就需要單獨對 PC 端的情況做適配,將背景圖與節(jié)點都做下縮放。


          多端適配貼邊節(jié)點

          1. Widget 組件為何物?

            Widget 組件為 Cocos 中的一個 UI 布局組件,用于將當前節(jié)點對齊到父節(jié)點的任意位置,我們通過設(shè)置 Widget 組件的各種數(shù)值可以讓節(jié)點對齊上邊界、對齊下邊界、對齊左邊界、對齊右邊界、水平方向居中和豎直方向居中。當場景中有節(jié)點需要貼邊時 Widget 組件是不二的選擇。

          1. 哪個節(jié)點作為貼邊節(jié)點對齊的父節(jié)點?

            當有節(jié)點需要貼邊時,我們希望的是無論屏幕分辨率如何改變,節(jié)點總是能在屏幕的固定位置出現(xiàn)。比如第一張設(shè)計稿圖中的倒計時節(jié)點,我們希望在不同屏幕分辨率的情況下它都能夠固定在屏幕左上角,不會出現(xiàn)隨著屏幕分辨率的改變而移到右上角的情況。所以貼邊節(jié)點指定的父節(jié)點大小要跟屏幕的大小一致,哪個節(jié)點最合適呢?

            沒錯,答案就是 canvas 節(jié)點!在我們使用 Fit Height 和 Fit Width 模式時,canvas 節(jié)點會占據(jù)屏幕的大小,這時需要貼邊的節(jié)點相對于 canvas 節(jié)點設(shè)置貼邊距離實際上就是相對屏幕設(shè)置貼邊距離。

          2. 多端貼邊距離設(shè)置

            根據(jù)設(shè)計同學的要求,貼邊節(jié)點(例如倒計時節(jié)點)在 PC 端、iPad 端、iPhoneX 端和 iPhone7 端貼邊的距離都是不一樣的,這個時候我們?nèi)绾胃鶕?jù)不同端分別設(shè)置貼邊距離呢?

            首先明確,設(shè)置多端節(jié)點的貼邊距離實際上就是更改節(jié)點 Widget 組件在各個方向上對齊的數(shù)值,例如倒計時組件,我們先判斷當前是哪一個端,然后再根據(jù)該端來改變其 Widget 組件的 top 與 left 數(shù)值。

            由于 Widget 的設(shè)置邏輯不僅需要在不同貼邊節(jié)點執(zhí)行,還需要在每個貼邊節(jié)點的不同端情況下執(zhí)行,使用場景眾多,所以我們可以把這段邏輯抽出來作為一個通用腳本組件,再分別添加到需要的節(jié)點上。以下是我們抽出來的一個 UIAdaptor 腳本組件:

            @ccclassexportdefaultclass UIAdaptor extends cc.Component {
            @property({
            type: Number,
            tooltip: `app: ${PlatformTypes.APP}, iPad: ${PlatformTypes.iPad}, PC : ${PlatformTypes. PC }, iPhoneX: ${PlatformTypes.iPhoneX}, AndroidiPad: ${PlatformTypes.AndroidiPad}`,
            })
            platform: PlatformTypes = -1;

            @property({
            type: [Number],
            tooltip: 'left, top, right, bottom, horizontalCenter, verticalCenter',
            })
            widgetOptions: Array<number> = [
            Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER,
            Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER,
            ];

            @property({
            type: Number,
            tooltip: `ONCE: ${WidgetAlignModes.ONCE}, ON_WINDOW_RESIZE: ${WidgetAlignModes.ON_WINDOW_RESIZE}
            ALWAYS: ${WidgetAlignModes.ALWAYS}
            `
            ,
            })
            widgetAlignModes = WidgetAlignModes.ON_WINDOW_RESIZE;

            @property({
            type: [Number],
            tooltip: 'width, height',
            })
            size: Array<number> = [];

            @property({
            type: [Number],
            tooltip: 'x, y',
            })
            scale: Array<number> = [];

            @property({
            type: Number,
            })
            fontSize = -1;
            // 對節(jié)點的 Widget 組件做適配 private fitWidget() {
            const widget = this.getComponent(cc.Widget);
            if (!widget) {
            return;
            }
            enum WidgetOptions {
            left,
            top,
            right,
            bottom,
            horizontalCenter,
            verticalCenter,
            }
            this.widgetOptions.forEach((value: number, index: number) => {
            if (typeof value !== 'number' || value >= Number.MAX_SAFE_INTEGER - 1) {
            return;
            }
            const optionType = WidgetOptions[index];
            widget[optionType] = value;
            widget[`isAlign${optionType.replace(optionType[0], optionType[0].toUpperCase())}`] = true;
            });

            widget.alignMode = this.widgetAlignModes;
            }
            // 對節(jié)點的大小做適配 private fitSize() {
            const [width, height] = this.size;
            this.node.setContentSize(
            width,
            height
            );
            }
            // 對節(jié)點的字體大小做適配 private fitFontSize() {
            const label = this.getComponent(cc.Label);
            label.fontSize = this.fontSize;
            }
            // 對節(jié)點的縮放做適配 private fitScale() {
            const { scaleX: originalScaleX, scaleY: originalScaleY } = this.node;
            const [x, y = x] = this.scale;
            this.node.setScale(originalScaleX * x, originalScaleY * y);
            }

            privatefit() {
            if (this.size.length) {
            this.fitSize();
            }

            if (this.fontSize >= 0) {
            this.fitFontSize();
            }

            if (this.scale.length) {
            this.fitScale();
            }

            this.fitWidget();


            const widget = this.getComponent(cc.Widget);
            if (widget) {
            widget.updateAlignment();
            }
            }

            start() {
            // 獲取當前場景所在的端 const platform = getPlatformType();
            if (platform === this.platform) {
            this.fit();
            }
            }
            }

            可以看到這個腳本組件不僅可以設(shè)置節(jié)點的 Widget 組件,還可以設(shè)置節(jié)點的 size、scale、fontSize 等,使用這一個腳本組件就可以實現(xiàn)多端下節(jié)點貼邊距離、大小、縮放等的設(shè)置。我們還是看回剛才說到的倒計時節(jié)點,使用 UIAdaptor 腳本組件后會是什么樣子?

            由于每個端上倒計時節(jié)點的貼邊距離都不相同,所以我們針對每個端都在倒計時節(jié)點上添加一個 UIAdaptor 腳本組件,填寫不同的Platform(PC 、iPhoneX、iPad、Android iPad)與 Widget options(left、top、right、bottom、horizontalCenter、verticalCenter),當場景加載執(zhí)行到這些腳本時會逐個去判斷當前場景所在的端是不是與 UIAdaptor 選擇的端一致,如果不是則跳過,如果是則根據(jù)填寫的數(shù)值進行設(shè)置。通過這種方式我們可以?無代碼?實現(xiàn)貼邊節(jié)點的多端適配。


          選項背景圖九宮格切割

          1. 為什么要對背景圖做處理?

            設(shè)計同學會給出第一張設(shè)計稿圖中選項的切圖,切圖長下面這個樣子:

            如果我們不對圖片做任何處理,直接將它扔進選項,讓它根據(jù)選項長度自動拉伸,會有什么狀況發(fā)生呢?

            可以看到,在選項長度較大的情況下,選項的背景圖展現(xiàn)出了一個很詭異的形狀,四個圓角被拉伸地很不協(xié)調(diào),如果被設(shè)計同學看到又少不了一通吐槽...我們希望的是無論選項有多長,四個圓角都能夠保持原始狀態(tài),不被選項長度所影響,而這種未經(jīng)處理的圖片顯然不符合我們的需求。

          2. 何為九宮格切割?

            為了讓開發(fā)者能夠制作可任意拉伸的 UI 圖像,Cocos Creator 中提供了針對圖像資源的九宮格切割方式。我們在 Cocos Creator 中選中圖像資源進行編輯,會出現(xiàn)一個編輯圖像的彈窗:

            在這里我們可以移動綠色線條將圖片資源切割成九部分,每個部分的拉伸規(guī)則如下:

            我們將選項按鈕的四個圓角切割到九宮格的四個角落,這樣無論選項如何拉伸,四個圓角始終能夠保持原始狀態(tài),不會因為選項長度的變化而縮放拉伸。來看看對圖像資源進行九宮格切割后的效果:

            怎么樣,選項看起來是不是比剛才協(xié)調(diào)了很多?

          3. 九宮格切割注意事項

            通常來說設(shè)計同學提供切圖時會提供切圖的一倍圖、二倍圖和三倍圖,選擇選項按鈕切圖的時候最好選擇跟設(shè)計分辨率下按鈕大小相近的倍圖。假設(shè)按鈕切圖的一倍圖高度為 44,二倍圖高度為 88,三倍圖高度為 132,而在設(shè)計分辨率下按鈕的高度為 88,這個時候我們就要選擇按鈕切圖的二倍圖。

            如果選擇一倍圖做九宮格切割,由于一倍圖的尺寸過小,四個圓角也會變得很小,如下圖:

            如果選擇三倍圖做九宮格切割,由于三倍圖尺寸過大,四個圓角也會變得很大,如下圖:


          參考文章

          多分辨率適配方案:

          https://docs.cocos.com/creator/manual/zh/ui/multi-resolution.html

          制作可任意拉伸的 UI 圖像:
          https://docs.cocos.com/creator/manual/zh/ui/sliced-sprite.html


          往期精彩

          瀏覽 239
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  白石真琴的AV成人片 | 肏逼乱伦视频 | 香蕉大伊人 | 久久久77777 | 熟女老阿V8888AV |