利用plus控件制作烟花特效

Mr_MAO 3天前 174

import win.ui;
/*DSG{{*/
var winform = win.form(text="烟花特效 (By: Mr_MAO)";right=991;bottom=679;border="dialog frame";edge=1)
winform.add(
plus={cls="plus";left=0;top=0;right=992;bottom=680;bgcolor=0x000000;db=1;dl=1;dr=1;dt=1;notify=1;z=1}
)
/*}}*/

// 烟花粒子类
class FireworkParticle {
    ctor(x, y, vx, vy, color, size, life, generation) {
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;
        this.color = color;
        this.size = size;
        this.life = life;
        this.maxLife = life;
        this.alpha = 255;
        this.generation = generation || 1;  
        this.canExplode = (generation == 1) && (..math.random() < 0.35); 
        this.explodeTime = ..math.random(10, 30);
        this.hasExploded = false;
    }
    
    update = function() {
        this.x += this.vx;
        this.y += this.vy;
        this.vy += 0.12;  // 重力
        this.vx *= 0.98;  // 阻力
        this.vy *= 0.98;
        this.life--;
        this.alpha = (this.life / this.maxLife) * 255;
        this.size *= 0.97;
    }
    
    // 检查是否需要二次爆炸
    shouldExplode = function() {
        if(this.canExplode && !this.hasExploded) {
            var elapsed = this.maxLife - this.life;
            if(elapsed >= this.explodeTime) {
                this.hasExploded = true;
                return true;
            }
        }
        return false;
    }
    
    isDead = function() {
        return this.life <= 0 || this.alpha <= 0 || this.size < 0.5;
    }
}

// 上升火花类
class RisingFirework {
    ctor(x, startY, targetY) {
        this.x = x;
        this.y = startY;
        this.targetY = targetY;
        this.trail = {};
        this.exploded = false;
    }
    
    update = function() {
        // 添加尾迹
        ..table.push(this.trail, {
            x = this.x + ..math.random(-2, 2); 
            y = this.y; 
            alpha = 200; 
            size = 3
        });
        
        // 限制尾迹长度
        while(#this.trail > 12) {
            ..table.remove(this.trail, 1);
        }
        
        // 更新尾迹
        for(i = #this.trail; 1; -1) {
            var t = this.trail[i];
            t.alpha -= 20;
            t.size *= 0.85;
            if(t.alpha <= 0) {
                ..table.remove(this.trail, i);
            }
        }
        
        // 计算上升速度
        var distance = this.y - this.targetY;
        var speed = distance * 0.15;
        if(speed < 5) speed = 5;
        if(speed > 25) speed = 25;
        
        // 上升
        this.y -= speed;
        
        // 到达目标位置即爆炸
        if(this.y <= this.targetY) {
            this.y = this.targetY;
            this.exploded = true;
        }
    }
}

// 烟花管理器
var particles = {};
var risingFireworks = {};

// 预定义的烟花颜色
var fireworkColors = {
    0xFFFF0000;  // 红
    0xFFFF6600;  // 橙
    0xFFFFFF00;  // 黄
    0xFF00FF00;  // 绿
    0xFF00FFFF;  // 青
    0xFF0066FF;  // 蓝
    0xFFFF00FF;  // 紫
    0xFFFF69B4;  // 粉红
    0xFFFFD700;  // 金色
    0xFF00FF7F;  // 春绿
}

