cocos creator螢幕適配的一些知識點

悲歡離合的圓缺發表於2020-10-13

一. cocos creator 提供的幾種適配策略

  • EXACT_FIT

   整個應用程式在指定區域可見,無需嘗試保留原始縱橫比。可能會出現失真,應用程式會被拉伸或壓縮。也就是說設計解析度的長和寬不會等比縮放,它們會各自進行縮放,設計解析度的寬會用 “螢幕寬/設計解析度寬” 作為縮放因子,設計解析度的高會用 “螢幕高/設計解析度高” 作為縮放因子。(2.0版本這個策略有問題,等修復)

  • NO_BORDER

   整個應用程式填充指定區域,沒有失真,但可能會有一些裁剪,同時保持原樣應用程式的縱橫比。這個是等比進行縮放設計解析度,取 “螢幕寬/設計解析度寬” “螢幕高/設計解析度高” 中較大的一個作為縮放因子,比如:“螢幕寬/設計解析度寬”是2,“螢幕高/設計解析度高”是1,那麼取2作為縮放因子,這個時候高放大兩倍,自然超出螢幕之外看不見了。

  • SHOW_ALL

   整個應用程式在指定區域可見而不失真,同時保持原樣應用程式的縱橫比。邊界可以出現在應用程式的兩側。這個是等比進行縮放設計解析度,取 “螢幕寬/設計解析度寬” “螢幕高/設計解析度高” 中較小的一個作為縮放因子,比如上述的那個例子,取1作為縮放因子,寬只放大一倍,明顯不夠,雖然整個應用程式都能看到,但會有黑邊。

  • FIXED_HEIGHT

   應用程式採用設計解析度大小的高度並修改內部的寬度畫布使其適合裝置的縱橫比,不會發生失真。這個是等比進行縮放設計解析度,保持設計解析度的高度不變,根據螢幕解析度改變設計解析度的寬度,這個時候設計解析度和螢幕解析度一樣了,再進行等比縮放。

  • FIXED_WIDTH

   這個和 FIXED_HEIGHT 類似,唯一不同的是它是保持的寬度不變。

  是否等比縮放 寬的縮放比例 高的縮放比例 是否改變設計解析度
EXACT_FIT 螢幕寬/設計寬 螢幕高/設計高
NO_BORDER 較大的比 較大的比
SHOW_ALL 較小的比 較小的比
FIXED_HEIGHT 隨便取,因為兩個比一樣 隨便取,因為兩個比一樣
FIXED_WIDTH 隨便取,因為兩個比一樣 隨便取,因為兩個比一樣

 

 

 

 

 

 

 

 

 

 

 

二. cocos提供的幾個獲取View的函式

  • cc.view.getDesignResolutionSize()

   獲取的是你在編輯器中設計的解析度,也就是canvas 元件下設定的設計解析度。

  • cc.view.getFrameSize()

   獲取各種手機、pad上的螢幕解析度,也就是硬體解析度。

  • cc.view.getVisibleSizeInPixel()

   獲取的是 visibleSize 的基礎上乘以各種適配策略下的縮放比例後的解析度。

  • cc.view.getVisibleSize()

   官方文件上說返回檢視視窗可見區域尺寸,經過輸出對比發現,這個可見區域尺寸實際上是指設計解析度,不過它返回的是經過各種適配策略後的設計解析度,在EXACT_FIT,NO_BORDER,SHOW_ALL中因為不改變設計解析度,所以和cc.view.getDesignResolutionSize()返回的結果一樣。但在FIXED_HEIGHT,FIXED_WIDTH中改變了設計解析度,所以返回的是修改後的設計解析度。

