creator版本手动放烟花组件

此处为翻写的大佬的网页放烟花版本js网页放烟花
我只是把大佬的效果改成了creator组件版本,用cc.Graphics实现的。昨天那个拖尾效果困扰我一个多小时,睡一觉起来查了下资料大致明白了是什么功能,模拟他这种不断刷半透明黑色背景层混合勉强算是达到这种效果了。
现在就有一个瑕疵了:我想在编辑器中勾选isWidget这个属性的时候才展示widgetTarget,求教下怎么做?这块没有经验也没找到相关资料,贼难受。
代码上传上来了,就一个组件文件,挂到节点上就可以用,勾选isWidget默认是全屏可点击,不勾选就是只有节点范围内可点击放烟花。不需要额外操作,代码中自己加了个widget和graphics

--------------------------------2021-02-03 修改----------------------------------------
昨天晚上查了下rgba混合的内容,添加了颜色混合,修改了拖尾方式,避免会把背景刷黑。
新版本放上来了,使用方式跟老版本一致,挂上就能用:
FireComponent.zip (2.2 KB)

5赞

我不是效果的生产者,我只是效果的搬运工

貌似没效果啊

文档都没看完就去用。。。我试了没问题。

勾选了isWidget也没显示啊,看到执行了绘制,是不是cc.Graphics里面还要设置啥颜色

不用啊,我就直接把脚本拖到场景中,配置一下widgetTarget到canvas,运行就能点击放烟花了

很奇怪,我也是配置一下widgetTarget到canvas,运行就能点击放烟花了。然后关了游戏后,重新换了个widget,再也不能放烟花了

上传下demo看看啊,不知道哪里不对,配置了也没用,点了没啥反应,撞鬼了,我用的是2.4.3

谢谢,哈哈,好了,不用自己挂cc.Graphics节点,我自己挂了,导致出了两个这个节点

我改成一次能放好几个,爆卡

刚午睡了,我改下说明吧,啥其他操作都不要,挂上就能使用

const { ccclass, property } = cc._decorator;
@ccclass
export default class FireComponent extends cc.Component {
    @property
    isWidget: boolean = false;
    @property(cc.Node)
    _widgetTarget: cc.Node = null;
    @property(cc.Node)
    public get widgetTarget() {
        return this._widgetTarget;
    }
    public set widgetTarget(value) {
        this._widgetTarget = value;
        this.isWidget = !!this._widgetTarget;
        if (!CC_EDITOR) {
            this.initWidgit();
        }
    }

    private gfx: cc.Graphics = null;
    private particlesSet: Set<Data[]> = new Set();
    onLoad() {
        this.gfx = this.node.addComponent(cc.Graphics);

        // widgit
        this.initWidgit();
        // touch
        this.initTouch();
    }

    onDestroy() {

    }

    private initWidgit() {
        // 全屏适配
        if (!this.isWidget) return;
        let canvas = cc.find("Canvas");
        let widget = this.node.addComponent(cc.Widget);
        widget.target = this.widgetTarget || canvas;
        widget.isAlignTop = true;
        widget.isAlignLeft = true;
        widget.isAlignRight = true;
        widget.isAlignBottom = true;
        widget.left = 0;
        widget.right = 0;
        widget.top = 0;
        widget.bottom = 0;
        widget.alignMode = cc.Widget.AlignMode.ON_WINDOW_RESIZE;
    }

    private initTouch() {
        // 点击事件
        this.node.on(cc.Node.EventType.TOUCH_END, (event: cc.Event.EventTouch) => {
            let wpos = event.getLocation();
            let npos = this.node.convertToNodeSpaceAR(wpos);
            this.fire(npos.x, npos.y);
        })
    }

    private fire(x, y) {
        // 生成烟花
        // this.gfx.clear();
        this.createFireworks(x, y);
    }

    private tick() {
        this.gfx.fillColor = cc.color(0, 0, 0, 26);
        this.gfx.fillRect(-this.node.width / 2, -this.node.height / 2, this.node.width, this.node.height);
        this.drawFireworks();
    }

