先前在第三章畫樹時,就有發現把樹葉畫上去時,系統工作時間會增加而導致掉偵,原圖是300x300,50kb左右,不是很大,但是我們要畫一大堆落葉的話,計算量就會很大,因為影像處理的底層就是去運算300x300個像素點,然後再根據要求的大小,比如50x50,再進行縮放,而context.drawImage
方法每次都會經歷這一個步驟,因此重複多次相當花費效能,為此我們有兩種方法做搭配:
預估每個葉子最大的大小為100x100像素,我們就依此來裁切原檔,在修圖時有個重點,因為在Canvas在繪製圖形時,都是以左上角為準,如果我們的素材不對齊左上角,就還要額外去做平移和計算,因此為了JS處理具有一致性和方便性,我們把所有葉子的根部都對齊左上:
接著就是基本操作:拉輔助線、旋轉縮放、調整版面尺寸
值得注意的是,100x100和300x300的大小可是差了約9倍,相當可觀,即使調整微小,也是會省去不少效能,實際上裁切完的大小是:50kb >>> 12kb,接著再進一步拿去影像壓縮:
又變得更小,剩下4kb了!不過提醒大家,一定要保留原檔,因為所謂的壓縮,本質上是一種對於檔案的毀損,同時它也失去再修圖的可能,只不過是肉眼看不太出來而已:
在樹建立之初在Stick的原型上設置min變數為20,如此以來每一個節點都可以由node.min來取得該變數,而不會被其他無關的物件來取用到:Stick.prototype.min = 20;
if(node.r < node.min){
// 在樹枝建立之初就有設置過node.img,但為了避免pngImg存有的canvas還未被建立,而重新指向
// 也可以在更源頭的地方解決,即圖片全部載入完畢,才開始遊戲
if(node.img == undefined) node.img = pngImg[1 + Math.floor(random(2))];
else{
context.strokeStyle = 'rgba(120, 215, 140, 1)';
context.save();
context.translate(x, y);
context.rotate(-Math.PI / 4 - theta2);
context.drawImage(node.img, 0, 0,
node.r * node.grow * 1.5,
node.r * node.grow * 1.5);
context.restore();
}
}
在canvas的座標中,不指Y是反過來的,包括角度也是反過來(順時針為正),一開始原點為圖片的左上角,表示葉子面向右下角,即45度(Math.PI/4),因此要先減去該角度,再減去theta2(該節點的生長方向)
壓縮圖片之後,對效能的確有幫助,但似乎不太夠,畢竟大約有近千片樹葉等著繪製,在console中可觀察到Tree.Draw()花了特別多的時間,其範圍為22.74-31.24秒不等,會造成嚴重的掉偵問題:
因此我們先嘗試剛剛說的先將圖片進行壓縮,放到更小的畫布上:
let leafImg = new Array();
let pngImg = new Array();
for(let N = 0; N < 4; N++){
leafImg[N] = new Image();
// 等待每一張圖片讀取好
leafImg[N].onload = () => {
// 每張圖片創建一個對應的畫布
pngImg[N] = document.createElement('canvas');
pngImg[N].width = 30;
pngImg[N].height = 30;
let ctx = pngImg[N].getContext("2d");
// 畫一次就可以了,以後就拿pngImg[N]來當圖片(N為0~3之間)
ctx.drawImage(leafImg[N], 0, 0, 30, 30);
}
}
leafImg[0].src = "../images/tinyPng/Leave2O.png";
leafImg[1].src = "../images/tinyPng/Leave2Y.png";
leafImg[2].src = "../images/tinyPng/LeaveRY.png";
leafImg[3].src = "../images/tinyPng/LeaveRR.png";
// 設定完檔案路徑網頁端才會開始讀取,接著觸發剛剛寫的onload
實際嘗試過後,似乎對於效能沒有顯著的幫助,至少還是需要21.64秒,僅快了1秒左右
不過值得注意的是,不只是樹葉花了很多時間,其實當初在設計並繪製樹枝時,也浪費了很多效能,如果把繪製樹枝的貝茲曲線去掉,只保留節點的計算和樹葉,便會發現時間落在11.03-13.34秒左右:
由此可知,演算約1400個樹枝花了近10秒,演算約1000個樹葉花了近12秒,算是相當接近了,
而只繪製樹葉的效能表現也是好很多,因此之後設計遊戲的時候,會考慮只有在遊戲讀取畫面時,會有讓樹長大動畫,而在玩遊戲時,只有一個靜置的樹在後面(可能是另外一個canvas),不做重複繪製的動作。
當然,我們也可以把RATIO改回1,事情就會單純很多:
(更正:由於Ratio改變,min=20就從原本的10px變成20px,使得樹葉少遞迴1~2次,以下兩個例子實際節點只有450個,約為原本的1/3)
相較之下,就沒什麼效能問題,不過就沒有原本想要的高解析度了,畢竟少了Ratio增加畫布大小,就只是單純拿30x30的圖片去放大
再想想看怎麼辦吧!以下是用RATIO=2繪製出來的,是真的比上面兩張圖精緻好看很多。
今天都就先用現有素材來做,不特別去找紅花綠葉了xd,
不知道大家覺得哪個比較好看呢?