摘要
有趣的物理小游戏总能让小伙伴们爱不释手,而 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 = [];
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~~
源码在我的微信公众号回复关键词【物理切割】即可获得
微信公众号
