粒子跟随进度条怎么做?

如图这种。

1赞

大神呢?求指点指点

这个应该不难吧放几个定位的点在你的头像边,计算出边长,进度条进度你也知道,根据进度算在那条边,然后算位置

← ← (如果进度条裁剪是在除圆角区域内是水平垂直线在圆角区域是斜线的话,感觉粒子跟随反而比这个进度条裁剪要好做……

主要是圆角处理比较麻烦,如果想取到精确位置,比较简便但是会加重运算负担的方法:你可以对进度条图片添加多边形物理组件,然后自己用 raycast 检测当前进度对应的角度和图片的交点坐标。

初步构思有两种实现方式:
1.用shader解决吧。简单暴力,先进行边缘检测(减少负担就改用相当尺寸的图像边缘),通过实时改变uniform的值来达到进度条功能。
2.使用复合图层。被阻挡层采用填充渲染模式(Fill Mode),填充方式(Type)为扇形。通过修改Range达到百分比进度条的效果。你需要解决两个难题:1.扇形夹角与进度条长度的关系;2.计算出进度条末端坐标,实时改变消除效果粒子的坐标。
个人认为第二种可行性高,如果事件允许,我会尝试做出demo。

3赞

线和粒子分开的两个动画,根据进度去播线的动画,然后粒子的动画搞个挂点放线的尾部,不知道这样可以不

AI写一个5分钟:

import { _decorator, Component, Intersection2D, math, Node, Rect, rect, Sprite, tween, UITransform, v2 } from 'cc';
const { ccclass, property } = _decorator;

interface Point {
    x: number;
    y: number;
}
@ccclass('Test')
export class Test extends Component {
    @property(Node)
    test_sprite: Node = null;
    @property(Node)
    test_particle: Node = null;
    @property
    speed:number = 1;

    progress_angle = 0;
    start_ani:boolean = false;

    start() {
        this.startLight();
    }

    startLight(){
        this.start_ani = true;
    }

    updatePaticle(){
        //计算当前进度角度对应的射线与test_sprite矩形框边缘的交点
        let rect = new math.Rect();
        rect.x = -this.test_sprite.getComponent(UITransform).width * 0.5;
        rect.y = -this.test_sprite.getComponent(UITransform).height * 0.5;
        rect.width = this.test_sprite.getComponent(UITransform).width;
        rect.height = this.test_sprite.getComponent(UITransform).height;
        let radias = Math.sqrt(rect.width * rect.width + rect.height * rect.height) / 2;
        console.log('r = ' + radias);
        let angle = this.progress_angle * Math.PI / 180;
        let x = radias * Math.cos(angle);
        let y = radias * Math.sin(angle);
        //计算线段v2(0,0), v2(x,y)与rect矩形框的交点
        let start_p:Point = {x:0, y:0};
        let end_p:Point = {x:x, y:y};
        let result = this.intersection(start_p, end_p, rect);

        this.test_particle.setPosition(result[0].x, result[0].y, 0);
    }

    update(deltaTime: number) {
        if(!this.start_ani)return;
        if(this.progress_angle >= 360)return;
        this.progress_angle += this.speed * deltaTime;
        if(this.progress_angle >= 360){
            this.progress_angle = 360;
        }
        let sprite_per = this.progress_angle / 360;
        this.test_sprite.getComponent(Sprite).fillRange = sprite_per;
        this.updatePaticle();
    }

    intersection(lineStart: Point, lineEnd: Point, rect: Rect): Point[] {
        const intersections: Point[] = [];
    
        // 矩形的四条边
        const edges = [
            { start: { x: rect.x, y: rect.y }, end: { x: rect.x + rect.width, y: rect.y } }, // 上边
            { start: { x: rect.x + rect.width, y: rect.y }, end: { x: rect.x + rect.width, y: rect.y + rect.height } }, // 右边
            { start: { x: rect.x + rect.width, y: rect.y + rect.height }, end: { x: rect.x, y: rect.y + rect.height } }, // 下边
            { start: { x: rect.x, y: rect.y + rect.height }, end: { x: rect.x, y: rect.y } }  // 左边
        ];
    
        for (const edge of edges) {
            const intersectionPoint = this.getIntersection(lineStart, lineEnd, edge.start, edge.end);
            if (intersectionPoint) {
                intersections.push(intersectionPoint);
            }
        }
    
        return intersections;
    }
    
    getIntersection(p1: Point, p2: Point, q1: Point, q2: Point): Point | null {
        const denominator = (p2.y - p1.y) * (q2.x - q1.x) - (p2.x - p1.x) * (q2.y - q1.y);
    
        if (denominator === 0) {
            return null; // 线段平行或共线
        }
    
        const t = ((p1.x - q1.x) * (q2.y - q1.y) - (p1.y - q1.y) * (q2.x - q1.x)) / denominator;
        const u = -((p2.x - p1.x) * (p1.y - q1.y) - (p2.y - p1.y) * (p1.x - q1.x)) / denominator;
    
        if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
            return {
                x: p1.x + t * (p2.x - p1.x),
                y: p1.y + t * (p2.y - p1.y)
            };
        }
    
        return null; // 交点不在线段上
    }
}