第一次開發小遊戲,用的是Hilo框架。由於專案開發時間比較緊張,對遊戲和CANVAS都沒有了解過。程式碼雖然寫的很爛,但是還是記錄下踩過的坑吧!本文為碎碎念模式,並不深入,寫錯的地方希望多多指點。
一、CANVAS橫屏適配處理
遊戲是微信內的一款橫屏遊戲。如果強制橫屏,提示使用者去控制橫豎屏開關並不友好。
解決方案,遊戲場景做成如下圖紫色部分結構,遊戲寬高和手機螢幕調換。如果手機為豎屏,那麼將遊戲旋轉90°即可。
注:所述【橫屏】為使用者開啟了允許橫屏的開關並橫屏,真正的橫屏。
程式碼如下所示:
let width = document.documentElement.clientWidth;
let height = document.documentElement.clientHeight;
let box = document.getElementsByTagName('canvas');
let style = '';
// 豎屏
if (width < height) {
style += `width:${height}px;`;
style += `height:${width}px;`;
style += '-webkit-transform: rotate(90deg); transform: rotate(90deg);';
// 注意旋轉中點的處理
style += `-webkit-transform-origin: ${width / 2}px ${width / 2}px;`;
style += `transform-origin: ${width / 2}px ${width / 2}px;`;
}
if (box.length) {
box[0].style.cssText = style;
}
複製程式碼
當使用者開啟了橫屏開關,如果使用者橫屏,那就將遊戲場景旋轉0°即可,也就是恢復最初的樣子。如下:
// 橫屏
if (width > height) {
style += `width:${width}px;`; // 注意旋轉後的寬高切換
style += `height:${height}px;`;
style += '-webkit-transform: rotate(0); transform: rotate(0);';
style += '-webkit-transform-origin: 0 0;';
style += 'transform-origin: 0 0;';
}
if (box.length) {
box[0].style.cssText = style;
}
複製程式碼
橫屏沒想象那麼順利,我們的遊戲是在微信場景。當使用者開啟橫屏開關並橫屏後,微信內建瀏覽器頭也會佔一大部分割槽域。這樣我們的遊戲場景旋轉後明顯是顯示不全的。
解決方案就是利用Hilo的api resize下舞臺
// 解決微信橫屏瀏覽器頭部 導致高度變化的問題
this.stage.resize(height, width, true);
複製程式碼
最後有幾個注意點:
1、注意旋轉過程中的寬高切換
2、注意單位適配問題
3、注意微信瀏覽器頭,就因為這個頭的變化。整個遊戲都需要處理,所以還是儘量不要自己處理。。。
複製程式碼
二、點選事件失效
如下圖所示,遊戲結束場景的2個按鈕。
左側旋轉90°後變成右側橫屏,但是豎屏下的橫屏(也就是旋轉90°得來的)【再來一次】按鈕點選事件會失效,但是點選紅色區域(沒有按鈕,大致繪製並不精準)部分這個時間會被觸發。
而使用者橫屏(開啟了橫屏開關,自然橫屏)【再來一次】按鈕點選事件不會失效。
繪製時座標以遊戲場景左上角為(0,0),而旋轉90°後坐標以遊戲場景左下角為(0,0)。因為旋轉90°後遊戲場景左下角變成了視覺上的左上角。因此豎屏下的橫屏點選紅色區域生效就是因為,Hilo的點選事件是繫結在元素繪製時座標區域上(猜測,沒有看原始碼)。旋轉後,按鈕的點選事件生效區間就變成了根據繪製的x、y也就是紅色區域。
那麼如何解決這個問題,如上圖所示,旋轉後的x、y如圖中藍色字所示。可以算出
// 再玩一次按鈕
const start = this.gameOverScene.getChildById('start');
// 再玩一次按鈕 新的x = 遊戲畫布寬度 - 繪製的y - 按鈕的高度
const startNewX = this.width - start.y - start.height ;
// 再玩一次按鈕 新的y = 繪製的x
const startNewY = start.x;
// 監聽舞臺點選事件
this.stage.on(Hilo.event.POINTER_START, (e) => {
// 利用新的x、y 和按鈕自身的高度和寬度 判斷是否點選在按鈕區域
if ((e.stageX > startNewX && e.stageX < startNewX + start.height) &&
(e.stageY > startNewY && e.stageY < startNewY + start.width)) {
// 在玩一次邏輯處理
}
)};
複製程式碼
【再玩一次】按鈕點選事件解決了,但是事情沒有那麼簡單。
給另一個【分享】按鈕加上事件,what?無論橫屏還是豎屏點選事件都不生效。至少【再玩一次】按鈕事件還是生效的,只是不準罷了。
原因,觀察上述圖。【分享】按鈕在初始化的過程中是在遊戲畫布右側,也就是手機螢幕外部。經過測試發現,發現繪製時在手機螢幕外的區域點選事件都不會生效。解決方法如【再玩一次】,無論橫屏還是豎屏都計算座標判斷。
三、音樂播放相容
Hilo的HTMLAudio聲音播放模組,官方文件表示【使用限制:iOS平臺需使用者事件觸發才能播放,很多Android瀏覽器僅能同時播放一個音訊。】但是目前使用來看,瀏覽器測試OK,絕大部分手機都不能正常播放。解決方案,採用DOM的audio,但是同樣iOS平臺需使用者事件觸發才能播放。因此最終的解決方案就是進入遊戲之前或者某個合適的環節獲取所有的音樂,先播放再暫停。使用者不會感知,可以完美解決。如下:
// html
<audio id="audio" src="xxx.mp3" preload="auto"></audio>
// dom為獲取
const dom = document.getElementById('audio');
dom.play();
dom.pause();
複製程式碼
四、部分機型遊戲場景顯示不全
遊戲中可能有某些元素是經常複用的,因此會單獨切出來。如下圖左側
如上圖右側所示效果,最開始的實現方式如下。在初始化的時候就將公用元素Y軸截斷展示,這個效果看似OK,但是在測試階段發現某些iPhone手機不能顯示這2個元素。
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect: [0, 100, 50, 300],
y: 0,
});
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect:[0, 150, 50, 300],
y: 0,
});
複製程式碼
檢視Hilo原始碼繪製圖片是用CanvasRenderingContext2D.drawImage
方法。
CanvasRenderingContext2D.drawImage()
是瀏覽器原生提供的在 canvas 上繪製圖片的方法。
其有以下三種引數形式(詳細用法說明及演示可見 MDN):
-
ctx.drawImage(image, dx, dy); ctx.drawImage(image, dx, dy, dWidth, dHeight); ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); 複製程式碼
引數 | 意義 |
---|---|
sx, sy | 源影象的選擇區域的偏移量 |
sWidth, sHeight | 源影象的選擇區域的寬高 |
dx, dy | 目標canvas的選擇區域的偏移量 |
dWidth, dHeight | 目標canvas的選擇區域的寬高 |
注:
- 在 Chrome 和 Firefox 下,最終的選擇區域超出源影象的部分不會被繪製。
- 在 IE 和 Edge 下,最終的選擇區域超出源影象的部分會用影象的邊界畫素來填充。
- Safari 7.1 額外要求
sx + sWidth
和sy + sHeight
不超過源影象的寬高,否則drawImage()
函式不會繪製任何影象。(未在更高版本的 Safari 上測試) - 在支援 canvas 的老版本的 Firefox 上,有著和 IE 等瀏覽器類似的對 sx, sy, sWidth, sHeight 的限制,在新版本中,這些限制已經被移除。
而我們的元素在部分機型上不能顯示就是因為觸發了第3點坑。修復程式碼如下,通過完整的繪製圖片,然後通過元素的座標來達到目標樣式。
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect: [0, 0, 50, 300],
y: -100,
});
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect:[0, 0, 50, 300],
y: -150,
});
複製程式碼
五、碰撞檢測,撞擊座標不準確
用Hilo最開始開心的一點也是碰撞檢測不需要自己寫,hitTestObject
檢測object引數指定的物件是否與其相交。因此撞擊區域可以書寫撞擊座標。
// 給如下圖中的一個多邊形實行撞擊座標
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect: [0, 0, 50, 300],
y: -100,
boundsArea: [
// 測試座標,非精準座標,圖中紅點的座標,從左到右
{x: 0, y: 0},
{x: 0, y: 100},
{x: 100, y: 100},
{x: 100, y: 200},
{x: 200, y: 200},
{x: 200, y: 100},
{x: 300, y: 100},
{x: 300, y: 0},
]
});
複製程式碼
理想中應該是如下黃色區域構成的碰撞檢測區域。
而實際卻是如下黃色區域,空氣牆???使用者反饋為什麼沒撞到就死翹翹了。(看了Hilo碰撞檢測這部分的實現原始碼,沒太看懂多邊形的處理。。)
解決辦法,類似雪碧圖使用,恩...主要是懶得切圖
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect: [0, 0, 100, 100],
y: 0,
boundsArea: [
// 測試座標,非精準座標,圖中紅點的座標,從左到右
{x: 0, y: 0},
{x: 0, y: 100},
{x: 100, y: 100},
{x: 100, y: 0},
]
});
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect: [100, 0, 100, 200],
y: 0,
boundsArea: [
// 測試座標,非精準座標,圖中紅點的座標,從左到右
{x: 0, y: 0},
{x: 0, y: 200},
{x: 100, y: 200},
{x: 100, y: 0},
]
});
new Hilo.Bitmap({
// 繪製的圖片
image: 'imgurl',
// 測試座標,非精準座標
rect: [200, 0, 100, 100],
y: 0,
boundsArea: [
// 測試座標,非精準座標,圖中紅點的座標,從左到右
{x: 0, y: 0},
{x: 0, y: 100},
{x: 100, y: 100},
{x: 100, y: 0},
]
});
複製程式碼
其實就是將1張圖裁剪成了3張圖,裁剪出來的區域撞擊座標都中規中矩。過於不規則的圖形只能儘量寫的粗糙一些。
這麼輕鬆就解決了嗎???當然NO!!!回顧下第四點,部分機型遊戲場景顯示不全。上面裁剪的3張圖裡,最後一張圖又觸發了safari的bug。o(╥﹏╥)o,所以還是乖乖切圖,或者雪碧圖留一點安全區域吧!
而且後來我才知道雪碧圖對於CANVAS來說更耗效能,還不如多切點圖呢~