    private createFireworks(sx, sy) {
        if (this.particlesSet.size >= 2) {
            return;
        }
        let particles = [];
        this.particlesSet.add(particles);

        var hue = Math.floor(Math.random() * 51) + 150;
        var hueVariance = 30;
        var count = 100;
        for (var i = 0; i < count; i++) {
            var angle = Math.floor(Math.random() * 360);
            var p = {
                radians: angle * Math.PI / 180,
                x: sx,
                y: sy,
                speed: (Math.random() * 5) + .4,
                radius: 0,
                size: Math.floor(Math.random() * 3) + 1,
                hue: Math.floor(Math.random() * 2 * hueVariance) + (hue - hueVariance),
                brightness: Math.floor(Math.random() * 31) + 50,
                alpha: (Math.floor(Math.random() * 61) + 40) / 100
            };
            p.radius = p.speed;
            particles.push(p);
        }
    }

    private drawFireworks() {
        // this.gfx.clear();
        let isDraw = false;
        this.particlesSet.forEach((data: Data[]) => {
            let count = 0;
            for (var i = 0; i < data.length; i++) {
                var p = data[i];
                var vx = Math.cos(p.radians) * p.radius;
                var vy = Math.sin(p.radians) * p.radius + 0.4;
                p.x -= vx;
                p.y -= vy;
                p.radius *= 1 - p.speed / 100;
                p.alpha -= 0.02;
                if (p.alpha > 0.1) {
                    isDraw = true;
                    this.gfx.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
                    this.gfx.fillColor = this.hsla2rgba('hsla(' + p.hue + ', 100%, ' + p.brightness + '%)', p.alpha);
                    this.gfx.fill();
                } else {
                    count++;
                }
            }
            if (count >= data.length - 4) {
                this.particlesSet.delete(data);
                isDraw = true;
            }
        });
        if (!isDraw) {
            this.gfx.clear();
        }
    }

    private hsla2rgba(str, alpha): cc.Color {
        const colorArr = str.match(/\d+/g);
        let [h, s, l] = colorArr;
        h = parseFloat(h) / 360;
        s = parseFloat(s) / 100;
        l = parseFloat(l) / 100;
        if (s === 0) {
            l = Math.round(l * 255);
            return cc.color(l, l, l, Math.round(alpha * 255));
        }
        const getRGB = num => {
            let q = l >= 0.5 ? l + s - l * s : l * (1 + s); // 注意是数字1加上s,不是字母l
            let p = 2 * l - q;
            if (num < 0) {
                num += 1;
            }
            if (num > 1) {
                num -= 1;
            }
            switch (true) {
                case num > 2 / 3:
                    num = p;
                    break;
                case num >= 1 / 2:
                    num = p + (q - p) * 6 * (2 / 3 - num);
                    break;
                case num >= 1 / 6:
                    num = q;
                    break;
                default:
                    num = p + (q - p) * 6 * num;
                    break;
            }
            return Math.round(num * 255);
        };
        let r = getRGB(h + 1 / 3);
        let g = getRGB(h);
        let b = getRGB(h - 1 / 3);
        return cc.color(r, g, b, Math.round(alpha * 255));
    }

    update() {
        this.tick();
    }
}

export class Data {
    public radians: number = 0;
    public x: number = 0;
    public y: number = 0;
    public speed: number = 0;
    public radius: number = 0;
    public size: number = 0;
    public hue: number = 0;
    public brightness: number = 0;
    public alpha: number = 0;
}

会卡到爆的,我就是因为卡才自己手动加了那几个clear,因为我是模拟的大佬那个不停刷alpha=25的黑屏来制造的拖尾

widget规则好像是只能对标上级,对同级的好像是无效的,然后我是对widgettarget做的适配

我觉得这种如果要放多个烟花的话,可以考虑做个graphic池,每次点击挂载一个,然后拖尾刷黑屏那里就不刷全屏,计算一下只刷自己这一块,在播放完的时候clear清除数据再卸载或许可行,但是多了的话应该依然会卡顿

nice 1