at main 3.1 kB view raw
1import type { KAPLAYCtx, GameObj } from "kaplay"; 2 3// Extend the KAPLAYCtx type to include our addConfetti method 4declare module "kaplay" { 5 interface KAPLAYCtx { 6 addConfetti?: (pos: { x: number; y: number }) => GameObj[]; 7 } 8} 9 10// Function to create a confetti effect at a position 11export function addConfetti(k: KAPLAYCtx, pos: { x: number; y: number }) { 12 // Number of confetti particles 13 const PARTICLE_COUNT = 50; 14 15 // Confetti colors 16 const COLORS = [ 17 [255, 0, 0], // Red 18 [0, 255, 0], // Green 19 [0, 0, 255], // Blue 20 [255, 255, 0], // Yellow 21 [255, 0, 255], // Magenta 22 [0, 255, 255], // Cyan 23 [255, 165, 0], // Orange 24 [128, 0, 128], // Purple 25 ] as const; 26 27 // Create particles 28 const particles: GameObj[] = []; 29 30 for (let i = 0; i < PARTICLE_COUNT; i++) { 31 // Random color 32 const color = COLORS[Math.floor(Math.random() * COLORS.length)]; 33 34 // Random size 35 const size = Math.random() * 8 + 2; // 2-10 pixels 36 37 // Random shape (circle or rect) 38 const isCircle = Math.random() > 0.5; 39 40 // Random velocity 41 const angle = Math.random() * Math.PI * 2; 42 const speed = Math.random() * 400 + 100; // 100-500 pixels per second 43 const vx = Math.cos(angle) * speed; 44 const vy = Math.sin(angle) * speed - 200; // Initial upward boost 45 46 // Random rotation speed 47 const rotSpeed = Math.random() * 10 - 5; // -5 to 5 radians per second 48 49 // Create particle 50 const particle = k.add([ 51 isCircle ? k.circle(size / 2) : k.rect(size, size / 2), 52 k.pos(pos.x, pos.y), 53 k.color(color[0], color[1], color[2]), 54 k.anchor("center"), 55 k.rotate(Math.random() * 360), // Random initial rotation 56 k.opacity(1), 57 k.z(100), // Above most game elements 58 { 59 // Custom properties for movement 60 vx, 61 vy, 62 rotSpeed, 63 gravity: 980, // Gravity effect 64 lifespan: Math.random() * 1 + 1, // 1-2 seconds 65 fadeStart: 0.7, // When to start fading (0.7 = 70% of lifespan) 66 }, 67 ]); 68 69 // Update function for the particle 70 particle.onUpdate(() => { 71 // Update position based on velocity 72 particle.pos.x += particle.vx * k.dt(); 73 particle.pos.y += particle.vy * k.dt(); 74 75 // Apply gravity 76 particle.vy += particle.gravity * k.dt(); 77 78 // Apply rotation 79 particle.angle += particle.rotSpeed * k.dt() * 60; 80 81 // Update lifespan 82 particle.lifespan -= k.dt(); 83 84 // Fade out 85 if (particle.lifespan < particle.fadeStart) { 86 particle.opacity = Math.max(0, particle.lifespan / particle.fadeStart); 87 } 88 89 // Destroy when lifespan is over 90 if (particle.lifespan <= 0) { 91 particle.destroy(); 92 } 93 }); 94 95 particles.push(particle); 96 } 97 98 return particles; 99} 100 101// Component to add confetti method to the game context 102export function confettiPlugin(k: KAPLAYCtx) { 103 return { 104 // Add the confetti function to the game context 105 addConfetti(pos: { x: number; y: number }) { 106 return addConfetti(k, pos); 107 }, 108 }; 109} 110 111export default confettiPlugin;