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~~

源码在我的微信公众号回复关键词【物理切割】即可获得

微信公众号

发布了130 篇原创文章 · 获赞 147 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/kuokuo666/article/details/104357041