自学内容网 自学内容网

【cocos creator】多边形图片切割,读取PolygonCollider点集,加遮罩实现

在这里插入图片描述



const { ccclass, property } = cc._decorator;


interface Point {
    x: number;
    y: number;
}
@ccclass
export default class NewClass extends cc.Component {
    @property(cc.Graphics)
    graphics: cc.Graphics = null;
    @property(cc.Node)
    touchNode: cc.Node = null;
    @property(cc.Node)
    cutNode: cc.Node = null;
    @property(cc.Node)
    root: cc.Node = null;
    @property(cc.EditBox)
    maxCountEditBox: cc.EditBox = null;

    /**当前线开始点 */
    _startPos: cc.Vec2 = null
    /**当前划线数量 */
    _cutCount: number = 0
    /**画的线数据 */
    lineArr: Array<{ a: Point, b: Point }> = []


    /**划x条线后开始判定(切割) */
    totalCount = 2;
    /**切割后图像数组,每个数组是点组成的数组*/
    pointsArr: Array<Point[]> = []

    start() {
        this.reset()
        this.touchNode.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.touchNode.on(cc.Node.EventType.MOUSE_MOVE, this.onTouchMove, this);
        this.touchNode.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
        this.touchNode.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
        const manager = cc.director.getCollisionManager();
        manager.enabled = true;


        // manager.enabledDebugDraw = true;
        // manager.enabledDrawBoundingBox = true;
    }

    drawLine(point?) {
        this.graphics.clear();
        this.graphics.lineWidth = 10
        for (let i = 0; i < this.lineArr.length; i++) {
            const element = this.lineArr[i];
            const ws1 = this.cutNode.convertToWorldSpaceAR(cc.v2(element.a));
            const pos1 = this.graphics.node.convertToNodeSpaceAR(ws1);
            const ws2 = this.cutNode.convertToWorldSpaceAR(cc.v2(element.b));
            const pos2 = this.graphics.node.convertToNodeSpaceAR(ws2);
            this.graphics.moveTo(pos1.x, pos1.y);
            this.graphics.lineTo(pos2.x, pos2.y);
            this.graphics.stroke()
        }
        if (point) {
            const ws1 = this.cutNode.convertToWorldSpaceAR(cc.v2(point.a));
            const pos1 = this.graphics.node.convertToNodeSpaceAR(ws1);
            const ws2 = this.cutNode.convertToWorldSpaceAR(cc.v2(point.b));
            const pos2 = this.graphics.node.convertToNodeSpaceAR(ws2);
            this.graphics.moveTo(pos1.x, pos1.y);
            this.graphics.lineTo(pos2.x, pos2.y);
            this.graphics.stroke()
        }
    }

    onTouchStart(event) {
        if (this._cutCount >= this.totalCount) return
        const touchPos = event.getLocation();
        const pos = this.cutNode.convertToNodeSpaceAR(cc.v2(touchPos));
        this._startPos = pos;
        this.drawLine({ a: pos, b: pos })
    }

    onTouchMove(event) {
        if (!this._startPos) return
        const touchPos = event.getLocation();
        const pos = this.cutNode.convertToNodeSpaceAR(cc.v2(touchPos));
        this.drawLine({ a: this._startPos, b: pos })
    }

    onTouchEnd(event) {
        if (!this._startPos) return
        this._cutCount++;
        const touchPos = event.getLocation();
        const pos2 = this.cutNode.convertToNodeSpaceAR(cc.v2(touchPos));
        let line = { a: this._startPos, b: pos2 }
        this.lineArr.push(line)
        this._startPos = null
        this.drawLine()
        if (this._cutCount < this.totalCount) return
        let collider = this.cutNode.getComponent(cc.PolygonCollider)
        if (!collider) return
        let points = collider.points
        let arr: any = [points]
        let newPointArr = this.cut(arr, this.lineArr)
        this.lineArr = []

        let i = 0
        let colorArr = ["#C1B7B7", "#D42E2E", "#2E95D4", "#A9D42E", "#45D42E", "#7DA97D", "#7DA3A9", "#817DA9"]
        newPointArr.forEach(points => {
            let node = this.root.children[i] || cc.instantiate(this.cutNode)
            node.parent = this.root
            node.active = true
            node.y = 0;
            this.pointsArr[i] = points
            node.children[0].color = new cc.Color().fromHEX(colorArr[i % colorArr.length])
            let x = -300 + i * 150
            this.upDateBox()
            node.stopAllActions()
            cc.tween(node)
                .delay(1)
                .call(() => {
                    this.onClickFly()
                    this.upDateBox()
                })
                .start()
            i++
        });

        this.cutNode.active = false
    }

    /**
     * 更新切割后图像位置
     * @returns 
     */
    upDateBox() {
        this.graphics.clear();
        for (let i = 0; i < this.pointsArr.length; i++) {
            const node = this.root.children[i];
            if (!cc.isValid(node)) return
            let points = this.pointsArr[i]
            if (!points.length) return
            let mask: any = node.getComponent(cc.Mask);
            let stencil = mask._graphics;
            stencil.clear();
            stencil.moveTo(points[0]);
            points.forEach(point => {
                stencil.lineTo(point.x, point.y);
            });
            stencil.fill();
        }
    }

