**問題描述:**在項目開發中,使用到了剛體, 在搭建地圖過程中,發現兩個相鄰的磚塊,即使貼合的再緊密,但星星人在上面走動的時候還是會有很大概率發生卡頓(被兩個剛體的邊界處卡住)。為了解決這個問題,我們小組對磚塊進行了分類處理。
**處理過程如下:**將所有左上角的磚塊(左邊和上邊都不是磚塊的磚塊)當做特殊磚塊(specialBlock),其余的磚塊是普通磚塊(normalBlock),對特殊磚塊進行特殊操作,使其的碰撞框范圍覆蓋所有的相鄰磚塊,特殊磚塊的定義如下:
被圈起來的磚塊就屬於特殊磚塊,參與碰撞范圍的繪制,其他的磚塊則只是普通精靈,沒有剛體組件(RigidBody)和碰撞體組件(collider)
實現代碼如下:
## 正確繪制特殊磚塊的碰撞范圍,並將points所需要的點存入到this.blockColliderItemArr 中,以備使用
```
/**
* 正確繪制特殊磚塊的碰撞范圍,並將points所需要的點存入到this.blockColliderItemArr 中,以備使用
* @param element 特殊磚塊本體
* @param i 特殊磚塊的橫坐標
* @param j 特殊磚塊的縱坐標
* @param type 是朝哪個方向遍歷 1 下 2 右 3 上 4左
* this.blockArr = []; // 這個數組用來放置被繪制過的磚塊
* this.blockColliderItemArr = []; // 一個完整的碰撞盒的坐標點
*/
setColliderRange: function (element, i, j, type) {
var temp = [i, j];
if (type !== 1 && type !== 2 && this.nowBlock[0] === i && this.nowBlock[1] === j) {
// 又回到了起點,那么就結束
// console.log("回到結束點了");
if (type === 3 && j + 1 !== constants.mapEditProperties.column && (this.mapData[i][j + 1].type === constants.elementValue.BLOCK ||
this.mapData[i][j + 1].type === constants.elementValue.BUTTONM)) {
// console.log("經過起始點去了右邊");
this.calculateColliderPoints(i, j, 3);
this.setColliderRange(element, i, j + 1, 2);
}
if (type === 3) {
this.blockColliderItemArr.push(cc.p(constants.itemSize.blockColliderSize.x / 2, constants.itemSize.blockColliderSize.y / 2));
}
return;
}
switch (type) {
case 1:
// 下
if (j !== 0 && (this.mapData[i][j - 1].type === constants.elementValue.BLOCK || this.mapData[i][j - 1].type === constants.elementValue.BUTTONM)) {
// 計算要存儲的點坐標
this.calculateColliderPoints(i, j, 1);
// 左
this.setColliderRange(element, i, j - 1, 4);
} else if (i + 1 !== constants.mapEditProperties.row && (this.mapData[i + 1][j].type === constants.elementValue.BLOCK ||
this.mapData[i + 1][j].type === constants.elementValue.BUTTONM)) {
// 下
this.setColliderRange(element, i + 1, j, 1);
} else if (j + 1 !== constants.mapEditProperties.column && (this.mapData[i][j + 1].type === constants.elementValue.BLOCK ||
this.mapData[i][j + 1].type === constants.elementValue.BUTTONM)) {
// 右
this.calculateColliderPoints(i, j, 2);
this.setColliderRange(element, i, j + 1, 2);
} else {
// 返回 上
this.calculateColliderPoints(i, j, 2);
this.calculateColliderPoints(i, j, 3);
this.setColliderRange(element, i - 1, j, 3);
}
break;
case 2:
// 右
if (i + 1 !== constants.mapEditProperties.row && (this.mapData[i + 1][j].type === constants.elementValue.BLOCK ||
this.mapData[i + 1][j].type === constants.elementValue.BUTTONM)) {
// 下
this.calculateColliderPoints(i, j, 2);
this.setColliderRange(element, i + 1, j, 1);
} else if (j + 1 !== constants.mapEditProperties.column && (this.mapData[i][j + 1].type === constants.elementValue.BLOCK ||
this.mapData[i][j + 1].type === constants.elementValue.BUTTONM)) {
// 右
this.setColliderRange(element, i, j + 1, 2);
} else if (i !== 0 && (this.mapData[i - 1][j].type === constants.elementValue.BLOCK || this.mapData[i - 1][j].type === constants.elementValue.BUTTONM)) {
// 上
this.calculateColliderPoints(i, j, 3);
this.setColliderRange(element, i - 1, j, 3);
} else {
// 返回 左
this.calculateColliderPoints(i, j, 3);
this.calculateColliderPoints(i, j, 4);
this.setColliderRange(element, i, j - 1, 4);
}
break;
case 3:
// 上
if (j + 1 !== constants.mapEditProperties.column && (this.mapData[i][j + 1].type === constants.elementValue.BLOCK || this.mapData[i][j + 1].type === constants.elementValue.BUTTONM)) {
this.calculateColliderPoints(i, j, 3);
// 右
this.setColliderRange(element, i, j + 1, 2);
} else if (i !== 0 && (this.mapData[i - 1][j].type === constants.elementValue.BLOCK || this.mapData[i - 1][j].type === constants.elementValue.BUTTONM)) {
// 上
this.setColliderRange(element, i - 1, j, 3);
} else if (j !== 0 && (this.mapData[i][j - 1].type === constants.elementValue.BLOCK || this.mapData[i][j - 1].type === constants.elementValue.BUTTONM)) {
// 左
this.calculateColliderPoints(i, j, 4);
this.setColliderRange(element, i, j - 1, 4);
} else {
// 返回 下
this.calculateColliderPoints(i, j, 4);
this.calculateColliderPoints(i, j, 1);
this.setColliderRange(element, i + 1, j, 1);
}
break;
case 4:
// 左
if (i !== 0 && (this.mapData[i - 1][j].type === constants.elementValue.BLOCK || this.mapData[i - 1][j].type === constants.elementValue.BUTTONM)) {
this.calculateColliderPoints(i, j, 4);
// 上
this.setColliderRange(element, i - 1, j, 3);
} else if (j !== 0 && (this.mapData[i][j - 1].type === constants.elementValue.BLOCK || this.mapData[i][j - 1].type === constants.elementValue.BUTTONM)) {
// 左
this.setColliderRange(element, i, j - 1, 4);
} else if (i + 1 !== constants.mapEditProperties.row && (this.mapData[i + 1][j].type === constants.elementValue.BLOCK || this.mapData[i + 1][j].type === constants.elementValue.BUTTONM)) {
// 下
this.calculateColliderPoints(i, j, 1);
// 特殊處理,如果是做下的話,把這個點放入到blockArr中
if (!this.blockArr.includes(temp)) {
this.blockArr.push(temp);
}
this.setColliderRange(element, i + 1, j, 1);
} else {
// 返回 右
// 特殊處理,如果是做下的話,把這個點放入到blockArr中
if (!this.blockArr.includes(temp)) {
this.blockArr.push(temp);
}
this.calculateColliderPoints(i, j, 1);
this.calculateColliderPoints(i, j, 2);
this.setColliderRange(element, i, j + 1, 2);
}
break;
default:
break;
}
},
```
###### calculateColliderPoints()方法用來計算單個碰撞體的點坐標,並將得到的點坐標添加到this.blockColliderItemArr數組中
```
/**
* 計算單個碰撞體的點坐標,並將其添加到this.blockColliderItemArr中
* @param i 當前點的橫坐標
* @param j 當前坐標的縱坐標
* @param type 要取的點的類型 1:左上 2:左下 3:右下 4:右上
* this.nowblock 存儲的特殊磚塊的相對坐標
*/
calculateColliderPoints: function (x, y, type) {
var temp = cc.p();
switch (type) {
case 1:
if (y >= this.nowBlock[1]) {
temp.x = (Math.abs(y - this.nowBlock[1]) - (1 / 2)) * constants.itemSize.blockColliderSize.x;
} else {
temp.x = (-Math.abs(y - this.nowBlock[1]) - (1 / 2)) * constants.itemSize.blockColliderSize.x;
}
if (x > this.nowBlock[0]) {
temp.y = (-Math.abs(this.nowBlock[0] - x) + (1 / 2)) * constants.itemSize.blockColliderSize.y;
} else {
temp.y = (Math.abs(this.nowBlock[0] - x) + (1 / 2)) * constants.itemSize.blockColliderSize.y;
}
break;
case 2:
if (y >= this.nowBlock[1]) {
temp.x = (Math.abs(y - this.nowBlock[1]) - (1 / 2)) * constants.itemSize.blockColliderSize.x;
} else {
temp.x = (-Math.abs(y - this.nowBlock[1]) - (1 / 2)) * constants.itemSize.blockColliderSize.x;
}
if (x > this.nowBlock[0]) {
temp.y = (-Math.abs(this.nowBlock[0] - x) - (1 / 2)) * constants.itemSize.blockColliderSize.y;
} else {
temp.y = (Math.abs(this.nowBlock[0] - x) - (1 / 2)) * constants.itemSize.blockColliderSize.y;
}
break;
case 3:
if (y >= this.nowBlock[1]) {
temp.x = (Math.abs(y - this.nowBlock[1]) + (1 / 2)) * constants.itemSize.blockColliderSize.x;
} else {
temp.x = (-Math.abs(y - this.nowBlock[1]) + (1 / 2)) * constants.itemSize.blockColliderSize.x;
}
if (x > this.nowBlock[0]) {
temp.y = (-Math.abs(this.nowBlock[0] - x) - (1 / 2)) * constants.itemSize.blockColliderSize.y;
} else {
temp.y = (Math.abs(this.nowBlock[0] - x) - (1 / 2)) * constants.itemSize.blockColliderSize.y;
}
break;
case 4:
if (y >= this.nowBlock[1]) {
temp.x = (Math.abs(y - this.nowBlock[1]) + (1 / 2)) * constants.itemSize.blockColliderSize.x;
} else {
temp.x = (-Math.abs(y - this.nowBlock[1]) + (1 / 2)) * constants.itemSize.blockColliderSize.x;
}
if (x > this.nowBlock[0]) {
temp.y = (-Math.abs(this.nowBlock[0] - x) + (1 / 2)) * constants.itemSize.blockColliderSize.y;
} else {
temp.y = (Math.abs(this.nowBlock[0] - x) + (1 / 2)) * constants.itemSize.blockColliderSize.y;
}
break;
default:
break;
}
temp.x = parseFloat(temp.x.toFixed(1));
temp.y = parseFloat(temp.y.toFixed(1));
var i = 0;
for (; i < this.blockColliderItemArr.length; i++) {
if (this.blockColliderItemArr[i].x === temp.x && this.blockColliderItemArr[i].y === temp.y) {
console.log("這個點陣中已經存在這個點了");
return;
}
}
this.blockColliderItemArr.push(temp);
},
```
最后,將計算出來的結果賦值給碰撞框的points屬性:
上圖中的points數組,這個數組決定了你的碰撞體的碰撞范圍,將計算出的頂點數組賦值給這個屬性。
代碼如下:
##### 帶來的收益:
1. 完美解決了星星人在磚塊上發生卡頓的問題
2. 大大減少了剛體精靈的繪制數量,對性能有一定的提升