CocosCreator之KUOKUO帶你做物理切割(第一部分)

摘要

有趣的物理小遊戲總能讓小夥伴們愛不釋手,而 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~~

源碼在我的微信公衆號回覆關鍵詞【物理切割】即可獲得

微信公衆號

發佈了126 篇原創文章 · 獲贊 138 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章