// 创建主爆炸粒子(第一代)
var createExplosion = function(x, y) {
    var particleCount = 100;
    var baseColor = fireworkColors[math.random(1, #fireworkColors)];
    
    for(i = 1; particleCount) {
        var angle = (i / particleCount) * math.pi * 2 + math.random() * 0.5;
        var speed = math.random() * 6 + 3;
        var vx = math.cos(angle) * speed;
        var vy = math.sin(angle) * speed;
        
        var color;
        if(math.random() > 0.3) {
            color = baseColor;
        } else {
            color = fireworkColors[math.random(1, #fireworkColors)];
        }
        
        var size = math.random() * 3 + 2;
        var life = math.random(50, 90);
        
        // 第一代粒子,可能会二次爆炸
        table.push(particles, FireworkParticle(x, y, vx, vy, color, size, life, 1));
    }
    
    // 添加闪光中心粒子
    for(i = 1; 30) {
        var angle = math.random() * math.pi * 2;
        var speed = math.random() * 2;
        var vx = math.cos(angle) * speed;
        var vy = math.sin(angle) * speed;
        table.push(particles, FireworkParticle(x, y, vx, vy, 0xFFFFFFFF, 4, 20, 3));
    }
}

// 创建二次爆炸粒子(第二代)
var createSecondaryExplosion = function(x, y, parentColor) {
    var particleCount = math.random(15, 25);  // 小型爆炸
    
    for(i = 1; particleCount) {
        var angle = (i / particleCount) * math.pi * 2 + math.random() * 0.8;
        var speed = math.random() * 3 + 1.5;  // 速度较小
        var vx = math.cos(angle) * speed;
        var vy = math.sin(angle) * speed;
        
        // 继承父粒子颜色或随机新颜色
        var color;
        if(math.random() > 0.4) {
            color = parentColor;
        } else {
            color = fireworkColors[math.random(1, #fireworkColors)];
        }
        
        var size = math.random() * 2 + 1;
        var life = math.random(25, 45);  // 寿命较短
        
        // 第二代粒子,不会再次爆炸
        table.push(particles, FireworkParticle(x, y, vx, vy, color, size, life, 2));
    }
    
    // 小型闪光
    for(i = 1; 8) {
        var angle = math.random() * math.pi * 2;
        var speed = math.random() * 1.5;
        var vx = math.cos(angle) * speed;
        var vy = math.sin(angle) * speed;
        table.push(particles, FireworkParticle(x, y, vx, vy, 0xFFFFFFFF, 2, 12, 3));
    }
}

// 鼠标点击事件
winform.plus.onMouseDown = function(wParam, lParam) {
    var x = lParam & 0xFFFF;
    var y = (lParam >> 16) & 0xFFFF;
    
    var rc = winform.plus.getClientRect();
    var startY = rc.bottom;
    table.push(risingFireworks, RisingFirework(x, startY, y));
}

// 自绘制函数
winform.plus.onDrawForegroundEnd = function(graphics,rc,rcContent){
    graphics.clear(0xFF000000);
    
    // 绘制上升的烟花
    for(i = 1; #risingFireworks) {
        var rf = risingFireworks[i];
        
        // 绘制尾迹
        for(j = 1; #rf.trail) {
            var t = rf.trail[j];
            var alpha = t.alpha;
            if(alpha > 255) alpha = 255;
            if(alpha < 0) alpha = 0;
            
            var trailColor = (alpha << 24) | 0xFFFF00;
            var trailBrush = gdip.solidBrush(trailColor);
            graphics.fillEllipse(trailBrush, t.x - t.size, t.y - t.size, t.size * 2, t.size * 2);
            trailBrush.delete();
        }
        
        // 绘制火花头部
        var headBrush = gdip.solidBrush(0xFFFFFFFF);
        graphics.fillEllipse(headBrush, rf.x - 4, rf.y - 4, 8, 8);
        headBrush.delete();
        
        var glowBrush = gdip.solidBrush(0x80FFFF00);
        graphics.fillEllipse(glowBrush, rf.x - 6, rf.y - 6, 12, 12);
        glowBrush.delete();
    }
    
    // 绘制爆炸粒子
    for(i = 1; #particles) {
        var p = particles[i];
        var alpha = p.alpha;
        if(alpha > 255) alpha = 255;
        if(alpha < 0) alpha = 0;
        
        var rgb = p.color & 0x00FFFFFF;
        var color = (alpha << 24) | rgb;
        
        var particleBrush = gdip.solidBrush(color);
        graphics.fillEllipse(particleBrush, p.x - p.size, p.y - p.size, p.size * 2, p.size * 2);
        particleBrush.delete();
        
        // 光晕效果
        if(p.size > 1.5) {
            var glowAlpha = alpha * 0.3;
            if(glowAlpha > 255) glowAlpha = 255;
            var glowColor = (glowAlpha << 24) | rgb;
            var glowBrush = gdip.solidBrush(glowColor);
            var glowSize = p.size * 1.5;
            graphics.fillEllipse(glowBrush, p.x - glowSize, p.y - glowSize, glowSize * 2, glowSize * 2);
            glowBrush.delete();
        }
    }
}

// 更新函数
var updateFireworks = function() {
    var needRedraw = false;
    
    // 更新上升的烟花
    for(i = #risingFireworks; 1; -1) {
        var rf = risingFireworks[i];
        rf.update();
        needRedraw = true;
        
        if(rf.exploded) {
            createExplosion(rf.x, rf.y);
            table.remove(risingFireworks, i);
        }
    }
    
    // 收集需要二次爆炸的粒子信息
    var secondaryExplosions = {};
    
    // 更新粒子
    for(i = #particles; 1; -1) {
        var p = particles[i];
        p.update();
        needRedraw = true;
        
        // 检查是否需要二次爆炸
        if(p.shouldExplode()) {
            table.push(secondaryExplosions, {
                x = p.x;
                y = p.y;
                color = p.color
            });
            // 二次爆炸后原粒子消失
            table.remove(particles, i);
        } elseif(p.isDead()) {
            table.remove(particles, i);
        }
    }
    
    // 执行二次爆炸
    for(i = 1; #secondaryExplosions) {
        var e = secondaryExplosions[i];
        createSecondaryExplosion(e.x, e.y, e.color);
    }
    
    // 刷新显示
    if(needRedraw || #particles > 0 || #risingFireworks > 0) {
        winform.plus.redraw();
    }
}

// 启动定时器
winform.setInterval(updateFireworks, 16);

winform.enableDpiScaling(false);
winform.show();
win.loopMessage();


最新回复 (8)
  • shzhbook 3天前
    0 2
    感谢分享,大佬还可以多发点实际应用相关的例子,比如网络应用相关的、验证码识别、下载、算法、界面、数据库分析等等相关的。
  • lcj21 3天前
    0 3
    好看,漂亮
  • 近我者赤 2天前
    0 4
    漂亮🤙🤙🤙
  • Mr_MAO 2天前
    0 5
    哪位大神给增加一下音效??
  • 近我者赤 1天前
    0 6
     // 鼠标点击事件
    winform.plus.onMouseDown = function(wParam, lParam) {
        var x = lParam & 0xFFFF;
        var y = (lParam >> 16) & 0xFFFF;
        
        var rc = winform.plus.getClientRect();
        var startY = rc.bottom;
        table.push(risingFireworks, RisingFirework(x, startY, y));
    
        import fsys.media;
        mediaFile = fsys.media("\烟花升空爆炸2.mp3");
    	if(mediaFile) {
    		mediaFile.play();
    	}
    }


    附合实际,但直观效果不理想

    上传的附件:
  • Mr_MAO 1天前
    0 7
    这个音效不错,但是没有实现异步播放,效果不好。
  • 近我者赤 1天前
    0 8
    Mr_MAO 这个音效不错,但是没有实现异步播放,效果不好。
    也就是有个动静吧了,复杂的完全弄不了啊😊
  • Mr_MAO 1天前
    0 9

    异步还是用多线程吧,我在你代码基础上修改了一下,添加了一个全局函数:

    playEffectSound = function(){
        //使用 fsys.media 在多线程中播放。
        ..thread.invoke(
            function(){
                import ..fsys.media;
                var media = ..fsys.media("beng.mp3");
                if(media){
                    media.play();
                    ..thread.delay(2000); 
                }
            }
        )
    };


    然后在需要播放声音的地方,调用 playEffectSound()  

    // 鼠标点击事件
    winform.plus.onMouseDown = function(wParam, lParam) {
        var x,y = win.getMessagePos(lParam);
        
        //播放声效
        playEffectSound();
        
        var rc = winform.plus.getClientRect();
        var startY = rc.bottom;
        table.push(risingFireworks, RisingFirework(x, startY, y));  
    }


    声效虽然有些不同步,但效果好多了🙂🙂🙂

    上传的附件:
返回