    /**
     * 初始化
     * @returns 
     */
    reset(): void {
        this.graphics.clear();
        if (this.maxCountEditBox) {
            let count = Number(this.maxCountEditBox.string)
            if (!isNaN(count)) {
                this.totalCount = Math.max(count, 1)
                this.maxCountEditBox.string = this.totalCount + ""
            }
            else {
                this.maxCountEditBox.string = this.totalCount + ""
            }
        }
        this.lineArr = []
        this.pointsArr = []
        this.root.children.forEach((value) => {
            value.active = false
            value.x = 0
            value.y = 0
        })
        this.cutNode.active = true

        this._cutCount = 0
        let collider = this.cutNode.getComponent(cc.PolygonCollider)
        if (!collider) return
        let points = []
        //  points = collider.points
        collider.points.forEach((value) => {
            points.push({ x: value.x, y: value.y })
        })
        let mask: any = this.cutNode.getComponent(cc.Mask);
        let stencil = mask._graphics;
        stencil.clear();
        stencil.moveTo(points[0]);
        for (let index = 0; index < points.length; index++) {
            const point = points[index];
            stencil.lineTo(point.x, point.y);
        }
        stencil.fill();
    }


    /**
     * 开始切割
     * @param cutNodePointsArr 要切割图形数组
     * @param lineArr 切割线数组
     * @returns 
     */
    cut(cutNodePointsArr: Array<Point[]> = [], lineArr: Array<{ a: Point, b: Point }> = []) {
        //切割后图形数组
        let newPointArr = []
        for (let k = 0; k < lineArr.length; k++) {
            //取一个线条
            let line = lineArr[k]
            let arr = []
            //第一次切割原本图形
            if (k == 0) {
                cutNodePointsArr.forEach((value) => {
                    let arr2 = []
                    value.forEach((value2) => { arr2.push({ x: value2.x, y: value2.y }) })
                    arr.push(arr2)
                })
            }//第二次开始切割上次切割后的图像
            else {
                newPointArr.forEach((value) => {
                    let arr2 = []
                    value.forEach((value2) => { arr2.push({ x: value2.x, y: value2.y }) })
                    arr.push(arr2)
                })
                newPointArr = []
            }
            for (let j = 0; j < arr.length; j++) {
                const points = arr[j];
                let newArr = this.cutOneBox(points, line)
                newPointArr = newPointArr.concat(newArr)
            }
        }
        return newPointArr;
    }
    //简易一刀切割两半
    cutOneBox(points, line) {
        let newPointArr = [];
        let crossPoints = []
        let newBoxs = []
        let boxIndex = 0
        for (let i = 0; i < points.length; i++) {
            let point = points[i];
            const nextPoint = points[i + 1] || points[0];
            let crossPoint = this.getIntersectionPoint(point, nextPoint, line.a, line.b)
            if (!crossPoint) {
                if (!newBoxs[boxIndex]) newBoxs[boxIndex] = []
                newBoxs[boxIndex].push(point)
            }
            else {
                if (!newBoxs[boxIndex]) newBoxs[boxIndex] = []
                newBoxs[boxIndex].push(point)
                newBoxs[boxIndex].push(crossPoint)
                crossPoints.push(crossPoint)
                boxIndex++
                if (!newBoxs[boxIndex]) newBoxs[boxIndex] = []
                newBoxs[boxIndex].push(crossPoint)
            }
        }
        if (crossPoints.length !== 2 || newBoxs.length < 2) {
            if (points.length) newPointArr.push(points)
            return newPointArr
        }
        let box1 = newBoxs[0]
        if (newBoxs[2]) box1 = box1.concat(newBoxs[2])
        let box2 = newBoxs[1]
        newPointArr.push(box1)
        newPointArr.push(box2)
        return newPointArr;
    }

    /**
     * 获取ab,cd两条线段的交点,没交点返回null
     * @param a 
     * @param b 
     * @param c 
     * @param d 
     * @returns 
     */
    getIntersectionPoint(a: Point, b: Point, c: Point, d: Point): Point | null {
        if (!a || !b || !c || !d) return null
        const ab = { x: b.x - a.x, y: b.y - a.y };
        const cd = { x: d.x - c.x, y: d.y - c.y };
        const ac = { x: c.x - a.x, y: c.y - a.y };

        const crossAbCd = this.crossProduct(ab, cd);
        const crossAcCd = this.crossProduct(ac, cd);
        const crossAcAb = this.crossProduct(ac, ab);

        if (crossAbCd === 0) {
            // Lines are parallel
            return null;
        }

        const t = crossAcCd / crossAbCd;
        const u = crossAcAb / crossAbCd;

        if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
            // Intersection point lies on both line segments
            return {
                x: a.x + t * ab.x,
                y: a.y + t * ab.y
            };
        }

        // Intersection point does not lie on both line segments
        return null;
    }

    crossProduct(v1: { x: number; y: number }, v2: { x: number; y: number }): number {
        return v1.x * v2.y - v1.y * v2.x;
    }
    // update (dt) {}

    onClickFly() {
        for (let i = 0; i < this.pointsArr.length; i++) {
            let center = this.getPolygonCenter(this.pointsArr[i]);
            let node = this.root.children[i]
            let dir = center.normalize();
            node.setPosition(cc.v2(dir.x * 100, dir.y * 100))
            // cc.tween(node).to(2, { x: dir.x * 100, y: dir.y * 100 }).start();
        }
    }

    onClickReset() {
        for (let i = 0; i < this.pointsArr.length; i++) {
            let node = this.root.children[i]
            this.root.children[i].stopAllActions()
            cc.tween(node).to(1, { x: 0, y: 0 }).call(() => {
                if (i === this.pointsArr.length - 1) {
                    this.reset()
                }
            }).start();
        }
    }

    private getPolygonCenter(polygon) {
        let x = 0, y = 0;
        for (let i = 0; i < polygon.length; i++) {
            x += polygon[i].x;
            y += polygon[i].y;
        }
        x = x / polygon.length;
        y = y / polygon.length;
        return cc.v2(x, y)
    }
}


原文地址:https://blog.csdn.net/K86338236/article/details/144295681

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!