後續通過檢視creator原始碼也證實了這一點,以下是原始碼解釋:

 1 /**
 2      * !#en
 3      * Returns the visible area size of the view port.
 4      * !#zh 返回檢視視窗可見區域尺寸。
 5      * @method getVisibleSize
 6      * @return {Size}
 7      */
 8     getVisibleSize: function () {
 9         return cc.size(this._visibleRect.width,this._visibleRect.height);
10     },
getVisibleSize()返回的是this._visbleRect的值,我們來找找this._visbleRect在哪進行賦值

 1 cc.js.mixin(View.prototype, {
 2     init () {
 3         .........
 4 
 5         var w = cc.game.canvas.width, h = cc.game.canvas.height;
 6         this._designResolutionSize.width = w;
 7         this._designResolutionSize.height = h;
 8         this._originalDesignResolutionSize.width = w;
 9         this._originalDesignResolutionSize.height = h;
10         this._viewportRect.width = w;
11         this._viewportRect.height = h;
12         this._visibleRect.width = w;
13         this._visibleRect.height = h;
14 
15         cc.winSize.width = this._visibleRect.width;
16         cc.winSize.height = this._visibleRect.height;
17         ......
18     },
19 ........
20 }

可以看到在這裡給一些保持解析度的物件賦了同一個值,那就是cc.game.canvas裡面的值,這個值代表的是螢幕解析度,因為後續進行適配策略是用的也是這個值,這就奇怪了,都一樣了還怎麼比較,每個函式返回的值也應該一樣呀,所以一定有一個地方改變了它們,繼續找。

 

 1 setDesignResolutionSize: function (width, height, resolutionPolicy) {
 2         .........
 8         this.setResolutionPolicy(resolutionPolicy);
 9         var policy = this._resolutionPolicy;
10         if (policy) {
11             policy.preApply(this);
12         }
13 
14        ..........28 
29         this._originalDesignResolutionSize.width = this._designResolutionSize.width = width;
30         this._originalDesignResolutionSize.height = this._designResolutionSize.height = height;
31 
32         var result = policy.apply(this, this._designResolutionSize);
33 
34         if(result.scale && result.scale.length === 2){
35             this._scaleX = result.scale[0];
36             this._scaleY = result.scale[1];
37         }
38 
39         if(result.viewport){
40             var vp = this._viewportRect,
41                 vb = this._visibleRect,
42                 rv = result.viewport;
43 
44             vp.x = rv.x;
45             vp.y = rv.y;
46             vp.width = rv.width;
47             vp.height = rv.height;
48 
49             vb.x = 0;
50             vb.y = 0;
51             vb.width = rv.width / this._scaleX;
52             vb.height = rv.height / this._scaleY;
53         }
54 
55         policy.postApply(this);
56         cc.winSize.width = this._visibleRect.width;
57         cc.winSize.height = this._visibleRect.height;
58      ........64     },

我們直接來看setDesignResolutionSize()這個函式,它是通過設定設計解析度和匹配模式來進行遊戲畫面的螢幕適配。所以它必然會改變相應的值。可以看到第29,30行把我們傳進來的設計解析度賦值給了this._originalDesignResolutionSize和this._designResolutionSize,改變了它們的值。那麼什麼時候改變this._visbleRect的值呢,我們看39行到52行,很明顯就是在這裡改變的。vb.width = rv.width / this._scaleX; vb.height = rv.height / this._scaleY;

 

那麼憑什麼說this._visbleRect是適配策略後到設計解析度呢,我們來看看result.viewport裡面是什麼東西,var result = policy.apply(this, this._designResolutionSize);policy是一個適配策略的例項,它根據你傳入的適配策略來決定呼叫哪個適配策略的方法,類似於c++中的多型。然後我們來看看每個適配策略做了什麼,這裡主要就拿SHOW_ALL和FIXED_HEIGHT來加以說明:

 1 var ShowAll = cc.Class({
 2         name: "ShowAll",
 3         extends: cc.ContentStrategy,
 4         apply: function (view, designedResolution) {
 5             var containerW = cc.game.canvas.width, containerH = cc.game.canvas.height,
 6                 designW = designedResolution.width, designH = designedResolution.height,
 7                 scaleX = containerW / designW, scaleY = containerH / designH, scale = 0,
 8                 contentW, contentH;
 9 
10             scaleX < scaleY ? (scale = scaleX, contentW = containerW, contentH = designH * scale)
11                 : (scale = scaleY, contentW = designW * scale, contentH = containerH);
12 
13             return this._buildResult(containerW, containerH, contentW, contentH, scale, scale);
14         }
15     });
16 
17 var FixedHeight = cc.Class({
18         name: "FixedHeight",
19         extends: cc.ContentStrategy,
20         apply: function (view, designedResolution) {
21             var containerW = cc.game.canvas.width, containerH = cc.game.canvas.height,
22                 designH = designedResolution.height, scale = containerH / designH,
23                 contentW = containerW, contentH = containerH;
24 
25             return this._buildResult(containerW, containerH, contentW, contentH, scale, scale);
26         }
27     });

可以看到它們都繼承於cc.ContentStrategy。在apply函式中返回值來自於this._buildResult()函式,我們來看這個函式:

 1 _buildResult: function (containerW, containerH, contentW, contentH, scaleX, scaleY) {
 2         // Makes content fit better the canvas
 3         Math.abs(containerW - contentW) < 2 && (contentW = containerW);
 4         Math.abs(containerH - contentH) < 2 && (contentH = containerH);
 5 
 6         var viewport = cc.rect((containerW - contentW) / 2, (containerH - contentH) / 2, contentW, contentH);
 7 
 8         // Translate the content
 9         if (cc.game.renderType === cc.game.RENDER_TYPE_CANVAS){
10             //TODO: modify something for setTransform
11             //cc.game._renderContext.translate(viewport.x, viewport.y + contentH);
12         }
13 
14         this._result.scale = [scaleX, scaleY];
15         this._result.viewport = viewport;
16         return this._result;
17     },

主要來看this._result中的viewport的值,它的width是contentW,height是contentH。到這裡,一切都清楚了。我們假設螢幕解析度的寬高是Pw和Ph,設計解析度的寬高是Sw和Sh。

那麼在SHOW_ALL中:假設Pw/Sw更小,contentW=Pw,contentH=Sh*(Pw/Sw),在進行this._visbleRect賦值時,width = Pw/(Pw/Sw) = Sw,height = (Sh*(Pw/Sw))/(Pw/Sw) = Sh,所以設計解析度沒有改變。

在FIXED_HEIGHT中:contentW=Pw,contentH=Ph,scale = Ph/Sh,在進行this._visbleRect賦值時,width = Pw/(Ph/Sh) = Sh*(Pw/Ph),height = Ph/(Ph/Sh) = Sh,所以設計解析度是改變的。

從上述程式碼還發現cc.winSize和cc.view.getVisibleSize()的返回值是一樣的,二者可以通用。

 

在進行螢幕適配時主要用到cc.view.getDesignResolutionSize()和cc.view.getFrameSize()兩個函式。

相關文章