原文链接:大佬实现
先说一下整体思路:
1:首先给一个待切割物体一个刚体组件(RigidBody)和多边形物理碰撞组件(PhysicsPolygonCollider)和一个切割脚本组件(主要负责绘制填充多边形轮廓),有人会问怎么要用物理组件,不用物理组件不行吗,加物理组件的原因是物理组件模拟了现实中的碰撞运动,摩擦等。
2:上面的准备之后就可以监听根节点的滑动事件根据滑动开始点和结束点进行切割操作,切割线必须经过刚体才可以实现切割,切割动作可以得到两个或者更多的与多边形碰撞体相交的点,利用这些点求出刚体节点坐标系下的点,然后在利用求出的交点与多边形的几条边进行比对,看一下交点在那条边上(利用交点到直线的距离近似计算下面会降到)。知道了交点在那条边上之后就可以将切割轮廓点集合放到两个数组里面了,一个数组存放的是本体切割之后的轮廓,一个数组存放的是切掉的轮廓数组,问题是切割本体的轮廓是可以知道的,直接将得的的数组替换PhysicsPolygonCollider的points属性重新apply,绘制一下轮廓就OK了,但是切割掉的那一部分怎么搞?
3:切割掉的一部分其实也是一个小刚体跟本体没什么区别唯一一个区别就PhysicsPolygonCollider的points不同,这样就好解决了,重新初始化一个本体,放到根节点下就OK了。这样就是整个切割的逻辑思路,下面开始上代码:
const {ccclass, property} = cc._decorator;@ccclass
export default class Item extends cc.Component {onLoad () {this.draw();}draw () {const points = Component(cc.PhysicsPolygonCollider).points;const ctx = Component(cc.Graphics);ctx.clear();const len = points.veTo(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();}}
这个是被切割本体上需要挂载的脚本,实际上就是绘制多边形轮廓并且填充的,这里只支持纯色的填充,如果想用图片的话还需要Mask来做,这个后面会讲到。另外就是切割逻辑了:
import Item from './cutItem'
const {ccclass, property} = cc._decorator;@ccclass
export default class CutMain extends cc.Component {@property(cc.Graphics)pen: cc.Graphics = null;private startPoint: cc.Vec2 = cc.v2(0,0);private endPoint: cc.Vec2 = cc.v2(0,0);private startP: cc.Vec2 = cc.v2(0,0);private endP: cc.Vec2 = cc.v2(0,0);physicManager: cc.PhysicsManager = null;onLoad () {this.physicManager = PhysicsManager();abled = true;this.physicManager.debugDrawFlags = 0;}start () {(cc.Node.EventType.TOUCH_MOVE,this.tapMove,this);(cc.Node.EventType.TOUCH_END,this.tapEnd,this);}tapMove(event: cc.Event.EventTouch) {const curLocation = Location());this.startP = StartLocation();this.startPoint = StartLocation());this.pen.clear();veTo(this.startPoint.x,this.startPoint.y);this.pen.lineTo(curLocation.x,curLocation.y);this.pen.stroke();}tapEnd(event: cc.Event.EventTouch) {this.pen.clear();dPoint = Location());dP = Location();this.cut(this.dP);}/*** @param {cc.Vec2} startPoint 世界坐标* @param {cc.Vec2} endPoint*/cut(startPoint: cc.Vec2,endPoint: cc.Vec2) {// 这里的射线检测Closest是检测最近的多边形 稍微慢些const result1 = this.physicManager.rayCast(startPoint,endPoint,cc.RayCastType.Closest);const result2 = this.physicManager.rayCast(endPoint,startPoint,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;// 射线与碰撞体相交的点1const resultPoint0 = result1[0].point;// 射线与碰撞体相交的点2const resultPoint1 = result2[0].point;console.log(`point0 is ${resultPoint0},point1 is ${resultPoint1}`);// 将射线与碰撞体相交的点转为刚体局部坐标const localPoint0 = cc.Vec2.ZERO;const localPoint1 = cc.Vec2.ZERO;LocalPoint(resultPoint0,localPoint0);LocalPoint(resultPoint1,localPoint1);const points = collider.points;let index1,index2;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(localPoint0,p1,p2)) {index1 = i;}if(this.pointInLine(localPoint1,p1,p2)) {index2 = i;}if(index1 !== undefined && index2 !== undefined) {break;}}console.log(`多边形边数从第三象限开始,点1落在了第${index1}条边上,点2落在了第${index2}条边上,`);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 ? localPoint0.clone() : localPoint1.clone());array2.push(i === index1 ? localPoint0.clone() : localPoint1.clone());time = 1;} else if ((i === index1 || i === index2) && time === 1) {array2.push(i === index1 ? localPoint0.clone() : localPoint1.clone());array1.push(i === index1 ? localPoint0.clone() : localPoint1.clone());time = 0;}}// 将本体的points替换成array1collider.points = array1;collider.apply();Component(Item).draw();// 克隆一个本体作为第二个const cloneNode = cc.de);de.addChild(cloneNode);const comp = Component(cc.PhysicsPolygonCollider);comp.points = array2;comp.apply();Component(Item).draw();} pointInLine(point: cc.Vec2,start: cc.Vec2,end: cc.Vec2) {const offsetDis = 1;return cc.Intersection.pointLineDistance(point,start,end,true) < offsetDis;}// update (dt) {}
}
本文发布于:2024-02-02 23:59:00,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170688953847322.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |