摘要
有趣的物理小遊戲總能讓小夥伴們愛不釋手,而 CocosCreator 的內置物理引擎讓其開發變得更加簡單。今天 KUOKUO 就爲大家帶來物理切割,從零開始做出切割單個物體!
正文
使用版本
CocosCreator 版本 2.1.3
最終效果
層級管理
如圖,在層級管理器中有 3 部分:畫布,物理層,繪圖層。其中畫布只是作爲觸摸節點,繪圖層用於繪製紅線,物理層中放入物理剛體。腳本有三個,其中的 setting 僅僅是開啓物理和物理繪製,cut-item 腳本用於繪製剛體,下面給出代碼。
draw 方法會根據物理包圍盒的 points 填充繪圖,注意要使用多邊形碰撞盒。
onLoad () {
this.draw();
}
draw () {
const points = this.getComponent(cc.PhysicsPolygonCollider).points;
const ctx = this.getComponent(cc.Graphics);
ctx.clear();
const len = points.length;
ctx.moveTo(points[len - 1].x, points[len - 1].y);
for (let i = 0; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.fill();
}
鼠標劃線
鼠標劃線很簡單,就是獲取起點和當前點然後繪圖線段。在 cut-main 腳本里註冊 touchmove 事件。
this.node.on(cc.Node.EventType.TOUCH_MOVE, (e) => {
this.draw.clear();
const startPoint = e.getStartLocation();
this.draw.moveTo(startPoint.x, startPoint.y);
this.draw.lineTo(e.getLocationX(), e.getLocationY());
this.draw.stroke();
}, this);
射線檢測
什麼是射線檢測?就是給定兩個點,返回這兩個點之間的物理碰撞盒。很顯然,我們鬆手的那一刻,劃線起點與終點就是我們要的那兩個點!我們把事件監聽方法提出來,放在 onLoad 裏面。
onLoad () {
this.registerEvent();
}
registerEvent () {
this.node.on(cc.Node.EventType.TOUCH_MOVE, (e) => {
this.draw.clear();
const startPoint = e.getStartLocation();
this.draw.moveTo(startPoint.x, startPoint.y);
this.draw.lineTo(e.getLocationX(), e.getLocationY());
this.draw.stroke();
}, this);
this.node.on(cc.Node.EventType.TOUCH_END, (e) => {
this.draw.clear();
const p1 = e.getStartLocation();
const p2 = e.getLocation();
// 核心邏輯
this.cut(p1, p2);
}, this);
}
接下來我們講 cut 方法。
核心邏輯
首先我們要調用射線檢測的方法,模式分爲四種:Any,Closest,All,AllClosest。本次教程針對單個碰撞體,使用 Closest 模式,每個模式更詳細的信息可以去查下官方文檔。因爲我們要獲取所有的交點,所以要正反來兩次射線檢測。
const result1 = cc.director.getPhysicsManager().rayCast(point1, point2, cc.RayCastType.Closest);
const result2 = cc.director.getPhysicsManager().rayCast(point2, point1, cc.RayCastType.Closest);
這樣我們就獲取到了結果,然後對是否爲單個碰撞體作檢測:
if (result1.length === 0 || result2.length === 0) {
cc.warn('無碰撞體');
return;
}
if (result1[0].collider !== result2[0].collider) {
cc.warn('不是單獨碰撞體');
return;
}
if (!(result1[0].collider instanceof cc.PhysicsPolygonCollider)) {
cc.warn('非多邊形物理碰撞盒無points屬性');
return;
}
檢測都通過後,我們就可以放心的取數據做邏輯處理了。接下來我們要將基於世界的座標轉化爲本地的座標數據。
const collider = result1[0].collider;
let localPoint1 = cc.Vec2.ZERO;
let localPoint2 = cc.Vec2.ZERO;
collider.body.getLocalPoint(result1[0].point, localPoint1);
collider.body.getLocalPoint(result2[0].point, localPoint2);
萬事俱備,我們現在知道了碰撞體與射線的兩個交點,那麼我們只要把碰撞體的 points 以兩個點爲界限分割爲兩部分即可!但是,首先我們要先找到交點在那條線上,先封裝個判斷方法:
/** 近似判斷點在線上 */
pointInLine (point, start, end) {
const dis = 1;
return cc.Intersection.pointLineDistance(point, start, end, true) < dis;
}
然後我們去查找那兩個點究竟在那兩條線上,用下標去表示。
const points = collider.points;
let index1 = undefined;
let index2 = undefined;
for (let i = 0; i < points.length; i++) {
let p1 = points[i];
let p2 = i === points.length - 1 ? points[0] : points[i + 1];
if (this.pointInLine(localPoint1, p1, p2)) {
index1 = i;
}
if (this.pointInLine(localPoint2, p1, p2)) {
index2 = i;
}
if (index1 !== undefined && index2 !== undefined) {
break;
}
}
cc.log(`點1下標${index1}`);
cc.log(`點2下標${index2}`);
OK!我們已經知道了那兩個交點在哪裏,我們下一步就是新建兩個數組,按照順序分好類,一邊一個!
const array1 = [];
const array2 = [];
// 碰到 index1 或 index2 的標誌量
let time = 0;
for (let i = 0; i < points.length; i++) {
let temp = points[i].clone();
if (time === 0) {
array1.push(temp);
} else {
array2.push(temp);
}
if ((i === index1 || i === index2) && time === 0) {
array1.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
array2.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
time = 1;
} else if ((i === index1 || i === index2) && time === 1) {
array2.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
array1.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
time = 0;
}
}
cc.log(array1,array2);
邏輯不難,拿 time 作爲碰到交點的標誌量,順利的分爲兩個數組。
拷貝節點
接下來就簡單了,將本體重置爲 array1 狀態,拷貝一個本體,設置 array2 狀態,再執行下自身腳本上的 draw 方法繪圖。
// 設置第一個碰撞體
collider.points = array1;
collider.apply();
collider.node.getComponent(Item).draw();
// 克隆一個本體作爲第二個
const cloneNode = cc.instantiate(collider.node);
this.gameLayer.addChild(cloneNode);
const comp = cloneNode.getComponent(cc.PhysicsPolygonCollider);
comp.points = array2;
comp.apply();
cloneNode.getComponent(Item).draw();
怎麼樣?如果感覺理解的差點意思,下方有源碼獲取方式哦!
結語
有意思吧!我們在第二部分教程講講多個物體的切割。
O(∩_∩)O~~
源碼在我的微信公衆號回覆關鍵詞【物理切割】即可獲得